wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
5 m7 ~) R. b# X# c w借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
# L. }7 U0 r f, V- ?" Q$ q6 M- <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;">;; 定义新的游戏地图7 c* _( i% p# \
- (def-map "/game/5x5.el" ; 对外开放的URL
7 n' q! C% }3 D6 N, a9 A - 'tutorial-room-0) ; 默认的入口# t/ y( {) o, a& h( j. I. G
4 D! O, E0 `8 a$ q; y- ;;; 游戏大厅
3 S: Q, Z! c- ^' U9 g5 Z( m! \ - (def-room living-room
3 z; l* M+ `, d: w* O( L7 H& A- [ - ;; 进入该房间后的提示语: l6 v3 L% I( Q$ W$ }
- "1. 教程+ O5 v: e1 m) J
- 2. 入门(3x3)
1 b a0 W* o6 F% q" U/ p/ t) Y - 3. 初级(5x5)
7 L' A/ b) e7 {6 h k" d2 I - 4. 中级(7x7)
7 {7 U9 D& m- x- }" M - 5. 高级(9x9)
4 _0 o. Y% S: ^ Y - 0. 关于作者 I. l! x" Z6 N/ b" R4 G. W
- 请选择1-5开始新游戏:" t" ?5 P! w# w
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名9 Q- k# k. _! Q8 M4 ?
- ("1" tutorial-room-0)
U. V: F; q0 \5 z* J - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配2 L9 X% @2 }0 U$ U! E
- ; 相应的返回也可以为函数,动态返回房间名4 X0 y7 @7 q( n7 r, v
- (t living-room)) ; 如果条件为t,为永真. w# x) U' m; f: n
# ^5 ~" O9 M. |- ;;; 作者信息7 m, m. ?# N$ Z
- (def-room about-room
/ \% `5 k6 H9 \3 H- s9 I7 ] - "作者:redraiment
/ G+ r' I5 i& ] B) g* p' h - 微博:http://weibo.com/redraiment
5 [" k7 L; J6 Y& L9 d+ u% h - 有任何建议,欢迎在微博或微信上联系redraiment。3 J* \# q% j4 {) n4 u. E
- 请输入任意数字返回游戏大厅。"4 Q4 E; I' `$ K$ ~/ ~
- (t living-room))
/ h/ A4 g/ T- I% U( V/ }: {
2 x7 ?5 B w% o, k `( v" p- ;;; 教程" s+ I/ v/ x, F
- (defvar *wechat-5x5-tutorial-rooms* 0
( {0 k/ O; v4 A - "The number of tutorial rooms")# _- J6 @0 z) y1 l
- $ ]7 ^" ~/ a& O! c4 |* H/ I2 |
- ;;; 简化教程的定义9 f2 Z' C7 i* e- C7 C H
- (defun string-last-line (content)
' G0 J1 q `) v7 R. Y) r9 W+ w& f - "多行内容组成的字符串中的最后一行"8 N" K+ p% ?$ Y7 J) x. e
- (with-temp-buffer
4 F7 ~) G# n7 j2 M/ c - (insert content)
D! F0 T8 X, B/ o - (buffer-substring (line-beginning-position)" T/ a* y# ^! ]5 {: Q& ^
- (point-max))))
# r5 r, C# G& u( q; E - ' r5 J; ` B& N' j7 [6 [
- (defun def-tutorial-room (prompt)
" S+ Q( a0 j* D+ x0 v - "根据提示语自动生成教程房间。
3 `" r! G3 c" o- D7 l2 O- [7 t& R7 N' t
, k0 [; H3 w% h- N- 1. 提取最后一行作为问题;1 {# T6 ~4 z, S! A# L2 T; m8 s
- 2. 分析问题,获取期望用户输入的内容; a+ I8 A+ @( y- H2 z7 F
- 3. 定义教程房间和重复提问房间。"; W0 y5 o( x2 M1 N5 C
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
/ ?- p/ @) o6 d5 n6 Z8 p3 t% _4 N - (repeat-room (concat room-name "-repeat")); e4 m8 W, p- U [1 ]# `0 P' O4 M7 W
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))" x& R2 }% Z3 j2 Z; O
- (question (string-last-line prompt)). C. o G! a' a
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)" [1 s5 u. `* K; f* G
- (match-string 1 question))). Y) V. M4 K+ r6 |
- (doors (if except
# H1 H6 \: F B; a& O$ m - `((,except ,(intern next-room)). r+ j! t5 W6 m0 g- L) B9 Q- Z
- ("q" living-room)" B$ L. o( g0 M- L+ s, e0 N" i* D) U& D
- ("Q" living-room)
4 i7 m5 D, w5 E x! Z5 S3 y - (t ,(intern repeat-room))); D; {1 ^/ q6 j/ B: D" C
- '((t living-room)))))6 a$ x. J' |; x: x
- (def-room-raw (intern room-name) prompt doors)
) m: H8 S% A7 t+ I0 i - (def-room-raw (intern repeat-room) question doors)))
. ~4 X% r( ^4 K; m. E3 q" }3 D8 ?+ |
* _1 h( r+ r% P- (defun def-tutorial (&rest prompts)# |, f4 A2 x1 h* t5 \% K
- "批量生成教程房间。"
% G7 l: K/ Y5 o: P. A - (dolist (prompt prompts)) R! n2 _2 Q: a7 j) F7 Q
- (def-tutorial-room prompt)))- i* \5 q l* A# Q5 J% p
- ( `. N% ?* |/ }6 \( U6 x) M
- (def-tutorial" n; g; E$ D6 K/ {' o5 F, {
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。% L' R( @3 z3 S
- 1. 教程
) h8 Q) l( d; ~2 a- } - 2. 入门(3x3) k+ n% j! J/ J( @4 e& `
- 3. 初级(5x5)
+ O. ?) h; Y5 p2 ^, {; K- Q6 |6 S! B - 4. 中级(7x7)
( k9 Q) {1 I0 W) l - 5. 高级(9x9)+ a% e: x H$ \! j$ p1 o( i2 V
- 0. 关于作者+ q4 U6 Y1 D+ B* j0 S& l) Z! g( a
- 请选择1-5开始新游戏:
- G6 V: S! Y- F6 t - 您现在正在游戏大厅里。
; L& w1 ~1 S8 g, f) i, n* M - 请输入“2”进入入门级房间"
) `' ]% R r) G5 _5 L1 X - " ①②③% J" C U9 D6 Q) v
- 1 ■ / h3 w4 c! E, b$ {
- 2■■■
) x. ^# Z/ ]- x; B0 p" q - 3 ■ 5 H7 W3 j5 F6 B, [
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
7 ~! z6 N4 F+ }& ~2 { - 请输入“22”来关闭第2行第2列的窗户。"
' o8 Y, v, [* \& y- r7 ^4 i5 {" c - " ①②③( V" Z9 y- T2 {( z' @# Q; v
- 1
, i1 m# y# z* n3 c1 L" @: r - 2 0 I8 M% Y; ^1 b
- 3 " K, d' ~) z0 C. t* E
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。& H* l7 k+ d! O7 V* ^, K
- 请输入“11”,试着开启左上角的窗户。"
! j, O6 P+ S2 j) r1 P- A - " ①②③4 _3 k4 x( _0 V1 e% B0 ^0 L
- 1■■ , [$ e8 C& f; U) N# D
- 2■
, h" N X7 x1 s - 3 - n. S5 a; T% v' I. _) N% K
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
# m+ {8 X+ G# j7 t - 请输入“13”开启右上角的窗户。"
4 x" u( ~* i8 `) k" E, \ - " ①②③0 p& U6 N* b1 _2 F; ]. y9 T
- 1■ ■/ P* S1 a. d' n5 f2 R J. a$ |% F
- 2■ ■
7 M2 o, {. h: z$ S: S* ?$ s: C - 3 6 A% v8 @" G+ g
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
1 Q: U9 m J: R) i - 请输入“31”开启左下角的窗户。"
) S+ G; b/ {* e - " ①②③
1 J% ^% E5 I4 O J1 D X4 i - 1■ ■
, ?) I- a) H6 X* Z# Q8 D - 2 ■1 i+ S6 U! R3 {9 U
- 3■■
4 k4 x/ I, R) b7 u2 ?1 H; Y - 此时,总共有5扇窗户被开启了。
* c( f. Y" `7 B7 W5 v* | - 请输入“33”开启右下角的窗户。"
8 L! y; ?( S0 Q: z1 K - " ①②③0 E2 p5 J3 @' k: O- |9 ^/ c
- 1■ ■8 T7 r( y/ F' U
- 2
" |7 [7 z9 R3 t, a0 N4 a - 3■ ■6 y' }; ?1 D0 b. S- ]
- 现在,只有四个角落的窗户被打开。
0 K- m. a3 h! v - 请输入“22”完成最后一击!"# `8 h7 }5 h4 M2 R
- " ①②③
! b& F+ v1 h( r% f; ] - 1■■■, q# h! B |; \* z% e/ |8 @6 @3 c
- 2■■■
: e+ o1 H- p" V' [3 I - 3■■■
- m7 P+ j: J: e o" S4 F - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
& B0 ?( Z( \% o; k5 ]6 S - / i( A% `$ m5 s$ p2 e: m, v
- ;;; 棋盘4 y5 `8 {) N6 g
- (defconst *wechat-5x5-white-chess* 12288
$ `# U7 W$ x8 J1 {& G - " ")3 a1 s8 K3 ]4 D4 I
- " }. ]/ e" u" C0 G: L# Z, \
- (defconst *wechat-5x5-black-chess* 9632
! J9 ?5 z! L* S2 m, Q5 `4 H - "■")
. X- o }+ N+ U: U - " N/ e; ?' Y) r8 I, p @* I! Q/ l
- (defmacro with-board (&rest body)! F; K5 m9 u5 s6 r# U% i" [
- `(with-temp-buffer
H0 P; e7 V# W/ S - (unwind-protect( z$ m' m6 n$ Y! o: Z
- (progn
. V T* T# D0 k; d1 {2 Q5 e - (if (session "board")
4 b- ~. p, `( w, O - (insert (session "board")))
7 t% z! [# u% T( E+ Q' d M2 W, f - ,@body)
: B `3 m1 u9 D! e8 @9 Q- v - (session "board" (buffer-string)))))
( i3 C G- l3 U2 s7 \; U - 9 \3 D* m, c& Z" j, `
- (defun board-init (size). b! O0 s& E* v) Z" M$ P
- (session "size" size)+ ] L; E* S; i1 Q
- (session "step" 0)
- S! q2 ?. Z: R$ d! { - (erase-buffer) s+ _- \8 u; I& Y# y
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))% ~1 M4 t& @* J; | {/ n4 \
- (dotimes (row size)
3 k) J/ C9 e9 F" X5 n' D - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))5 ]5 v+ U6 z( p; v* W( r$ H
7 r, `, o6 b/ R# a' r1 W- (defun board-contains-p (y x)
0 j Z7 F! b$ [' I4 Z; d7 J4 U5 C! D - (let ((size (session "size"))); B7 {# p0 W# h1 {4 i5 t
- (and (<= 1 y) (<= y size)
9 k1 k( L& p/ J6 {8 G6 | - (<= 1 x) (<= x size))))# E# z! M7 |1 g& N9 N6 [
1 d* P. W0 H. Z' V- (defun board-toggle (y x)
6 d+ o6 F7 [$ o+ A/ F, w% x: c6 e - (when (board-contains-p y x)
: r* k% P% F- a* ?* s4 w9 X7 f; @ ~ - (goto-line (1+ y))3 ~# e# K/ b0 U9 J5 U N
- (beginning-of-line)0 F& l* N4 x2 u3 Y7 Q( {5 x% r" ^+ E
- (forward-char x); s/ z: N( K/ {- `, `
- (insert (if (= *wechat-5x5-white-chess* (following-char))8 P; h* z8 s, u! c4 M
- *wechat-5x5-black-chess*4 H" t+ R2 o3 t3 C! N
- *wechat-5x5-white-chess*))
3 S: }" p& a0 K1 C - (delete-char 1))): a3 G" o9 n( i8 B9 V
- 0 }# D8 p/ G/ Z8 l" b$ Y" L0 J
- (defun board-put (y x)# v' O8 }# w+ t K
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))! s) ~9 d4 u1 T# I
- (board-toggle (+ y (first dir)), }. H5 A+ |$ Y- @$ q
- (+ x (second dir)))))
. t t9 T" _. y& I# d
# Q9 e" X# }! }% [# T* T8 w5 M: j! n: @- (defun game-over-p ()
9 S3 Q) u$ f- h f# K( P - (beginning-of-buffer)! n6 C7 T: `' z3 @
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
; M8 Z1 w- E; I" i" V
1 b5 N5 N3 A$ J) J4 k- (defun board-show ()% N& [8 h0 ]! ~- Q/ ]
- (with-board4 F* d' x7 p* s9 C+ K+ z
- (concat (buffer-string)
. O e4 x5 n" X0 E - (if (game-over-p)
$ m2 l9 b' |0 i - (format "共%d步,输入任意内容返回大厅" (session "step"))! b9 K" Q% _9 z' ]+ m' A. W/ ~
- (format "第%d步" (1+ (session "step")))))))
1 o1 g/ A2 h7 d; ~2 r, w- z; K
$ d0 D8 P4 o% z- (defun board-position-parse (cmd)" O/ k0 f4 r8 [, Y
- (if (= (length cmd) 2)
! q. [6 s- ~+ ~) @1 M - (list (string-to-int (substring cmd 0 1))6 h! }( J6 }, q$ K0 C
- (string-to-int (substring cmd 1 2))) y- j" I) R3 a! V
- '(0 0)))# Y' Z: S. F# I: b6 L: h# [
3 v; Z" L* \. t8 V- ;;; 游戏房间
0 o9 m! f' B2 M" e - (defun game-room-init (cmd)1 f% V: q5 s2 J4 P/ T* n' O# A# E
- (let* ((middle (string-to-int cmd))
* H: B( I4 g5 A, A5 A: T( I - (size (1- (* 2 middle))))
/ M: b! I# |4 t N3 `. H - (with-board
7 l4 w7 A4 i/ w4 A0 W6 D( x9 M4 \ - (board-init size)2 u E4 q+ P* D- f, [- P6 z
- (board-put middle middle)))
[+ V e; j& |3 v - 'game-room)6 y1 N, p" o# `6 b: o
- * c9 G6 j1 S; o. V5 U$ z9 d
- (def-room game-room
3 J8 _; g. i% [+ ^0 C - #'board-show
, K0 e: ` B' n' l* J - (t (lambda (cmd)
0 X$ e3 F6 e& T# V9 r# W! L+ } - (with-board
' k) t- M4 { O8 N' y - (if (game-over-p)
/ \% @9 s' r$ {5 ~" [ - 'living-room
% f8 c8 a) P" F5 G% r( I - (destructuring-bind (y x) (board-position-parse cmd)
$ R$ Q6 @ [; T+ w$ M2 s - (when (board-contains-p y x)
7 { z c9 p4 {" I+ P# U" S! q8 s - (board-toggle y x)
' n# y$ G) ]% Q5 C+ g! | - (session "step" (1+ (session "step"))))
% t: O6 w W5 d! [5 {- b - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|