wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
' ]4 n8 I% ?2 y借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- # ^1 W' Z3 j4 ]
- <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;">;; 定义新的游戏地图
+ _2 v: h# P+ f) ?# e! S- i4 F2 j - (def-map "/game/5x5.el" ; 对外开放的URL9 r! E+ y* y7 B9 x( q
- 'tutorial-room-0) ; 默认的入口
d5 d7 r; F' @& N( H6 W
! R) }) T1 Y& B- ;;; 游戏大厅
0 u( M* F; j4 `* }& K$ D - (def-room living-room
D* m( T) B* f' @- i0 g, v - ;; 进入该房间后的提示语+ b4 F# c7 A: d- X2 ]
- "1. 教程
% J' w6 x( F/ `4 Q - 2. 入门(3x3)
% z3 R4 |% T- h; T1 I7 R4 [ - 3. 初级(5x5)
2 J, v* m* }4 {- ]9 v" ^# p - 4. 中级(7x7)
+ I, w; K. I. K" I% h - 5. 高级(9x9)
. Y( q6 |5 q; M" A - 0. 关于作者
5 J. e; x4 b8 \4 K* ~5 J3 ~ - 请选择1-5开始新游戏:"+ R+ Y7 N4 Q- w( b) @
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名2 a- l9 N' \+ }$ }$ d
- ("1" tutorial-room-0)
. Y0 ?/ @) k) _+ S0 H - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
7 u# ]( w5 y) n" D0 f2 f - ; 相应的返回也可以为函数,动态返回房间名& H! T2 b5 }& R, r' R P
- (t living-room)) ; 如果条件为t,为永真0 N% m8 B ^( {: b; b
- 1 m( D* I7 {4 D& ^, X
- ;;; 作者信息* C; G$ s, k3 f* _. z& m, G# g
- (def-room about-room
) n+ |$ V* M5 e V: k - "作者:redraiment" h0 A: y3 Y7 M/ C7 h* F; k: `
- 微博:http://weibo.com/redraiment0 Z) s4 ~6 e0 Z* P9 ~! C
- 有任何建议,欢迎在微博或微信上联系redraiment。
7 w7 N# n0 U6 \0 |) M - 请输入任意数字返回游戏大厅。"1 P( q, I- C% _; {! v
- (t living-room))! `8 {/ \# r) g3 k" O& s; L
5 W( Z: q' ~. I6 T ~5 z- o- ;;; 教程& u; c- R* i0 }. c4 O9 x
- (defvar *wechat-5x5-tutorial-rooms* 0- Q3 N4 z5 u" N
- "The number of tutorial rooms")2 r7 D% h1 X; ~0 f* m, Q5 t
3 s/ z+ e# h' Y1 H2 d+ c& v- ;;; 简化教程的定义0 y$ ^- Q+ c$ X [
- (defun string-last-line (content)
0 q1 t* Z8 [' r" M; \, X7 L - "多行内容组成的字符串中的最后一行"9 K! \3 _# m/ F$ M( c9 e1 U
- (with-temp-buffer% ^3 ~- R7 m. ~7 m% I }
- (insert content)
4 a2 {8 m( y" Z p F4 b; ~( r; _ - (buffer-substring (line-beginning-position)
$ P, L: L3 a" ^$ l - (point-max)))); j' l/ ~1 N: j
$ f( n8 F. K6 @, I) f- (defun def-tutorial-room (prompt)
( c' j( P: l% N. {9 p - "根据提示语自动生成教程房间。
/ s2 D( t/ m7 Y7 K+ j: f! W' b' m& k
, `- J$ X! O6 f. a- 1. 提取最后一行作为问题;
( J, o4 E4 ~8 X6 @ - 2. 分析问题,获取期望用户输入的内容;
: F" _) q! a7 v. K" u - 3. 定义教程房间和重复提问房间。"
$ `% F' N" f; @5 q0 o: m - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
7 l4 a! v0 U! T u1 \ - (repeat-room (concat room-name "-repeat"))" Y ~3 L8 a. s
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
- \' C* ?, E1 y$ f M - (question (string-last-line prompt))" {9 t! ]9 Z9 F) ~, k
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question); Y( n/ M0 l5 m; K! y+ E1 @) d
- (match-string 1 question)))
) r7 t1 T1 ^+ o9 Z/ o M" y - (doors (if except
( e4 F8 C/ b. ` - `((,except ,(intern next-room)): N7 G+ C4 N6 u; K, ^. B1 V
- ("q" living-room)) B5 C0 u: A3 A# ^: L. ?, k
- ("Q" living-room)
$ |6 s2 {& p& s, _ - (t ,(intern repeat-room)))" }4 J& L) {; @) j+ |0 r1 f
- '((t living-room)))))1 H& g1 }( {3 l1 s$ @; T2 y
- (def-room-raw (intern room-name) prompt doors)
5 _' ^( }4 R6 L% {$ ^ - (def-room-raw (intern repeat-room) question doors)))4 M4 A4 b# ~/ a! R
- " J$ g, ?" l6 {$ S
- (defun def-tutorial (&rest prompts)
- s5 x5 z1 m+ Y1 p# `, C) P - "批量生成教程房间。"
/ j9 Y$ e M2 u - (dolist (prompt prompts)3 T( N3 d" a: K# B H* I
- (def-tutorial-room prompt)))6 Z- s$ w+ \3 i
r7 j, _$ H7 O; p/ K- (def-tutorial8 l6 V5 W" }) j% `" I
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
4 t: l% G% H2 A - 1. 教程
: k! i' v* Y+ P - 2. 入门(3x3)
( d. e2 s; x: ?9 d - 3. 初级(5x5): f% L# U& p0 q/ q
- 4. 中级(7x7)* ~ z! ~' c' e& H# P3 U: L
- 5. 高级(9x9)
* F f ^$ B2 }6 y - 0. 关于作者1 M/ n2 _5 g! c% A2 P. J* s
- 请选择1-5开始新游戏:* ~( R8 g- b: x: W0 n8 u- e2 R
- 您现在正在游戏大厅里。
& y, s4 p# f/ P6 B - 请输入“2”进入入门级房间"
! ^. J, [/ G7 h, q5 ~# n% r# @ - " ①②③
4 j9 z9 L. H# Z" f - 1 ■ ) f7 C: y% w. I c& {
- 2■■■
0 J: O, w3 M; N( _+ Z1 k, \ - 3 ■ 3 I+ X% C3 V0 F: C5 l, a
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
' w8 ^; W$ O' }) r - 请输入“22”来关闭第2行第2列的窗户。": b- R2 _+ @- q
- " ①②③1 U- r" _! ^" F
- 1 % @+ E& Z; V. G0 G( }5 f, v* d
- 2
" j0 V4 @& a7 F! x" { - 3
' W" v3 w$ p0 B - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。/ U$ p. f y9 C7 f0 ?
- 请输入“11”,试着开启左上角的窗户。"
% V* ^) j& t3 ?; Z( t - " ①②③' Y) a9 R- N9 k! c3 V
- 1■■ & l1 P, f% z o: V' {( @ H
- 2■
/ y" _9 N2 r1 n- E1 z0 i - 3
N( T" A5 i& v - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
' [# J" z/ ~! [( ~8 n# Q - 请输入“13”开启右上角的窗户。"
. c( w! d/ s) k/ \+ F! Z - " ①②③# L! n6 j5 z1 Q, _
- 1■ ■
9 t7 o+ N- o5 G% v$ |; C: Z6 w - 2■ ■
4 t+ g! z3 J( j9 g4 ~- S - 3 : Q X, q/ U) W$ y7 w' q7 Q
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
/ F: z" ~, N. F7 B: S5 X, H# g; H - 请输入“31”开启左下角的窗户。"
& P& j' `8 w2 a1 u' C# | - " ①②③8 ~# J6 g7 E# j: Z4 G
- 1■ ■
- d* G/ Y, g( N& ^* x5 l9 s- y - 2 ■/ e0 b2 _ A" I1 ~3 t4 R8 ^9 H+ T
- 3■■
! N T9 y, F6 |3 h3 k! z/ o - 此时,总共有5扇窗户被开启了。) v2 c8 }6 ~$ ?9 ^8 \6 y. b
- 请输入“33”开启右下角的窗户。"" x. O$ A. ]# P3 A
- " ①②③( O; t/ @$ J- p7 L m+ a
- 1■ ■3 W( u$ O0 ?+ j
- 2 - `$ J0 z( Y: m# v) ^) |
- 3■ ■
: q' z. _% V2 N. l9 h @ - 现在,只有四个角落的窗户被打开。
2 Y. h* I. b$ U! ~ - 请输入“22”完成最后一击!"
5 `2 k! i9 y( F$ `3 G/ ] - " ①②③
1 @; v+ S, Q7 c# O, a2 H1 w - 1■■■
: Q$ f' Y8 ]; h2 }9 @6 E! ^. X) T - 2■■■
6 b. r1 E. V# @ - 3■■■+ L0 p! u( q. q
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")# {7 J# }' c# c4 i' i
" G* M* ?0 u+ y( q# Z- ;;; 棋盘
) D! {& s) R! N5 q9 E7 l - (defconst *wechat-5x5-white-chess* 12288 T( M+ h# p; _! N6 ~/ T, O
- " ")
. d2 ?' A* S2 P2 s' v" P - 7 @: m4 r8 F9 m7 |5 s- I# s
- (defconst *wechat-5x5-black-chess* 9632* K- _& S" U9 D8 r
- "■")- v% G& ?1 ]$ D- u- i
- ) Q$ p* Y3 O5 k. L
- (defmacro with-board (&rest body) c! v# x- ~, o
- `(with-temp-buffer) \7 Z. {+ }- b" u R! i5 M5 S
- (unwind-protect
L' G H5 i) |4 u0 W3 i7 R - (progn
2 ^! ~" k% ]) q0 h - (if (session "board")- O* a1 h* t: b
- (insert (session "board")))
/ e V! I9 k: Q B6 u - ,@body)
8 l: [) Q8 G$ _8 D: G" p( H0 | - (session "board" (buffer-string))))). B/ g- h) z0 G1 E {- M
. m4 x3 ~% U- ]- U7 h2 Y% j+ \- (defun board-init (size)" _3 \& H$ a2 s3 J& r/ @; A
- (session "size" size)8 f% i$ E; n: b' |( |/ E. q
- (session "step" 0); e0 ]. f N: M% k
- (erase-buffer): a) S, R! h* m" ?$ R% A5 e
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
* V/ S# A/ t! b. F# J9 ]4 r7 A - (dotimes (row size)
: w6 }9 ]8 e- c! H% {( q' o - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))1 j- ]8 b3 A. {, c$ i
4 a Y3 b6 {* o+ \6 H; ^6 f% g- U* l- (defun board-contains-p (y x): Z& Y3 X* P. {' m% ]$ i
- (let ((size (session "size")))
9 [) _/ g7 I4 R% V; f - (and (<= 1 y) (<= y size)
8 l- Y& J8 A1 D" y0 ^5 }! X - (<= 1 x) (<= x size))))) V7 E6 {9 R% m+ Y2 y# ^$ r4 W2 Q
- ! p( ?1 Q6 m& b3 H+ t* j
- (defun board-toggle (y x)& Y8 c. k* \# Z. S
- (when (board-contains-p y x)( Q4 T/ J' |9 s; U3 [
- (goto-line (1+ y))
+ z6 Q% ?) w4 v( P - (beginning-of-line)7 x& @! g( b8 _# C
- (forward-char x)
' Z4 c: a+ Y3 B! z) T2 x - (insert (if (= *wechat-5x5-white-chess* (following-char))
K; W4 u& s! Y - *wechat-5x5-black-chess*
( c. ` p9 K3 v, N F - *wechat-5x5-white-chess*))! L3 u4 j+ P+ l* \7 S
- (delete-char 1)))% k0 f" X& j& J
! |1 P6 f6 G6 m- (defun board-put (y x)
- y: A& r5 B; b7 |+ ]- T - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))) w7 u! m( l7 E8 O) ]$ S, @
- (board-toggle (+ y (first dir))
) `* }' Q# w u! f, { - (+ x (second dir)))))
H) q2 J2 Y p8 c o! F- l
5 r% L7 Y& a- D- (defun game-over-p ()
5 W2 x; v2 j7 w6 e+ F& ~ - (beginning-of-buffer)
$ G* F' b. A2 T$ c* Q: ^( B: v9 p# L - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))& ]1 y+ {, F6 f4 I4 O6 ~
9 c" y" B: S. k" x" `$ x3 i$ y- (defun board-show ()3 a- Q7 _. R1 r3 N! t
- (with-board
7 d6 U3 A9 s+ s - (concat (buffer-string)
8 V% R5 u( d7 F( a0 R' b- n - (if (game-over-p)% F' \7 u i7 [/ m; p7 P
- (format "共%d步,输入任意内容返回大厅" (session "step"))
$ G4 ` u. N1 b' v& |7 P! P) n$ d - (format "第%d步" (1+ (session "step")))))))- T$ ?) T. g( S( Y& W7 }( _6 A* N
" X1 T" @+ ]7 @% Q' [- (defun board-position-parse (cmd) Y R+ [( Y" i) r: L
- (if (= (length cmd) 2)
n$ f _ S; M( ^+ a - (list (string-to-int (substring cmd 0 1)); ~% o5 ]! n7 }
- (string-to-int (substring cmd 1 2)))
; F) x! L) d4 C) O% z9 k' c* _0 A - '(0 0)))" O; Y3 K9 |# k
& a3 r/ @! ~; [' A5 e- ;;; 游戏房间8 p0 g- c1 \6 U5 i/ A
- (defun game-room-init (cmd)( I( B0 l( ~! s
- (let* ((middle (string-to-int cmd))
- `+ B7 s: M1 I* {+ H - (size (1- (* 2 middle))))
% i$ P/ M. |, O1 r - (with-board; @3 a- h; N+ S* B& m
- (board-init size)
6 V/ A B: C+ ^, ]3 y; x - (board-put middle middle)))
( y0 U% @& Q* ]9 U" C1 u - 'game-room)1 \/ H1 p, I: [2 ^0 r
- ; _% j* E( \2 l9 p- E
- (def-room game-room
. G) C; A! M' n9 q9 g! g3 H - #'board-show5 G; o3 v' h& x3 _% f
- (t (lambda (cmd)
( a" A" D0 s7 f5 Q! f2 h1 | - (with-board- {2 [% r ]) ~+ r0 y
- (if (game-over-p)
8 x g! g0 X* c; w3 }" d - 'living-room7 ]9 J# j, x( l7 V( E$ K
- (destructuring-bind (y x) (board-position-parse cmd), w( |7 k6 i: r$ `+ v0 v
- (when (board-contains-p y x)
3 H3 Q9 S/ f- U - (board-toggle y x)
/ X% v% m. N" L* \4 c; D - (session "step" (1+ (session "step"))))$ ?9 P# [$ D4 b* j3 V
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|