wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
4 t7 n4 G9 D% [3 {借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
3 A) }' I0 S* k* t2 b- <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 u* t0 e8 t# G - (def-map "/game/5x5.el" ; 对外开放的URL- V% k4 ~* Y! w' o0 e
- 'tutorial-room-0) ; 默认的入口
# I! n) [' q. }+ D- z* `
4 {" d' i5 |! ?1 }$ b* u- ;;; 游戏大厅2 \# i0 K* f% Z& |5 Q
- (def-room living-room
1 w2 O$ A, q3 U: U7 t - ;; 进入该房间后的提示语7 w0 G3 q7 S$ E9 P n
- "1. 教程
: ]( e X/ o; L7 Q - 2. 入门(3x3)
* Z9 ?1 P1 ^5 [! L) h& P2 Q - 3. 初级(5x5)+ j1 ^% W) \, r4 l
- 4. 中级(7x7)2 e( |& ` n+ ?
- 5. 高级(9x9)
! |. E# c* R) ^) w3 m - 0. 关于作者) K$ f# O% L4 P% O
- 请选择1-5开始新游戏:". T0 E2 j' K; X# W/ n+ h: f4 L
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名1 p3 t7 |7 F" ]0 Z1 l# a
- ("1" tutorial-room-0)
0 z) z2 Q2 |2 z4 \5 s q - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配/ v( I" ?2 c! _5 m7 H* x1 L
- ; 相应的返回也可以为函数,动态返回房间名
' X0 f1 I/ }! {! C0 Q- i - (t living-room)) ; 如果条件为t,为永真. L3 s$ I* Z; L( {+ v
* l: }& L6 i- Y3 W- ;;; 作者信息8 [ [3 |, l; m5 [1 T
- (def-room about-room
$ x' B! q4 i2 D1 ? - "作者:redraiment
! G6 I9 u! g2 p( T+ i2 e9 y - 微博:http://weibo.com/redraiment
6 k% i) v) x# k+ P, C - 有任何建议,欢迎在微博或微信上联系redraiment。$ w2 W2 z$ e8 C+ f+ X$ _3 s
- 请输入任意数字返回游戏大厅。"
! y, [3 f1 s8 ` - (t living-room))0 y; G( Q5 `9 P: Q$ F
& u8 ` q+ @, I6 o0 ? m* B) ^5 o- ;;; 教程
! U3 i9 t. n* {3 T0 _# [ - (defvar *wechat-5x5-tutorial-rooms* 0" g3 |7 e' J7 Y- ]6 v B
- "The number of tutorial rooms")5 g+ v+ u% V) O( h% `5 f, T
- : B! W E' r I0 y- R: i1 d
- ;;; 简化教程的定义# _4 n( G- S# A' C. L7 ^
- (defun string-last-line (content)2 T$ `5 l5 M- s: q* J; f
- "多行内容组成的字符串中的最后一行"
+ h; S4 w- D/ r" @+ c/ [ - (with-temp-buffer% K7 |% S" L* x* {; a' [+ D
- (insert content); j; l5 E5 S9 x8 b
- (buffer-substring (line-beginning-position)1 g$ L% G% F. j m s
- (point-max))))7 t3 d$ _5 n. \' {( N9 t) v
! {6 M6 j. Q1 b! G- (defun def-tutorial-room (prompt)0 m2 y3 [; G: b7 y% T0 X0 s9 c9 T
- "根据提示语自动生成教程房间。+ H9 S, C& l0 h1 `, @3 N
0 p0 u- P1 h0 C$ H9 n C+ U- 1. 提取最后一行作为问题;& n2 i' {' a0 q# N- S
- 2. 分析问题,获取期望用户输入的内容;
3 l) W; f1 f8 i% D9 n - 3. 定义教程房间和重复提问房间。"
6 p h! k% B8 n - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))! [+ |) I3 Z. @9 t4 v/ D8 W8 o
- (repeat-room (concat room-name "-repeat")) U- k: V$ \5 Y# x% T: ]- B# t
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
1 N# S# e, n5 |8 l8 Q. ^ - (question (string-last-line prompt))
5 [6 Z! M/ ^4 A& i$ ~ - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)& Z" T( ~4 Q1 m; ?6 o7 t: q
- (match-string 1 question)))/ k v4 V. i8 M6 K1 `
- (doors (if except
6 r/ I; G2 g# s4 h - `((,except ,(intern next-room))
5 Q- z/ b/ a' _! [3 X/ Z - ("q" living-room)" | g |9 Q$ e S% V) l
- ("Q" living-room)+ I" t/ }: o2 n8 H0 ^2 s+ H4 k2 |2 X
- (t ,(intern repeat-room)))
4 X" K! B9 D, u+ _1 n) s, ~ - '((t living-room)))))
/ i$ X$ [- w! U9 I - (def-room-raw (intern room-name) prompt doors)
, k. [) P$ c/ E( v7 a; ^3 F - (def-room-raw (intern repeat-room) question doors)))$ Z9 J7 l* P5 A: @6 B
- 9 ^) G! b' Q' I# S; t( C& T
- (defun def-tutorial (&rest prompts)& x# ^/ b' [% s% w1 @' \/ r
- "批量生成教程房间。"
0 Z9 S2 O( y4 s4 ~; W, D* ]# y - (dolist (prompt prompts)
$ g m j* v1 R. \8 _; _) ]% g" \ - (def-tutorial-room prompt)))/ g; ?# k% p% s# A: L
- ! D2 f8 E# Q- L. @! C2 W ?
- (def-tutorial
9 a' O/ z4 L( L' b: E" X - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。6 z. G+ F- [0 j* ? p( L, C( d
- 1. 教程
: n1 W0 c6 _7 P8 f7 k. u6 t0 D2 Y - 2. 入门(3x3)
( \9 I3 Y4 K5 r9 |/ p# Z - 3. 初级(5x5)
' D3 ?- r( T) L9 g2 t - 4. 中级(7x7)
0 R! E" s J+ i1 q. A, x - 5. 高级(9x9)
, V, D# q8 [! k* ? - 0. 关于作者0 _5 k& l2 R8 M( o
- 请选择1-5开始新游戏:$ g c5 c% Q; |1 A' E- X/ y
- 您现在正在游戏大厅里。
/ z* }: c3 b; I" n6 c( U6 ^ - 请输入“2”进入入门级房间"
7 ^, z$ _$ o% a/ J - " ①②③
% t9 F; _. `3 |& s9 d( ? - 1 ■ 1 r) d: v% W, M9 e6 E
- 2■■■2 i8 R+ U/ Y1 l8 k3 A
- 3 ■ 8 E: \ ?, p/ ~
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!) Y- J% S2 L3 H( F; N
- 请输入“22”来关闭第2行第2列的窗户。"# a: a1 D/ G$ u1 f; C9 x; i
- " ①②③
5 a3 T% D6 u* j6 O+ s! n, G - 1
5 t& q1 `0 h$ {4 K$ P& k4 O$ M( ^4 G - 2 7 g7 O- D$ v7 r3 ~6 O H; @ l
- 3
( W2 w( ^) d; b - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
& e' ^0 H$ @1 _) i" o+ U" @ - 请输入“11”,试着开启左上角的窗户。"
+ X$ r9 F& k4 N6 y) O* U - " ①②③2 ~2 A- p( I0 [1 w) I: V( t6 G9 ~* d
- 1■■
7 ^' O% ]& v- M- B2 h - 2■ 9 F4 }& [! A6 {! c
- 3
$ D& {6 u( t. X" \ u - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
" y W+ W2 e9 k - 请输入“13”开启右上角的窗户。"
2 U7 Z$ B5 M0 [! [ - " ①②③
& l: ]! S) F5 \8 E' Q8 I$ B: M - 1■ ■. C0 l4 s) c. F
- 2■ ■
\( V0 p. U% n! V. p - 3
& _ O: b" C7 y8 d( X3 c - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。2 T7 J) q7 u1 j, i
- 请输入“31”开启左下角的窗户。"
( L8 K+ k, D; L1 ~4 A P( j2 O( P - " ①②③
' E& Q8 e' u" R J/ x - 1■ ■
6 H* v0 n7 ^) q2 c - 2 ■, I0 x$ Z0 Z4 F, P% C
- 3■■
2 A1 H; j! v( `1 } - 此时,总共有5扇窗户被开启了。- m5 Y6 I: L% v# v5 G2 ]
- 请输入“33”开启右下角的窗户。"
6 a& X* M: P: r1 w - " ①②③
# \$ h2 W3 o6 @% F. M% s - 1■ ■& a5 F2 Y; C% r# I# |
- 2 : S& Z9 p* N$ N' v+ S. I: x/ M
- 3■ ■
" ?, ~( A7 g0 _( G" R* r) z5 { - 现在,只有四个角落的窗户被打开。
. \- }# S; F5 x5 C8 _& G. Q3 S; G: ] - 请输入“22”完成最后一击!"
; W' w* j7 |7 Z3 u Y - " ①②③& l& z9 k; l1 q' x+ J) z1 Y4 u, K; h
- 1■■■* }& S# N$ t- j
- 2■■■
, J9 p- R& o8 z! w( u8 N - 3■■■
$ X# y) L) N. a4 [9 R - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")1 A& h; Z! N0 @6 L `
! H( {$ ~. ^- E+ Z1 O1 F" E1 G; W- ;;; 棋盘
+ j: ^) \; b7 ]3 {& M - (defconst *wechat-5x5-white-chess* 12288
3 y" e5 I# j( p( D8 {5 Q - " ")% d& x. M) G- P+ K& ]( H( N8 {% m
- ^. C6 j1 T! I- (defconst *wechat-5x5-black-chess* 9632/ A" b: Z$ o8 v1 n
- "■")
1 G5 }( C. i& ^1 n6 U
# ?+ M4 Y, ~1 }( }- (defmacro with-board (&rest body)
, E' G3 I/ Z6 i2 Y - `(with-temp-buffer# T( P# f) b3 v) k6 j. y
- (unwind-protect
; J+ A2 @4 D4 S q: t u - (progn/ I" i+ k2 {& z" h$ Q0 B7 k. M$ X
- (if (session "board")
- W* a1 O- b/ n' \& |# U7 @ - (insert (session "board"))), t w1 ^6 q! h; x" W: O
- ,@body)
8 F! C+ u+ U3 T" X+ U6 V, Z! O! d/ @/ E - (session "board" (buffer-string)))))+ p) D$ J' \# E! {
( ^+ _. J5 o- T- (defun board-init (size)8 |4 _% o" Y2 v2 F) X
- (session "size" size)4 X- p4 m. [# ]. S, U1 h: o6 Z
- (session "step" 0)6 D+ S- D# d% |/ M6 i+ ~' N, {
- (erase-buffer)
; c. y; ?& B& p: h0 y* t8 @7 } - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
: h% \) l" G& B: @ - (dotimes (row size)& q% A3 b0 u8 v
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))1 A3 o' X! p- e3 Y; V
( E- q0 z0 Y9 I+ o: a- (defun board-contains-p (y x)& |, |7 u# S" V+ Q: `
- (let ((size (session "size")))( L$ q* A& K6 v
- (and (<= 1 y) (<= y size)
& ]9 p; X1 f/ | - (<= 1 x) (<= x size))))' G- H5 E$ g* Q
. c. j5 e2 S2 n s5 b- (defun board-toggle (y x)+ j2 {6 f1 f& M
- (when (board-contains-p y x)
1 r" n- g5 P3 R1 h0 V. [ - (goto-line (1+ y))
- z" c b* X( q - (beginning-of-line)" v% o0 R1 ~+ s4 z+ {* }9 |
- (forward-char x)" ~( W, v( j0 n' Y. D
- (insert (if (= *wechat-5x5-white-chess* (following-char))% F u* @7 Z' |' p
- *wechat-5x5-black-chess*
5 ~+ Y- h8 Q7 { - *wechat-5x5-white-chess*))! m1 z1 ]. g" ^/ _8 f; s
- (delete-char 1)))0 b- [# w1 J; D+ h
( O0 a& g) i6 o; K }* L( I) V- (defun board-put (y x)
) k! C, P+ \" P5 E2 Y2 s - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))+ v6 @8 ~9 I: A2 _
- (board-toggle (+ y (first dir))
; g3 I; m$ p$ f1 D - (+ x (second dir)))))
$ }: V }$ B5 }
# Y# \) A: ~1 h8 x7 ^; ^- (defun game-over-p ()' w% h5 \- ~0 `# D$ k# ]
- (beginning-of-buffer)& n+ P4 |+ g$ D* U: `7 N2 P! h
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))- Z0 Q. m4 G) y$ Z$ j
/ E6 O6 e% S1 ~. i) P, ~- (defun board-show (). p* k5 f+ I* J& |( |/ D
- (with-board
$ O3 y! I" L5 F+ B0 \5 U9 T - (concat (buffer-string)* J3 H. c r( [1 ?5 F
- (if (game-over-p)
& `0 D1 O& i+ I' K - (format "共%d步,输入任意内容返回大厅" (session "step"))/ M; ~9 S/ M* n* S
- (format "第%d步" (1+ (session "step")))))))
1 S, a* \$ B0 m4 O; _- v - ' v2 S# g" A1 X& i: _, z
- (defun board-position-parse (cmd)
3 S2 f. l( h! m q; }9 ?/ b9 ~ - (if (= (length cmd) 2)1 |: \& P- X( D- x7 Y w2 `! J
- (list (string-to-int (substring cmd 0 1)) g4 p$ s8 r3 K( j5 N, r7 P
- (string-to-int (substring cmd 1 2)))$ P! i4 k- u9 Q/ i" V
- '(0 0)))& {# B+ r7 C3 f2 ]& X
- ' u1 M7 A9 M& u! K6 x
- ;;; 游戏房间
( h( A+ J9 f3 D& Q; y - (defun game-room-init (cmd)
* V) M* G. M( e, }# B' g+ B - (let* ((middle (string-to-int cmd))
! j+ F ~! b V; |& ?& }' b, M - (size (1- (* 2 middle))))4 T$ h$ j P( [- n5 @# \9 }1 M( X
- (with-board$ A; U& O9 C* s( U
- (board-init size), c& o. G2 n' H' O( d
- (board-put middle middle)))
! N7 g$ V$ s, C. g( p - 'game-room)3 N$ A$ V5 Q* J6 T$ I7 W, Y
/ E7 e" F C9 V) v1 `! D, }- (def-room game-room( h8 l4 L" a/ t* a% V' p
- #'board-show
Y6 {8 ?& m2 f' `$ x9 K+ _0 O, g - (t (lambda (cmd)
; g- e) G4 H, I- C: f5 W - (with-board
! _( H: \5 Q R - (if (game-over-p)6 H& G' d! i1 E: J
- 'living-room
/ A/ g" {8 A( u8 O& u - (destructuring-bind (y x) (board-position-parse cmd)
1 @' r6 g' q1 p* i - (when (board-contains-p y x)& H2 T* c9 B9 R' G& g# S$ E. e
- (board-toggle y x)
: S1 y; ^- X( h0 u9 [( `, U, ` - (session "step" (1+ (session "step"))))
. a# v: y, T$ J2 o2 X3 u - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|