wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。+ M* i/ @0 a# J( j
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- / [! t" g$ S2 } r5 w
- <p> </p><pre style="color: rgb(0, 0, 0); text-transform: none; line-height: normal; text-indent: 0px; letter-spacing: normal; font-style: normal; font-variant: normal; font-weight: normal; word-spacing: 0px; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;">;; 定义新的游戏地图3 g/ G5 W5 Y/ F
- (def-map "/game/5x5.el" ; 对外开放的URL
$ u6 E' a$ G L" L) ?, R - 'tutorial-room-0) ; 默认的入口
0 I; A; ~' ]% g* a/ u# T5 C; [1 v
5 N. O* }. h/ x+ M* l- ;;; 游戏大厅
5 m( j/ {8 X: @8 y/ W( d* b - (def-room living-room6 D. y& i3 j' N0 U% K1 K8 ^( b
- ;; 进入该房间后的提示语/ t+ t( S6 x+ Z& a7 w2 h4 I
- "1. 教程
& r& }7 B. d2 W2 r - 2. 入门(3x3)
0 ~& \1 ^0 o* @7 ?; ?3 h! n0 x - 3. 初级(5x5). }! j9 `% U) N0 x4 f! h
- 4. 中级(7x7), L9 Z% K# X g: }
- 5. 高级(9x9), l' W9 A+ y, z: S" Y0 e6 f
- 0. 关于作者+ x5 v2 U8 U! y$ }) L9 v
- 请选择1-5开始新游戏:"( o% `$ v ~* f! v; A* _$ V+ P
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
* _+ j$ }1 n% @9 f - ("1" tutorial-room-0)
! [1 N0 e; ~$ z; F - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配5 k6 n5 x6 B9 j+ p' N1 D9 p, o0 Z$ l
- ; 相应的返回也可以为函数,动态返回房间名
/ T0 k/ O$ E0 g( M7 Z: Y - (t living-room)) ; 如果条件为t,为永真, I5 `8 p& B- Y% B) p9 E
- " f' l+ f R& h
- ;;; 作者信息; M& `" }% P1 a, I$ I, A6 j0 j
- (def-room about-room8 a L( e4 I+ h& H$ G0 S- Z
- "作者:redraiment
- W5 r) y1 n% t2 r6 ? - 微博:http://weibo.com/redraiment
% s: M1 s7 c) J- |* r9 E' T4 E, Z - 有任何建议,欢迎在微博或微信上联系redraiment。5 r3 W* h% A- ?# z; ^
- 请输入任意数字返回游戏大厅。"" I* M" N; b: g
- (t living-room))8 E% K& I0 V$ F: w4 a
- ' P) [8 N& `: A2 o* p8 J
- ;;; 教程
( J7 B( q1 l+ Q - (defvar *wechat-5x5-tutorial-rooms* 0. _+ P' t7 E! Z
- "The number of tutorial rooms")7 J( B9 ^2 S5 Q4 D* }
- 0 Y- A; K0 s* _% ]3 o
- ;;; 简化教程的定义
4 R9 Q4 P8 j) n3 I1 `8 C - (defun string-last-line (content)2 X& g: P) e# t) h+ t c
- "多行内容组成的字符串中的最后一行"7 t& u9 h l! h4 I
- (with-temp-buffer" b& d. f7 S3 p8 q
- (insert content)5 o0 {' q# [0 z. L" ?. ^* V) c
- (buffer-substring (line-beginning-position)* [; `) n' L. @7 @
- (point-max))))
0 N2 h. l C* X/ v! Y6 M - : M2 z+ X# ?, ~; j
- (defun def-tutorial-room (prompt)
5 |/ ^0 }2 U3 H( l - "根据提示语自动生成教程房间。1 M8 U) C% V1 Z: v
- ( W. W% j; U# M- x" X5 L6 N2 e* _- A
- 1. 提取最后一行作为问题;
% l0 U7 K' F" E' P2 m9 p - 2. 分析问题,获取期望用户输入的内容;
5 t, D4 ?. o0 f2 J* Q3 E6 v - 3. 定义教程房间和重复提问房间。"* ~- p9 ~. L2 Z$ C( z# B
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))8 I+ H. O. b' U# U6 Y
- (repeat-room (concat room-name "-repeat"))4 g: U; e+ d/ E0 M+ [- W2 P
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
# S: Y: o! @4 V* | - (question (string-last-line prompt))7 \7 y+ e7 W E2 R! I
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)8 h! j5 t5 r- C
- (match-string 1 question)))
# I4 T9 r& T5 Q g& X1 M1 f - (doors (if except
- T! G* ~, e3 F - `((,except ,(intern next-room))
6 g# d5 Q7 E1 z5 ` - ("q" living-room)
, f$ C' ?2 ~5 s: N- S6 F2 [0 U1 l - ("Q" living-room)
0 k, t6 U% O2 n0 i7 i2 C - (t ,(intern repeat-room))): f0 w6 P2 z: o8 K p: e. C' q
- '((t living-room)))))
/ f7 y4 I a1 w4 L - (def-room-raw (intern room-name) prompt doors)' U* e7 I. V$ X: b- `7 \
- (def-room-raw (intern repeat-room) question doors)))1 B, R- G! z( _" C
- ; \4 b" K# q/ m0 e0 x- j6 ]
- (defun def-tutorial (&rest prompts)
; U/ E2 E/ Q; e0 u/ j N - "批量生成教程房间。"- F4 C9 c; u u9 b) H, `( R
- (dolist (prompt prompts)
; N5 X& {" C$ B - (def-tutorial-room prompt)))
) c. M6 _6 B6 X: [ - ( o4 @* c! R* j
- (def-tutorial$ h3 M }8 B1 L s9 ]7 U& z- C( |) e* Q
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
3 w5 b' R/ ]8 M ~+ x: _3 e' X - 1. 教程
; B, E2 c0 Z4 [0 F4 \, L O0 J - 2. 入门(3x3)# z- M! q _9 ~0 c, _; Y. R
- 3. 初级(5x5). U; J9 T! s- C9 v2 A% `8 G6 w
- 4. 中级(7x7)/ V8 a+ b0 Y$ ~' @9 j) ^( H" r+ W( A
- 5. 高级(9x9)* c% W4 f/ I) K& _- i1 o% A
- 0. 关于作者
( A& ?# S9 h+ ?- p- G6 A, n2 G - 请选择1-5开始新游戏:
, f6 L& r( D1 d# r7 d- I - 您现在正在游戏大厅里。; d+ p! Y6 i/ o0 J
- 请输入“2”进入入门级房间"2 C2 d* Q, s0 ^
- " ①②③+ f7 \/ }" m# ^7 i: V4 H# G6 s
- 1 ■
9 |! \. w7 m. z( B8 t$ f - 2■■■
+ @+ e/ M2 w3 X) S$ g& k8 y) L) E - 3 ■
& ~; L( p0 |4 Z: g - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!7 a9 D+ S9 B% H, w3 c6 \ U `
- 请输入“22”来关闭第2行第2列的窗户。". ?3 i n# \1 A
- " ①②③
C$ Y7 ]$ Y2 Q+ P8 I - 1 " h. s1 ]- X- x
- 2 . s7 ?% l( i! D) v2 _
- 3
# Z @, Q3 C1 E. l3 A6 u - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。& H% [9 A# O0 {8 M
- 请输入“11”,试着开启左上角的窗户。"! q; h: F6 d% }. t- W5 O2 X
- " ①②③
6 d- ~% `* M2 T* G# ?5 T - 1■■
0 u3 S& d/ c3 h3 j P$ d - 2■
. g) o- J# D. D: l7 n8 Z' {' d' F - 3
7 q5 Z) ^6 V9 w1 h2 H* m, s: c% i - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
, d* y% n2 K8 z0 c* ~! G - 请输入“13”开启右上角的窗户。"
; ?3 \9 O! ]2 Z3 q) M - " ①②③# \6 ?/ w" Q* R( @, J3 _
- 1■ ■8 t$ {$ i# d' o1 a
- 2■ ■& [+ i( `/ E ^
- 3
9 E3 z6 R2 @! T: Q - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
4 e. j9 |8 R) O: X% _ - 请输入“31”开启左下角的窗户。"' D6 f: Y) [! T9 }
- " ①②③
- e: `' Y& H6 V# t1 t5 h& Q - 1■ ■
5 A# V5 h( G# e9 ~6 P. D3 ]4 u - 2 ■$ Z w4 R+ z9 Y: @
- 3■■
9 M/ Q' R$ `6 |) |7 i5 p - 此时,总共有5扇窗户被开启了。
1 R7 L1 E8 b7 O2 d0 C& ` - 请输入“33”开启右下角的窗户。"
& V' i. Y% G2 P' u- z - " ①②③& y6 A# I) S1 j
- 1■ ■
/ v/ P! b5 a) y1 o3 a; Z - 2
3 \4 C! X: O6 T/ `) M M/ V( O! _ - 3■ ■
@' G7 @6 Q: t$ T* E1 A - 现在,只有四个角落的窗户被打开。+ i. E8 o M3 l
- 请输入“22”完成最后一击!"" [* N2 R" Q1 V# U/ t! s' m
- " ①②③$ f8 B; y2 t0 _' W" O
- 1■■■
7 X2 ^. y/ X# Z5 _% ^' b; O - 2■■■
! x# i5 S) J* [2 Z% E - 3■■■* ~ G! h! i3 V: b8 @
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")8 W( ~5 y, |% |2 n% X# H
- " K6 l' ?( p" f9 t
- ;;; 棋盘1 H6 E' y# n) c4 ?( v6 H
- (defconst *wechat-5x5-white-chess* 12288
' z! ?! O7 Z4 e6 _8 N9 O, H - " ")
( h S3 C: H; z" N: N8 ~0 h: z - 4 M) Y- L+ b- n' G" E! c8 T: o
- (defconst *wechat-5x5-black-chess* 96324 }( F' x1 `, Z0 B& A# Q
- "■"), I# t# u8 f: C6 X& s5 T; p) ^0 n
! Y5 p0 {' |3 e6 v- (defmacro with-board (&rest body)3 i. E- W2 a: ?- `" a( s% U: Q
- `(with-temp-buffer
5 u" o7 {) D, B' @- e - (unwind-protect( B" Q4 w" p& i% H: e- q, m
- (progn- u* }1 w$ }5 e. i! R/ J" j4 u
- (if (session "board")$ _7 V4 Y6 n- ? p: S a
- (insert (session "board")))
c4 S u0 ?3 A2 t - ,@body)
, l6 z% C3 K0 k, U7 {5 `3 c - (session "board" (buffer-string)))))" `$ y) |2 v5 @1 Z
- " ]7 ^' M+ d9 z- l( B' F0 S- T
- (defun board-init (size)
7 n0 \& G6 V8 D$ ~4 y - (session "size" size)
# ^1 _( P9 i+ ?( J2 z5 X - (session "step" 0)/ B3 |6 d' f( J4 x4 A. j7 F
- (erase-buffer)
, X3 d# ^- J5 p$ K: Z - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
$ c1 m5 Q" H q - (dotimes (row size)# ^# E1 u, ~: w* Q7 `1 `. s* n
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))6 k" ^* U$ ]8 P: }
- + q' n& M0 A" W/ e
- (defun board-contains-p (y x)
% e- }! ?6 u, R R2 \ - (let ((size (session "size")))
, k8 u6 P S/ `8 }( U/ C - (and (<= 1 y) (<= y size)
0 S- H) C1 N& P, J: {- S - (<= 1 x) (<= x size)))): D: I2 m2 O6 c& }
- 7 G3 H. H" C4 I2 R
- (defun board-toggle (y x)) N' e7 h+ K( p O% X2 D
- (when (board-contains-p y x)
6 H- }* J$ g5 G9 g$ z: Z - (goto-line (1+ y))
* `9 y- N) f3 Q( w - (beginning-of-line)7 \9 R% `! d3 R% }5 u3 N
- (forward-char x)9 P. f, x$ M3 E- L! i" A" g7 p
- (insert (if (= *wechat-5x5-white-chess* (following-char))0 c( @9 q- Y5 `; r8 {7 o2 ^
- *wechat-5x5-black-chess*! j4 @6 [' q8 j# \( }
- *wechat-5x5-white-chess*))
+ Y5 v2 p* K8 U! O - (delete-char 1)))0 w9 ]5 s9 p6 v: [. V
7 Z8 v- n$ w: [* ] I3 B6 u- (defun board-put (y x)
5 \& J6 U/ n) j" ?1 d- K - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
* D" [1 u& n+ ]& @ - (board-toggle (+ y (first dir))( p5 K0 U! h5 i- c0 [
- (+ x (second dir))))), {; M U& x3 W7 c c7 G5 u% B* h( P
- ! Q7 _0 Y5 x6 x, l) i5 x. ~
- (defun game-over-p ()
- z6 T, w" u4 k8 D! C0 ] - (beginning-of-buffer)+ V$ u! A) o" p2 Z* l% W
- (not (search-forward (string *wechat-5x5-white-chess*) nil t))): N; v1 A2 }7 \( P1 [6 u) c$ W, }) {
- & c6 U$ c" X* s! \/ Z h
- (defun board-show ()
4 d9 k3 ^' {" l8 Y$ ~- {+ ]+ h - (with-board
1 C1 K Q# F/ @) C- W - (concat (buffer-string)
/ {& n( X" b {6 j - (if (game-over-p)
% |6 I3 j2 y n9 q) H; @$ s' V - (format "共%d步,输入任意内容返回大厅" (session "step"))( Z! A# ]( [( r" p
- (format "第%d步" (1+ (session "step")))))))
% X/ ]# u- O8 h' u' d4 h) x - 4 F6 D; A, p0 B
- (defun board-position-parse (cmd)& E% R y! p+ h* T- e# d
- (if (= (length cmd) 2)' f- J, x' a/ b7 q) D9 M' n6 W( o
- (list (string-to-int (substring cmd 0 1))
, K: a L& E n - (string-to-int (substring cmd 1 2)))* g* b( q$ t( V0 \8 `0 o* c
- '(0 0)))
+ u8 Q2 O( a8 B& o4 f: h0 O" | - - S( c+ F! O2 }3 _9 d ?: [+ }
- ;;; 游戏房间6 ~6 {( v( u8 W) v9 x% O# `
- (defun game-room-init (cmd)5 c0 K% t$ j4 K' A0 N1 o2 U
- (let* ((middle (string-to-int cmd))2 S y) u( D+ f/ R1 q1 t
- (size (1- (* 2 middle))))
/ k: N* A( v* X; o5 n4 c) S - (with-board0 ?. Q% u; _# @- `2 D
- (board-init size)+ d4 d( p; W: P
- (board-put middle middle)))
/ {2 {! W$ n0 C6 \ - 'game-room)/ g0 U' B. Z$ u% Y4 b7 X
/ a D- B, |) t. _- (def-room game-room
+ F- ^; s0 V$ E: U+ h2 h- c - #'board-show
F8 D6 O1 z, e# I, j) B - (t (lambda (cmd)
2 w: n6 h+ }4 y6 ~0 L - (with-board
. {% n/ c3 y- t: U5 P+ x! p - (if (game-over-p)
, v9 H+ \. h; [! l3 a' o, [2 O - 'living-room
& T% f3 o X3 F" L5 d - (destructuring-bind (y x) (board-position-parse cmd)
% }/ Q" V o) [ K4 ~ - (when (board-contains-p y x)
+ X8 \* U ^+ \ - (board-toggle y x)' H2 m* u+ [" f: r8 O. a
- (session "step" (1+ (session "step"))))
9 G: d1 I Y B4 q - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|