wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
* Q" A: }; J5 }; g借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- 6 J1 S1 i! O& K/ D4 L
- <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;">;; 定义新的游戏地图. d* m: g& R2 b8 Q4 R
- (def-map "/game/5x5.el" ; 对外开放的URL
7 k7 _+ z7 M# @4 c: k - 'tutorial-room-0) ; 默认的入口3 `! u- g$ |& Y3 c) U
& Z0 k6 g! O0 T* L2 u9 Z- ;;; 游戏大厅# \) f: P( p a3 ~
- (def-room living-room
5 P& |3 I5 l; T6 V# ~ V, D9 C( K - ;; 进入该房间后的提示语
$ j3 I/ j: U) s: ]7 }2 e - "1. 教程
7 e6 M% n3 ?0 m2 W7 o2 h& ~ - 2. 入门(3x3)
7 I! G6 I- j, P& Z& ]$ H; [$ D - 3. 初级(5x5)
1 O0 ~" z$ V* D, T, V7 t# `' h - 4. 中级(7x7)
1 P. W5 ^& M, w2 c1 @' C - 5. 高级(9x9)% V. ^& I9 w9 P* q8 F
- 0. 关于作者
" h) u' ?! ]3 Z( P' l - 请选择1-5开始新游戏:", ~# W- k. O# A& z* I% r) x, R1 ~0 E
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
Q- X, O& c7 d5 j# t - ("1" tutorial-room-0). H# \; j% Y- P6 }
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配% _0 Q$ f! O8 \
- ; 相应的返回也可以为函数,动态返回房间名, C5 j: g- Q. m% h* z
- (t living-room)) ; 如果条件为t,为永真
( {/ ]. H; Z1 I0 Y6 [8 \; w - 8 D( J p* g0 K! ]3 s
- ;;; 作者信息
( K( l, D, t8 i5 p& y% { - (def-room about-room
4 E* h% H2 Y" d" @ R. q0 x - "作者:redraiment
7 J6 k6 q( x, U: q0 R8 {* Y( e - 微博:http://weibo.com/redraiment. y& U9 D6 S, B$ Q% q, \
- 有任何建议,欢迎在微博或微信上联系redraiment。
' @3 {8 `+ n& g5 a( E: O) k - 请输入任意数字返回游戏大厅。"
5 R& q! {4 k% z( y; D2 d' n - (t living-room))& V; K' G. d; Q* z
: N8 Z7 U9 S+ a+ r: y" f& y/ Y) z- ;;; 教程
' x& }8 H4 n6 ?/ @8 Z$ Z/ s! b - (defvar *wechat-5x5-tutorial-rooms* 0* t; e, k7 P; p# B$ E+ F
- "The number of tutorial rooms")$ O7 p) m7 t9 F( A. }" X; A
+ P E/ Z. h3 t2 \9 ?- ;;; 简化教程的定义1 B" \1 M. [9 q" I0 C& n
- (defun string-last-line (content)( E7 g7 f! P9 H& G
- "多行内容组成的字符串中的最后一行") S9 k5 o0 V9 J) B$ S
- (with-temp-buffer* j1 @6 t& b7 N0 O1 D
- (insert content)
7 i' V6 F* h/ J, F$ } - (buffer-substring (line-beginning-position)
{+ }' R/ u' l) L4 p - (point-max))))5 E) u, K+ D6 h) E3 w/ x3 x2 _
- 1 V; P1 n: |/ `
- (defun def-tutorial-room (prompt)
$ |/ G4 B( C# {$ P - "根据提示语自动生成教程房间。
2 ], S: G O0 l, |( D$ |5 m/ a
: H# @/ S) |2 [ M2 Y) M" j" ?- 1. 提取最后一行作为问题;* h9 @; n( ^7 }$ u& ]$ ]
- 2. 分析问题,获取期望用户输入的内容;0 E- f! z9 |7 R8 \( P h* S% k6 E& {
- 3. 定义教程房间和重复提问房间。"9 [2 y3 ?( u8 A [4 y/ L4 y
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))( Z1 I' I0 Z1 w' b4 v$ b( D; e6 W3 R
- (repeat-room (concat room-name "-repeat"))
# o6 ?5 O( p+ r+ Z$ S, } - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))* J9 z4 C& ]. m- W5 |' x2 S; Q
- (question (string-last-line prompt))
n3 U" M3 y. P3 B7 T6 g6 K) O - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)" O# c' \* a9 Z
- (match-string 1 question)))0 A5 P- _8 e* k; y: g
- (doors (if except; d* m) ^$ V. ]) h( J
- `((,except ,(intern next-room))9 X. K0 k' A" ~" W. ` q8 V
- ("q" living-room)8 u& ^5 u/ T7 W# L# I3 c
- ("Q" living-room)5 L( i, g1 l! d# |
- (t ,(intern repeat-room)))
( t% f* l" e3 j4 z - '((t living-room)))))8 u3 x& S( I0 g, [% ^6 R8 r
- (def-room-raw (intern room-name) prompt doors)
( k6 [' ]4 ^) a% m5 f5 `: I - (def-room-raw (intern repeat-room) question doors)))
/ m( R" w* x h* A7 P - 5 v D4 L0 q8 L5 V9 y" V$ A% o9 S
- (defun def-tutorial (&rest prompts)9 _9 [' z! [3 o6 I e
- "批量生成教程房间。"0 n/ n: t% K( I& @; @* h
- (dolist (prompt prompts)
- ~- D [; j: L4 S, a2 O8 O6 d - (def-tutorial-room prompt)))4 _' F8 r; }/ y1 ?) I, l8 N
- 7 g! ]+ j7 k, d. ]- F e
- (def-tutorial( o7 r# l, o% m- j
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
* H' i W+ z+ L* h n0 F! n - 1. 教程
8 O2 v' I% O4 C3 e' x% F' f - 2. 入门(3x3)
! |/ y5 Z3 H. d9 q/ P5 A( J7 s - 3. 初级(5x5)
! g( i7 m& [% _2 z - 4. 中级(7x7)5 T# B3 P- }: `/ D
- 5. 高级(9x9)5 c, c: A0 h4 u3 w1 J5 e* s
- 0. 关于作者; F3 E4 D; z# _4 f/ A& G3 S
- 请选择1-5开始新游戏:" U* d6 N# _5 q0 S
- 您现在正在游戏大厅里。+ Y7 ?% N7 e. f: G; D
- 请输入“2”进入入门级房间"
8 W$ _( x0 j6 w - " ①②③
1 p: Z( u8 L; [ - 1 ■
9 B8 f+ ], @9 S: T4 `+ t4 m - 2■■■
b$ [/ W3 }9 f# U; y% O' P; g1 V: q - 3 ■
* a0 l1 G& E# ^8 t9 q! _. i - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!4 I9 n2 {* n3 t' b
- 请输入“22”来关闭第2行第2列的窗户。"
1 [+ c$ ~8 m3 p/ y - " ①②③: e; C* v( x8 @! R
- 1 0 z& X6 H4 U9 [+ M( H
- 2
, s6 s4 b+ O1 z: a5 ?+ _ - 3
" D7 j; K4 X0 c8 ` - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
# ^3 a: F& Z( |# U ]; B8 [ - 请输入“11”,试着开启左上角的窗户。"! m {- U$ y8 U- W0 N. x
- " ①②③7 m; S8 F1 p& o8 `
- 1■■
# H: n+ C \5 L - 2■
' R; N. _ N5 X& ^2 i" n; x# D; ? - 3
* `* C/ ]3 J3 G$ {1 E - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
8 x: w' z. S9 v - 请输入“13”开启右上角的窗户。"
/ s& b( B% _$ I( |3 ? - " ①②③) P) d+ v: h( u3 Y& r( V
- 1■ ■* K1 b4 E. I7 d5 L$ Q
- 2■ ■
' s$ P* @& @$ Q - 3
4 N7 e, p, o: f1 }- f - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。8 A7 X! P5 h9 Q; J0 l# k
- 请输入“31”开启左下角的窗户。"
: o! v, Y- }8 [ - " ①②③6 r* U$ g5 f5 |2 n
- 1■ ■( v4 [; v/ D; q2 W+ y" q! c
- 2 ■
# I6 l' m w0 g0 J - 3■■ % j5 a/ ?& q( z3 u, E. x
- 此时,总共有5扇窗户被开启了。
& P$ h3 }& Q& b$ w# e) T - 请输入“33”开启右下角的窗户。": j$ `, t6 V2 P
- " ①②③, d' ^, j9 y: s& t$ x) x
- 1■ ■
2 I Q/ O2 a% u& g& f - 2
& @% l+ ~/ N. V: D& E9 ^& A9 W - 3■ ■
/ |5 D" ^3 `' K) Y% Y - 现在,只有四个角落的窗户被打开。! Y6 F; a" s& Y' A: y4 z
- 请输入“22”完成最后一击!"& r6 q9 g: k+ E3 g
- " ①②③% r. s( i( \& d9 j
- 1■■■
- w, H$ b2 B0 M! X, W8 ? - 2■■■; X {7 h* C& _! N* @" W
- 3■■■. M2 ]& D$ B' w$ _4 |
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")0 o( B8 d, o# p* L# d$ V
- , K( i8 x" `6 A9 z. J
- ;;; 棋盘
U5 @( T, V) t - (defconst *wechat-5x5-white-chess* 12288* L. z6 {% d( R
- " ")0 E8 q8 F2 y$ V I
- ) ~/ C5 W9 ]% ? i) }, D
- (defconst *wechat-5x5-black-chess* 9632
% i$ n2 @9 W2 j$ ~; N' ]% e5 x - "■")
& F& H$ C9 d7 B8 \ - , {7 C% k$ N" S" C9 p
- (defmacro with-board (&rest body)" o- t% k) ?* ]5 F+ F2 z/ w
- `(with-temp-buffer2 r( b" I; ?0 Y7 Q! H+ y0 Q1 q( N2 Q
- (unwind-protect
: M: O0 v* v+ i0 J+ x4 J0 S - (progn$ _6 L; j3 x7 R& }0 m+ z
- (if (session "board")0 _& ]) @' b" ^# z6 B% D
- (insert (session "board")))
4 R* ^4 C0 ^1 |* `: _/ { - ,@body)1 S2 ], X* E5 i$ w8 {1 q
- (session "board" (buffer-string)))))
6 m$ b5 I7 [. d% ?
' }0 L) {6 P+ Y6 q' K8 |- (defun board-init (size)+ s* n/ S2 Y4 y9 k5 ~
- (session "size" size)
% J0 D+ l+ f' H D& Q* c4 u2 l - (session "step" 0); z' T [! N9 C
- (erase-buffer)4 W% m5 m2 \+ V8 v8 L# q" k
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))6 w; K/ h4 y% l: o6 N
- (dotimes (row size)% O/ }4 @) f/ }8 w. e1 t& B0 d& B
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))3 i& e/ ~2 T# B" [7 q" k( `! g6 z
1 E, H. F- {' m- c- (defun board-contains-p (y x)
# j) s$ Z E( w0 [9 z - (let ((size (session "size")))
( \# |- {$ G3 Z( K/ r5 G/ M - (and (<= 1 y) (<= y size)
: }" a( f- s: z/ q - (<= 1 x) (<= x size))))
1 o C9 C" C* k
4 v6 L% B. B- t- (defun board-toggle (y x)
5 G3 B1 k( t% I" N' ]2 T! e, _ - (when (board-contains-p y x)
% u t( I) x& f, Q$ A5 F - (goto-line (1+ y))# F. }9 k( Q. X- u7 N/ K0 M
- (beginning-of-line)
- u; X. X; H; x' S - (forward-char x)
" W; g, h0 y! i - (insert (if (= *wechat-5x5-white-chess* (following-char))
% }. O" n: @, d& P2 i - *wechat-5x5-black-chess*
/ D( S4 \/ [, { o9 f - *wechat-5x5-white-chess*))
- J5 v1 u' C* R+ F1 S1 f* Z - (delete-char 1)))
# T! i2 ~% s- Q+ O - , @2 e7 ^0 i% F. e- l# m
- (defun board-put (y x)( H% H, G# ^8 I$ j* H- O; T5 {
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
4 h# |: l* K, k) x7 m - (board-toggle (+ y (first dir))' c/ F: `& A& ?; O+ A
- (+ x (second dir)))))
5 A# f$ L7 n% K5 u" L: [
! h0 K; |( W- `- (defun game-over-p () [7 W. n; Y7 C3 q" j
- (beginning-of-buffer)5 W+ C# j! M( J( H& @) q( C! V
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
: K7 N, B$ s! P: k/ A/ w$ j) n" N- C - ( i3 h; J1 Y7 Y2 B$ q( J
- (defun board-show ()1 Y7 p R, _6 R& S$ E5 P
- (with-board! c2 Z g7 x# l
- (concat (buffer-string)
' K" t2 ]0 \8 a9 a - (if (game-over-p)
; S, C k+ U" i) @1 |: e7 Z - (format "共%d步,输入任意内容返回大厅" (session "step"))5 ?- ~" g \3 [0 W! T* g7 y6 l: {
- (format "第%d步" (1+ (session "step")))))))
' r1 w, R" e9 {7 y0 u( X% m7 \1 f! J+ D - & V& A" @/ W2 ?4 s8 K `/ A. ?
- (defun board-position-parse (cmd)
$ `/ L7 G6 K3 _! q8 M, G5 o - (if (= (length cmd) 2)
) ]+ g# y9 U" _' S4 H6 X9 @ - (list (string-to-int (substring cmd 0 1))
8 m9 c$ F0 m* k4 B4 O - (string-to-int (substring cmd 1 2)))7 O2 Q2 j X1 F& E4 q
- '(0 0)))
* C; m% n% G( I0 M% j - * T0 }3 o4 A+ S/ W3 u
- ;;; 游戏房间
' u4 P w& f+ s7 `; t - (defun game-room-init (cmd)7 E- [' ?" e' v
- (let* ((middle (string-to-int cmd))
% i0 ?& F- r p - (size (1- (* 2 middle))))) k# U! u+ q. G9 ? h* d
- (with-board
o ]6 w7 S' I; O - (board-init size)# V& [) M# ]2 t% U1 S
- (board-put middle middle))): `% q" d) ]& A3 y3 w q
- 'game-room)3 h* j: c% l% u: l
5 b B! W9 K; p( L- (def-room game-room
6 u! M4 G( _& h3 m( d F - #'board-show" ~. D: `. Q4 f3 |1 A/ s* z, k, \5 q& g `
- (t (lambda (cmd)
3 g% J+ z& t4 X& e) g5 R - (with-board
a3 C; `+ ?! g! b6 D+ p9 g- F - (if (game-over-p)
6 p+ {% K z9 P/ s" S- s - 'living-room
& u8 H l# z0 W: M5 F3 F- e - (destructuring-bind (y x) (board-position-parse cmd)7 E6 Y' k+ Y1 r* F( N7 Y
- (when (board-contains-p y x)
8 `! p" A1 V" T6 N) q - (board-toggle y x)
- ~7 Q% p) I S2 `7 b - (session "step" (1+ (session "step"))))
# N) l! Y" V; \) Y9 z - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|