wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。4 k+ P2 N' y; Y. f
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- 7 l7 n5 {7 D$ m4 Y' l1 s, F
- <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 x: Z; ?' [) {. M
- (def-map "/game/5x5.el" ; 对外开放的URL# O: @# p. c! [ f% J
- 'tutorial-room-0) ; 默认的入口) X- q) q1 \6 E3 i8 j& r4 o- j
4 j. ~4 O1 e; C& G' n O/ L+ w5 {' u- ;;; 游戏大厅$ z+ d1 S4 m, @- t3 Y7 B
- (def-room living-room
5 @0 \& {! Z$ V( l8 {2 O% p, } - ;; 进入该房间后的提示语 m. }/ R; D' J! ~- i
- "1. 教程7 v& [( C: t5 t" P% w- ^
- 2. 入门(3x3)1 K1 i0 C' s) i$ v9 f. D7 V/ p
- 3. 初级(5x5)
' ^6 V& E( ^. @/ y3 p" \" N* f - 4. 中级(7x7)
: p5 i: F- W; P J - 5. 高级(9x9); Q- v, }4 c6 L
- 0. 关于作者
+ p9 Z! P0 H3 j, ]4 ? - 请选择1-5开始新游戏:"9 u3 J2 ]% y& Y3 z4 N) j
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名/ r3 @- } ^7 b& u
- ("1" tutorial-room-0)& H: Z' K! `: k% y* Y$ E
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
2 b% [# @( `* [/ \3 e - ; 相应的返回也可以为函数,动态返回房间名* W# e7 M; ?2 f. |2 T% e! W
- (t living-room)) ; 如果条件为t,为永真; ? i# h3 [3 A9 N7 c
- 4 z& m, L6 }8 D0 J
- ;;; 作者信息: e9 v# H. Q$ x! g+ F% Y
- (def-room about-room. A( [1 U2 A" P
- "作者:redraiment( I7 X0 T- o9 U z2 E
- 微博:http://weibo.com/redraiment
8 k' _; [7 H9 ^& }6 n; I - 有任何建议,欢迎在微博或微信上联系redraiment。# C' ~+ Q* V: h4 |
- 请输入任意数字返回游戏大厅。"
2 g! x. `% P" b2 A a) O) u - (t living-room))" M E- G. @* p E k
7 t$ E, i8 u. w$ S# M, d& ]- ;;; 教程( d% J: s9 F9 E% Q9 s& w# D
- (defvar *wechat-5x5-tutorial-rooms* 0
/ r* u6 l* s8 {9 C3 b - "The number of tutorial rooms")
$ v, [' g8 E2 B5 h, S7 G, }' H - * N2 ?. O2 S& v" M, o. C3 J1 W; Q4 M
- ;;; 简化教程的定义
6 i9 i) `8 _7 y8 X0 S) {! s - (defun string-last-line (content)
2 v: A" w0 t; E5 C - "多行内容组成的字符串中的最后一行"
5 y* j# n/ w0 t" u* x - (with-temp-buffer
; ]0 @: G3 g% O3 M6 G1 u- C - (insert content)
( w3 J0 M) Y7 S# D! x - (buffer-substring (line-beginning-position)
5 T# m$ |0 w5 Q7 K' l - (point-max))))
/ D' R$ U; Z. V% E* b! F& p - 0 ^9 p. ^% R L" ?
- (defun def-tutorial-room (prompt)7 @0 ~' n- D9 K+ K& Q! n
- "根据提示语自动生成教程房间。+ p( P: x' \9 d7 |0 i
- 4 O9 M: F# k+ y$ j
- 1. 提取最后一行作为问题;
. C, c+ o! e- C8 J+ B - 2. 分析问题,获取期望用户输入的内容;
Z( }& p3 R0 l% t1 B( U - 3. 定义教程房间和重复提问房间。": H! J7 x# @* Q# `0 J1 `
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
9 }/ S7 I) G9 C# N" a1 C' _1 s - (repeat-room (concat room-name "-repeat")); }% {% x' n6 t( Y, C1 v; {; L
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
8 _0 F! H& \* E3 ~; i - (question (string-last-line prompt))
! s2 M! X3 p" G# C" S4 G( A/ B - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
: u$ C" Q1 o! T# M - (match-string 1 question)))
, c3 K$ t* I/ k# w8 q3 f3 ?+ o - (doors (if except# ?, o# s# C9 `7 G: S, E: W
- `((,except ,(intern next-room))5 E: q. W2 V W1 `4 n
- ("q" living-room)
, i; N# \0 R7 O: j1 y6 U - ("Q" living-room)
) m2 f) ~- [( R# j6 N3 d) z+ O - (t ,(intern repeat-room)))
3 B L! v. i9 v) T( R0 h5 N; ] - '((t living-room)))))
6 ?& I1 b# D( A- [( N9 G - (def-room-raw (intern room-name) prompt doors)
4 {, y3 s1 @1 x - (def-room-raw (intern repeat-room) question doors)))7 I! U3 V- z" J, r/ K
! P: n7 N( n5 c- (defun def-tutorial (&rest prompts)- E3 a/ T1 ]$ `3 a8 c2 T" n# n
- "批量生成教程房间。"0 C; g6 @; {3 ^4 V4 X: V$ ]! N% c
- (dolist (prompt prompts)
0 H' m. {$ c- q% C' ?7 M5 D - (def-tutorial-room prompt)))
" m- B% s ^0 y/ D7 b- o6 ]
( _$ A. e6 [- x- (def-tutorial: L9 S4 i' f0 S4 `; U
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
+ t2 D4 c1 `: s7 P - 1. 教程% r8 H( y9 k9 L: G# Q
- 2. 入门(3x3)' z+ L* o& n5 N, R
- 3. 初级(5x5)* l! g3 K, N" Z
- 4. 中级(7x7)
0 H# |& C7 O& d3 Q8 A7 r - 5. 高级(9x9)0 x0 d9 z2 w2 D: u
- 0. 关于作者6 m7 ^ D7 e) g: G- e! U4 D9 g1 U
- 请选择1-5开始新游戏:8 q6 t6 e0 {" @# T! M
- 您现在正在游戏大厅里。3 ^) m' k$ `% U- X
- 请输入“2”进入入门级房间"% }8 H F, e5 r" ]/ ]% D2 ?
- " ①②③
( k$ F) S- I. l* R8 w* S. h6 v - 1 ■
0 M3 M: U Z" h! _$ U& i, { - 2■■■* v6 O7 F. ^% k" e
- 3 ■
( O2 K9 n4 K; u4 i2 S j8 E - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!: O: ^- X( q8 c+ r6 f
- 请输入“22”来关闭第2行第2列的窗户。"
2 O, z' h- b) R - " ①②③: P) G# T ]0 m6 Y4 D5 w$ @
- 1 % S$ N: @' d9 @" i
- 2 8 O/ \: v3 i0 i! o$ z' V' q
- 3 : I# g1 t1 J, Y0 n( i. P
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。: [9 h8 }% o. ~0 H
- 请输入“11”,试着开启左上角的窗户。". F& r* x- [; @: C( ^
- " ①②③3 s; C; Z( I6 N) p
- 1■■ 4 \, P) d8 h7 \% ?# E8 `
- 2■ 2 Z# v" H. v: N- ]2 u `
- 3 2 G8 w% U. X9 ^: O6 _' W; ?
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。2 j5 J/ c+ C1 F" o0 P+ [; J: r
- 请输入“13”开启右上角的窗户。"
, X- V5 L. `: a; G - " ①②③1 Q( Y& e: `- i( t H
- 1■ ■
) t$ ]; k# L2 M" C I) S/ }& |; d - 2■ ■
, \( a9 M* z6 O: E9 p' R - 3 : i! h( s, }4 A9 P6 ~! L
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
: W; Y1 E. e- h - 请输入“31”开启左下角的窗户。"# ^( f0 n# ?- u6 d0 Z
- " ①②③" b( I' w9 j% C/ a3 L
- 1■ ■8 L6 j! Z- I" y9 u7 D
- 2 ■
7 z& P4 p7 c% |3 ] - 3■■
* ]# }: ]) ]# j; D - 此时,总共有5扇窗户被开启了。 \" j( D$ g) J5 W9 M& x2 d
- 请输入“33”开启右下角的窗户。"
0 v3 [, y" m ?+ m) o& [ - " ①②③# u9 Q* f o* M" X' Z+ f
- 1■ ■
& v& H! K; U2 N t8 d7 q - 2
\! R/ g# j7 x2 X* M, V2 P( p - 3■ ■2 i) w# T2 B; B3 c7 R* i, i m& ^+ O
- 现在,只有四个角落的窗户被打开。
) ?( Z& V9 R' J4 g7 ]- v - 请输入“22”完成最后一击!"# z& ^- |) T- U6 f8 `/ K o% i. g
- " ①②③# ~5 r; b: C! u7 V9 }
- 1■■■
* x7 e5 `) m% U4 r - 2■■■* R7 {9 d# x+ E8 X' u7 K4 u
- 3■■■/ Y' S2 Y i0 V# H$ |
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
5 b* z. G: z1 `4 \% D6 j - " c/ F0 J9 w6 Z- R' W( Z" F7 ^: J4 s
- ;;; 棋盘
, k1 P. g: K, y+ q8 l" J) J: V) _ - (defconst *wechat-5x5-white-chess* 12288
- y( y4 r8 o5 Q0 U h8 O; R - " ")
' z0 y- J& o* G( e) o! f" }
6 q' ~: W. X* V: x" U- (defconst *wechat-5x5-black-chess* 9632
# ~0 k: I V/ Y( f& c - "■")
$ |+ Z( H3 V, e. _4 m/ |9 \1 K - 6 P4 {7 ]2 B2 B. \0 Q( w
- (defmacro with-board (&rest body)9 z! w. i) ?! ?- s( P& N
- `(with-temp-buffer
) L% Q8 K% n- ~1 o }' X* s - (unwind-protect
! ^7 q3 t' p" q" l; p! L/ I( W - (progn
! v9 J q7 q" I+ D/ t" `, D8 C - (if (session "board")
! O% B" h y; R# B; g - (insert (session "board")))
+ b) w5 ? q7 ~: a, P' P - ,@body)4 W9 ]) s9 \4 v' e5 R" f1 e
- (session "board" (buffer-string)))))# j) m A% }+ n5 B% J% _( i
) j* n3 s) T, w* O2 L- (defun board-init (size)
; k' H% m: ^+ [; H) U - (session "size" size)
( ^" b7 [6 W' R6 N: Q: r - (session "step" 0)& w3 V5 ?) f P
- (erase-buffer)
; E9 b+ v0 q2 P8 Y3 @* T - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))6 _1 ]! |8 T3 _
- (dotimes (row size)5 H6 Q) {) B" B3 P' ^; d
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
/ M0 ~4 k! G4 m- u. X
; u2 D: r3 R J j1 l6 x6 `. \% V- (defun board-contains-p (y x)5 z0 T) k) a8 c
- (let ((size (session "size")))
% R ~; |$ E' n - (and (<= 1 y) (<= y size)
3 H# [; Z$ u" E4 i# ` - (<= 1 x) (<= x size))))
2 |# r9 @# D1 l, d% ^& s& G - + }. t- X! N) }# ]4 k
- (defun board-toggle (y x)0 W) w1 ?8 _7 U* C
- (when (board-contains-p y x) T0 S' |8 j* f2 u8 q
- (goto-line (1+ y)). ^- x6 ?' i/ r) ?7 T/ r/ i7 X
- (beginning-of-line)2 I4 L$ a5 T& ^
- (forward-char x)6 N' [& i+ \* d, T4 P- `+ Z
- (insert (if (= *wechat-5x5-white-chess* (following-char))
! I7 C' w0 [' _ - *wechat-5x5-black-chess*
) s: A3 H' ^* P& v9 u: L2 T - *wechat-5x5-white-chess*))( y& \! q* Y: n; j
- (delete-char 1)))
: j# s, k& k8 Q( j0 A; D7 w - ( x/ Z8 g/ t8 p5 N
- (defun board-put (y x)
/ A. j6 M) O/ C" w - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))1 J! Z7 D V; A. [/ w7 K8 q
- (board-toggle (+ y (first dir)); W$ W" x$ m" \; b+ I
- (+ x (second dir)))))6 m/ A# T0 J# h) J: l; g4 u& F
: S9 S r0 G- n- Y4 C- (defun game-over-p ()! W8 g, S: K% ?
- (beginning-of-buffer)
8 O2 O/ j/ `; d' M3 t [5 N+ y8 p - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
* r! x: b1 O( D8 @ - + {# Y0 [3 G- H
- (defun board-show ()
- V) G7 Y7 m: w1 I* W1 t9 J2 y: r; \( G - (with-board
7 n% k& x/ @6 ^5 p$ o# {+ s - (concat (buffer-string)
; I/ A: v0 O* J; f4 j2 O4 v5 U - (if (game-over-p)! x1 V8 x2 S# \) t' `
- (format "共%d步,输入任意内容返回大厅" (session "step"))
2 U' B) J# R! g& D - (format "第%d步" (1+ (session "step")))))))
' W3 ~& b. f, l1 b5 j" f7 V* V - 8 |9 T3 ~6 k* q
- (defun board-position-parse (cmd) I. l- R: `3 y s* T* l' S
- (if (= (length cmd) 2)
3 P, A9 J) B+ ~" ^ - (list (string-to-int (substring cmd 0 1))
4 O3 a9 {2 P5 ~ - (string-to-int (substring cmd 1 2)))
+ s% Z( V$ n' O( W3 d [ - '(0 0)))
' L0 b2 Q" S+ O8 c - ( p* u# p/ s7 s# V. m: `7 D7 ~0 ~/ H
- ;;; 游戏房间
2 ?8 H- ^! D( }4 f - (defun game-room-init (cmd)
" ?( o2 Z, C# n: z7 C: h - (let* ((middle (string-to-int cmd))$ B) n% s( i% {2 ^& [9 ~
- (size (1- (* 2 middle))))
0 A X# g d6 { - (with-board7 b: S2 [6 z3 W: m% E
- (board-init size): I& g+ U7 y, Z& v$ r
- (board-put middle middle)))( h+ g) o) D' h" f( d8 z
- 'game-room)
* X9 g% v2 w) S
2 }; @. o: z3 W; T- (def-room game-room( Y# d! b: Q5 \
- #'board-show. M& M; Q- i7 C8 i
- (t (lambda (cmd)! O: h0 n, q5 m5 |* k8 |! U
- (with-board
% p4 I& f1 e9 U3 S: R4 g# T - (if (game-over-p)
4 j6 a- A0 W& {6 L- `( l - 'living-room0 b( m' J3 j: Y% S9 @
- (destructuring-bind (y x) (board-position-parse cmd)
0 v+ m g2 X7 \" `0 Z - (when (board-contains-p y x)
6 q3 C6 X- o2 }, o6 O6 i3 } - (board-toggle y x)
% ^ c4 T4 R% `% f# k" }/ f - (session "step" (1+ (session "step"))))
+ i! T$ Y& o7 X - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|