wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
& d5 k2 a( [* S0 u; X G4 q借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- # U5 y6 a2 n) e* Z" }
- <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;">;; 定义新的游戏地图' b! |) g) ~1 J! |
- (def-map "/game/5x5.el" ; 对外开放的URL4 `7 p* e" D3 M0 f+ h
- 'tutorial-room-0) ; 默认的入口
' W/ @2 F- P4 g2 V( }( b
9 f( z1 H; O: M; U) B8 W- ;;; 游戏大厅
# Y' u; `* K; q* z9 s( Y - (def-room living-room
/ C7 B: Y$ x" j0 ]# A - ;; 进入该房间后的提示语
* ^" R+ j$ N+ I$ H! v v& l - "1. 教程
Z! j5 g, R2 d - 2. 入门(3x3)
! h: W! M9 M% P" ~# ?+ f: J - 3. 初级(5x5)
) h$ R$ Q6 w9 q# O: i n* }9 n - 4. 中级(7x7)
4 J! l: K$ r$ q8 D& ~( i - 5. 高级(9x9)
6 e% E- _# ?- S# ? - 0. 关于作者
# D% D: \1 z4 Z" v" s - 请选择1-5开始新游戏:"
; R- V8 Y; Z b0 u$ M* V) c - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名7 a/ B1 l% l+ c" U! X
- ("1" tutorial-room-0) r8 f1 r L. H( Y8 O
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
+ P" f; U+ p: {7 {# q - ; 相应的返回也可以为函数,动态返回房间名% W: h3 f$ W! f: ], y3 T9 j
- (t living-room)) ; 如果条件为t,为永真1 Q/ z0 U) s) m: ? l. {: ]
" u9 h5 u( Z: G0 _0 _1 n9 ~+ N7 h- ;;; 作者信息" Q7 f( L+ A5 O# Z
- (def-room about-room8 r3 [0 F) J4 h7 b: w
- "作者:redraiment
$ y0 t& j! T" G, P$ N. }! N* G# t$ E - 微博:http://weibo.com/redraiment
& _2 j, T' A x$ ? o8 ` - 有任何建议,欢迎在微博或微信上联系redraiment。
6 W D R/ }" |1 ? - 请输入任意数字返回游戏大厅。"
; W5 o: D4 T: F/ I* m! i, C - (t living-room))/ i# k* D ], Z- V4 P$ R/ I) X. `) ^
- 8 O4 @! w. c4 E
- ;;; 教程6 e( F' M, k; x$ }% L0 J9 e
- (defvar *wechat-5x5-tutorial-rooms* 0
# d- U' m- }- q' d - "The number of tutorial rooms")
- U# K5 y1 a% i
. Q; U% F1 @3 Q( n) ^- ;;; 简化教程的定义- |/ A3 u* ?5 @% P: f
- (defun string-last-line (content)
4 t8 A9 g: F% y" b3 u- x6 z - "多行内容组成的字符串中的最后一行": K+ o, T) R' G
- (with-temp-buffer1 @- t) m$ S1 r M
- (insert content)
' N0 }% |$ s1 J7 s - (buffer-substring (line-beginning-position)$ p7 D1 e# w1 B$ a; m$ W
- (point-max)))) W7 B2 h7 p" X' p) Z) a
- % s9 E3 u" s5 L" w* [
- (defun def-tutorial-room (prompt)4 L, m& f' |/ ]; E4 t: y8 R* W2 {8 J
- "根据提示语自动生成教程房间。5 k- b9 N4 m" q6 t- K: ^
- ! S* n+ M' a p+ P7 Q& X
- 1. 提取最后一行作为问题;- |( X8 e* V U( ?1 M8 T
- 2. 分析问题,获取期望用户输入的内容; K/ {$ t5 t# w7 U9 Z( _
- 3. 定义教程房间和重复提问房间。"7 j# C6 c V- ~" a& ?4 ?9 Z! p
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
/ W9 L V- s$ f% u( ? - (repeat-room (concat room-name "-repeat"))/ y- V7 \3 ~5 k4 i
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))0 s' |* l: P! F a
- (question (string-last-line prompt))
) q( g5 @# o8 E0 o# ] - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)& H) i- g% A n- |
- (match-string 1 question)))
1 q9 s/ G+ |5 o4 s. g0 m - (doors (if except- T& c' d% n8 G: p( I
- `((,except ,(intern next-room))
( l8 I2 i: n/ [* k - ("q" living-room)
! q# R w4 U5 F - ("Q" living-room)
, t9 @* \+ ^; d, x( E/ W7 h0 N - (t ,(intern repeat-room)))
* r. d: s8 O" T1 L - '((t living-room)))))
! |+ \$ ]' j T$ T' { - (def-room-raw (intern room-name) prompt doors)( x5 m0 P; [7 R/ X
- (def-room-raw (intern repeat-room) question doors)))" t0 @6 Q: z& j5 A! s7 k. ~
4 t+ l6 p8 u4 A) C) ^1 h- (defun def-tutorial (&rest prompts)
X' k# f! @; s; G+ F) X - "批量生成教程房间。"
4 _" }7 t7 I/ w/ G - (dolist (prompt prompts)) g$ s# m& V, d9 Z4 i5 J r
- (def-tutorial-room prompt)))
y; {$ G" P, n y; ~
& }' p4 i( L: J) b- (def-tutorial; S& {+ \4 _# K; u# e6 S& R
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。$ m6 n! j2 q9 w/ q/ ], M* ?
- 1. 教程4 R8 ~9 X# V6 Q) e
- 2. 入门(3x3)
, I4 _$ x8 B6 u. N, [ - 3. 初级(5x5)
0 P3 t& i+ x2 p& p+ _9 p - 4. 中级(7x7)
; D. ^, {: w4 K8 m - 5. 高级(9x9)0 z9 ~) _2 |2 }' B
- 0. 关于作者
' ~/ E9 D6 @3 Q& s5 n i - 请选择1-5开始新游戏:/ Z5 \) N: _! A9 x/ H% {
- 您现在正在游戏大厅里。
, J7 B$ d' ^2 J - 请输入“2”进入入门级房间"' G. r' X$ r. `( n
- " ①②③ K8 H N a& w& L/ \7 p
- 1 ■ 2 N) r: ^( }4 l# u- w6 x
- 2■■■
( ?+ g* X# |3 t5 V, X - 3 ■
/ X8 P' m# g8 I3 ?2 Q2 | - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
, o- `) F& a; f$ m. g7 J - 请输入“22”来关闭第2行第2列的窗户。"7 J5 \- ~: X A m: E
- " ①②③
) s0 } l! l1 O0 \: r4 i, t - 1 # M9 J9 h* V W: g, m i
- 2
, a# m1 J% k, s1 M - 3 3 h- l% [! x7 L: s: i9 v
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
+ x6 K" j3 v5 Z6 c- C/ u6 [6 H; P - 请输入“11”,试着开启左上角的窗户。". E: K2 F! k( d
- " ①②③% S6 m2 C. X8 `6 b9 ^
- 1■■ 6 i) b/ z; p+ l$ T5 M
- 2■ ) }: _7 `) F6 q2 c0 e/ c/ n/ [ L
- 3
7 }0 W! b. n( `8 y0 \ - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
& V, ]; }7 A1 I; s" J: U - 请输入“13”开启右上角的窗户。"0 Z& {, L r5 V
- " ①②③ B' I0 r# j" S7 C9 X
- 1■ ■
, w- V4 ~& C' V5 R$ e- P - 2■ ■* A2 `) j0 e! t- R% w& _
- 3
2 W. D, {( O1 z$ w" l5 D) ^8 } - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。, Q) v, \+ F% H4 v* D
- 请输入“31”开启左下角的窗户。"- f9 k+ i9 A2 h0 P0 s6 h `1 P
- " ①②③
# q5 K) [) o! j! @& g( A - 1■ ■
0 N$ n3 o4 N2 y) r - 2 ■" |) {. q5 x! C8 h( M# B' P
- 3■■
$ }* e, k3 N" c8 k - 此时,总共有5扇窗户被开启了。( q: G- z( {3 G4 P
- 请输入“33”开启右下角的窗户。"
! T" f/ u, }' u8 L3 l# B/ o3 O - " ①②③. x# r1 K. l8 Y
- 1■ ■7 W" [' K x0 j5 r7 ^- s8 B8 h
- 2 + W* r1 R$ h' r& e6 \
- 3■ ■# Q3 e: c! u+ u) w! S/ c% y b
- 现在,只有四个角落的窗户被打开。
$ m) x% e( Y$ C/ K - 请输入“22”完成最后一击!"9 {/ L; l2 t% r8 }( i4 d
- " ①②③* d& v* U; o+ G9 r6 w' J$ }5 V
- 1■■■ [! n; ?8 a+ T1 [$ n& ~
- 2■■■
8 `3 R* r/ X" J! ^ - 3■■■9 S8 n) ?. O, i" P
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!"): {; Y2 f6 x0 p8 R' e
- 7 l2 ~) M1 ~0 g5 O- S. [, G% d
- ;;; 棋盘
/ ^! S" C$ k- |! E: B: {& \* G - (defconst *wechat-5x5-white-chess* 12288
3 H; l" W) ^! i8 p! t" ? - " ")
9 } N: V; e( D; Z4 ?/ x$ Q9 c - + ^, r* a: N' ?" a" V7 ^$ c
- (defconst *wechat-5x5-black-chess* 9632; ]' H: O4 U! ^( j# e4 g
- "■")/ x) I( v! ?3 ~5 x$ M
0 a. |9 k: q8 B7 E2 X, X8 a- (defmacro with-board (&rest body)
$ g4 @( z+ O2 V j - `(with-temp-buffer
S* n, N# Y2 O% F1 i - (unwind-protect
2 i$ @6 H' q& k7 S# h- M; u+ J - (progn
6 t. r$ p2 u4 K& H( x8 p - (if (session "board")
* g& W* F( j, R( E - (insert (session "board")))3 k$ }5 O9 N- n; E. x, j5 i
- ,@body)
( y4 {' A4 t+ f8 u0 _3 I* w - (session "board" (buffer-string)))))! \4 w" y7 L9 R2 s4 H
- ' P# Z" D8 J2 Q
- (defun board-init (size); M/ }8 g4 |( w0 f% {
- (session "size" size) A% ]6 J+ S: _ |6 {
- (session "step" 0)- u0 K4 V" _% k3 b. v5 g; h" j
- (erase-buffer)
+ u: {, j5 K- E& B - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))9 u+ Y5 F$ c. G5 @! ]! Q2 h
- (dotimes (row size)
4 X$ `. |3 e$ s, w/ i4 l9 ~ - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))8 c5 w' |$ n' i7 k5 G- ]0 M9 ]0 O
' @# ^/ J3 L. j+ j& ]' r2 H- (defun board-contains-p (y x)- ]; a. Z4 R; |" D, R
- (let ((size (session "size")))
1 Y% F, t0 G9 Y: k- }; r$ E; z1 g - (and (<= 1 y) (<= y size)! P2 P+ D+ w, Q2 G# e6 G$ l8 c7 ^
- (<= 1 x) (<= x size))))
+ y9 A* _- s+ Z5 C1 q - : p' J5 m; L# P. t2 z& c# U
- (defun board-toggle (y x)
: H+ g& y# z/ N! p( X6 Z5 k1 N - (when (board-contains-p y x): W2 J. _& Q" Q% \
- (goto-line (1+ y))# M4 z l* D8 n9 P1 o4 n- j
- (beginning-of-line)
6 A' t; w: Q* d: i3 V$ R - (forward-char x)! w" K8 d$ {! ]$ ^# N% c$ [
- (insert (if (= *wechat-5x5-white-chess* (following-char))- I8 C5 X+ `7 _! R4 K, F L- ?3 [$ R
- *wechat-5x5-black-chess*& F8 B& f; h7 B' c. O
- *wechat-5x5-white-chess*))
& e2 V1 S I# l" O, }( w - (delete-char 1)))
& U9 [1 N! w* a- x6 U - : e+ X5 w# s6 H; C4 Q- F
- (defun board-put (y x)! @4 Y a- p- \/ `! [' E @$ a
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
7 @4 P0 B7 a1 X+ q, _ - (board-toggle (+ y (first dir))
! v* g7 t' ^" X8 ~6 q - (+ x (second dir))))), ~& s/ q/ o, u2 ~( b) s1 }+ A
8 n6 Z' b. A6 \. I1 p- (defun game-over-p ()1 P- j) I- t$ Z. \0 F L# ^; \
- (beginning-of-buffer)8 _. W& n& Z# g( `
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))' d% T" W5 C; p( b5 q9 `' b
- 4 d/ q. g% m+ Q* @
- (defun board-show ()% Y$ m1 D4 ^# T
- (with-board8 l7 @4 C% {1 n8 z0 _: H2 q6 \
- (concat (buffer-string)8 ?: [( ?: H# T2 D6 @
- (if (game-over-p)* r+ u- m) B, }3 C" P* a
- (format "共%d步,输入任意内容返回大厅" (session "step"))
_; c/ f' `) Y0 x" M F2 w# D( c - (format "第%d步" (1+ (session "step"))))))) \9 e) R6 s. P
- " z% ~1 K/ w n9 A
- (defun board-position-parse (cmd)
, y4 l2 o7 q( Z" S* V - (if (= (length cmd) 2)3 \ S4 Q# }8 A0 h9 J
- (list (string-to-int (substring cmd 0 1))2 b0 D9 e3 E5 E" j/ P/ A- A# r
- (string-to-int (substring cmd 1 2)))
+ L G9 Z9 d4 h3 v, l' M, n - '(0 0)))2 s) W* B, T7 W
- 6 a/ w" _% z! U1 C$ {
- ;;; 游戏房间
' D* o% T5 o h0 ]- J, A - (defun game-room-init (cmd)) N5 D8 ] L" }) s& ~: T
- (let* ((middle (string-to-int cmd))) T* \" ~9 O# O
- (size (1- (* 2 middle))))8 {3 s, [$ g2 k/ j5 ^. M' F2 A
- (with-board
. R$ z- `( [" {5 A, C - (board-init size)
3 a! o4 j6 y, l. |4 h8 z - (board-put middle middle)))7 [3 T7 E6 c1 O0 D
- 'game-room)7 T! s8 o: U3 D" G8 n
- 0 w% N0 I- B, J# T' ?$ i
- (def-room game-room
- }, ?& `- a% Y- F9 l - #'board-show# R* i* V2 I7 b8 S' X+ Z
- (t (lambda (cmd)9 }: I+ {% Y7 P
- (with-board; o! ~ {' @4 D; V! e# `
- (if (game-over-p), u" e4 \; h% f2 N+ h2 G. e
- 'living-room
; S, {0 M6 I! f, e5 ?9 ^3 h - (destructuring-bind (y x) (board-position-parse cmd)3 z* c# l2 {5 e5 [$ N) Z( Z9 _
- (when (board-contains-p y x)
, P/ I& Z$ }8 g - (board-toggle y x)6 y' @; Y0 c& l% x4 L5 _$ t
- (session "step" (1+ (session "step"))))
/ x' X, ]8 y: N1 D - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|