wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
9 `+ R6 I* |2 @借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- ) r# T7 l" q2 q" I$ H! S% o
- <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;">;; 定义新的游戏地图
, f. J3 W5 q0 c* C - (def-map "/game/5x5.el" ; 对外开放的URL
4 J$ ~/ @1 D! V, F0 ] - 'tutorial-room-0) ; 默认的入口
: v0 K2 l- A+ Y: s7 w+ t
, g( z' N$ J' q( ~) W% B- ;;; 游戏大厅- }8 d3 i) W4 ?9 l
- (def-room living-room( ^5 k, c" g/ ~) j, L. @3 }
- ;; 进入该房间后的提示语
. s; `3 F0 c% n; ?' O1 i - "1. 教程
5 {- S2 K5 Y9 I4 M7 o - 2. 入门(3x3)
# }* ], H, k2 E+ u% H/ n - 3. 初级(5x5)1 j3 H! f* p, P- U+ g, i F' j
- 4. 中级(7x7)% i/ \; B; P0 R; g
- 5. 高级(9x9)
0 n& C" @8 d6 n. l9 m# B - 0. 关于作者( {' _& j3 I/ r) Y0 v
- 请选择1-5开始新游戏:"
/ P9 r) q* z3 f- Z - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
% ^6 l+ Y7 z' e( E: K: U$ ^ - ("1" tutorial-room-0)
' K; k8 @* \& |% l+ Q% H - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配% r/ c+ |4 B* X/ }5 f) J
- ; 相应的返回也可以为函数,动态返回房间名
7 R. R& y/ F+ _( K& a0 C! u& v - (t living-room)) ; 如果条件为t,为永真
2 g1 z g7 o A7 Q$ w( z - 7 w% d& c" A9 W( A) \5 M, U
- ;;; 作者信息
' z- p. B% X& V: _+ W1 w2 {, ? - (def-room about-room s* s' H2 n# G: a
- "作者:redraiment
. e+ E$ W; ]* d2 d: v5 X& F - 微博:http://weibo.com/redraiment# e) \7 l& G4 Q; d) H
- 有任何建议,欢迎在微博或微信上联系redraiment。/ e. Z; h/ l6 K6 p. u5 }9 V
- 请输入任意数字返回游戏大厅。"4 {9 ^9 k# ^; N5 Q
- (t living-room))
* E' N6 n' P% ^6 ^' b% s- P0 u7 u' x. p - ) t& k4 J o5 N3 a4 G% h& n0 @; T
- ;;; 教程6 [" |" L1 J& Q; T& Q7 ]2 ^
- (defvar *wechat-5x5-tutorial-rooms* 0
7 t: S, V% o5 z% O, |0 o. X - "The number of tutorial rooms")
4 ]7 ~) b, K2 x& [7 Q3 X - * t' _. y+ e4 `$ v& N5 _
- ;;; 简化教程的定义6 U; J" }! ]6 p; u. D
- (defun string-last-line (content)9 m, l8 Z. Q+ R# v% A
- "多行内容组成的字符串中的最后一行"/ v. E. ?. [) ?/ I D# K
- (with-temp-buffer, y4 W6 }% r; M( q: q0 u
- (insert content) q+ z! W. L, c! ?
- (buffer-substring (line-beginning-position)
) P8 @+ f) V6 ? [# Y2 f- v8 R/ B3 E - (point-max))))
8 f6 i8 D9 t+ X' R7 |
7 [8 ?& c" e5 p/ |2 s5 s- (defun def-tutorial-room (prompt)/ o" j2 h) T) o( t
- "根据提示语自动生成教程房间。0 l" T% g* k8 t$ j' }; ?
; X+ b2 N2 i9 B! t+ r- 1. 提取最后一行作为问题;
/ r/ b$ \3 R8 b - 2. 分析问题,获取期望用户输入的内容;% W# j5 p, J i# o. ] ]+ g
- 3. 定义教程房间和重复提问房间。"7 q# Z; K4 n' @, M( R
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
! p% v$ i. R- ^; L; E% L* @ - (repeat-room (concat room-name "-repeat"))
" v b% j9 r7 @$ H9 L - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
$ ^" D) @: R$ P7 q$ ]7 X8 ? - (question (string-last-line prompt)): m* B) }4 f+ _, d. K+ V6 B, U. q
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
h0 O1 d6 E/ }8 b1 u - (match-string 1 question)))7 Y4 Y3 c0 O, V. X
- (doors (if except7 f- {, F* [6 {
- `((,except ,(intern next-room))6 E: q/ r; |# M/ V3 M! A$ f* ^
- ("q" living-room)
! [: i' O3 S* r- Z - ("Q" living-room)! p7 `! z. ?; S7 ]3 D T
- (t ,(intern repeat-room)))8 a, g+ r' h' t5 _% W
- '((t living-room)))))/ G- k! v! j% h1 N3 a! N1 k
- (def-room-raw (intern room-name) prompt doors)! m# _ T; G# D1 s# R8 F8 r
- (def-room-raw (intern repeat-room) question doors)))4 f/ |+ V1 j3 J: A, N' ?& |
! w8 z3 h5 L5 N. {7 {2 I6 [- (defun def-tutorial (&rest prompts)
' i, A+ @/ d# a7 f - "批量生成教程房间。"
1 G6 v _+ m# T L, E# I - (dolist (prompt prompts) f+ t- l) G. ?) m
- (def-tutorial-room prompt)))
9 {: N7 S5 l0 T6 _
- r0 K/ w2 W0 d9 R/ |; N- R* W- (def-tutorial
1 j" G- z- z0 C" l) X8 U - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
' H6 `. i0 l8 A7 B' W - 1. 教程
7 P& L+ [8 z6 { - 2. 入门(3x3), \6 [5 n/ L" I& ?; m4 L
- 3. 初级(5x5)8 M# n- b5 H( d3 q- M4 d
- 4. 中级(7x7)
* Y1 s4 D) O# K1 h+ y( A7 S5 F - 5. 高级(9x9)
& V( p1 Q4 t5 s/ Z: ^& W& l: Z - 0. 关于作者) f4 r7 B( X! u6 Z6 O8 C/ K
- 请选择1-5开始新游戏:
! z$ L* r$ ?: x7 `3 G - 您现在正在游戏大厅里。
C( g* l. K$ v& g - 请输入“2”进入入门级房间"" c1 k: S8 Q5 n3 }' W& h
- " ①②③
2 R- |, @9 I ~" O5 f& b - 1 ■ , h8 z: _2 {) l8 O$ ~
- 2■■■: ?+ c6 k6 a* f r0 o5 T3 u
- 3 ■ 9 R" v* s2 e+ a
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
( V0 q) r& N4 ]/ b - 请输入“22”来关闭第2行第2列的窗户。"
* r% I' j- I9 V3 E' W' ` @ - " ①②③2 m7 X9 ]% I: O5 r
- 1 / U y1 [, W0 `% x) \1 P
- 2
* T& k. |. S9 f5 R - 3 , ^/ P5 |+ z l
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。" H( G4 `$ w7 M+ v/ A
- 请输入“11”,试着开启左上角的窗户。"" a0 W$ V9 k) K. ~ k* i
- " ①②③) s. R! q" L5 g; n
- 1■■ + w7 M8 o+ {( I! s2 X
- 2■
+ G' l: @" C2 F7 n7 _ ] - 3 |; }$ W( x$ _0 G
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
+ S3 f0 a) b- w9 F2 ~ - 请输入“13”开启右上角的窗户。"
3 P, |9 L Q6 p8 Z - " ①②③( Y/ ~& j6 g! s, R% b/ `
- 1■ ■
( S8 q" z+ |! r6 B) a+ ^ - 2■ ■5 \) ^8 T! ^/ \) T6 A- N
- 3 : `9 ~. H3 ]2 {' z& f. A, Y
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。8 O' e' a4 _9 o* i
- 请输入“31”开启左下角的窗户。"
6 n8 `8 K5 C& t1 Z - " ①②③
_3 _% F8 z% i+ l/ C- }6 u) V - 1■ ■
, o) h! u( m2 x* ~. d' D - 2 ■
7 J! p+ v- j: g" \ - 3■■
* @3 l8 { K% `& }) Q0 r - 此时,总共有5扇窗户被开启了。
. u* l9 w8 c! P) _- g9 S - 请输入“33”开启右下角的窗户。"
0 `# b* }* L6 }+ W2 O - " ①②③/ t. G/ Z# `3 v! B& v' i2 f
- 1■ ■% [9 ~( j% y- {2 w, }% i, q. s
- 2
7 T+ ^2 x1 ]2 Z- @6 \% R5 i2 ~6 j - 3■ ■
4 h8 x& A1 Z3 K" ^: p& ~ - 现在,只有四个角落的窗户被打开。
6 W( h; V6 ^$ B2 Y7 T: E - 请输入“22”完成最后一击!"
4 H$ y9 i, }+ _* ^ - " ①②③
4 L7 D, A! N1 `5 U( M6 e4 D - 1■■■: ?- E, B1 w# Y+ x4 S$ r+ O
- 2■■■& R! H; I8 }7 s
- 3■■■
; @9 v' S0 e" _! ~; H4 | - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!"). }- c. t) G5 w- z6 E( `9 Z3 _
- . m4 n+ U# m: H, O9 t
- ;;; 棋盘0 R2 z; G8 _- E' D, W" g4 f
- (defconst *wechat-5x5-white-chess* 12288
j% t9 H! J6 m2 C, q7 V% W# S - " ")5 g, J: k8 O$ I, G: L
- ( R2 k. N: B) ~5 A. Z4 p; J( X
- (defconst *wechat-5x5-black-chess* 96325 ~+ _5 v" e/ G0 W) ~
- "■")
/ t2 E8 a. d! q* x5 U - 8 B0 N: y: q/ m
- (defmacro with-board (&rest body) L8 A0 ^1 ]$ P4 R. c* ~' b
- `(with-temp-buffer
" r9 P$ j0 F& Q9 x - (unwind-protect
6 P4 z! V; g; [6 q3 v5 x+ }+ h5 e - (progn
5 R6 b9 c" j. z4 c' w - (if (session "board")
' K- ?( _) k. A5 s - (insert (session "board")))
) z4 ?3 U* V- q5 d2 W8 \0 Q - ,@body)/ ~( s1 Q" _3 W6 y
- (session "board" (buffer-string)))))
1 c6 b1 n0 {) I7 v - 6 N& g7 Y1 E3 f$ M9 n0 R+ h' _
- (defun board-init (size)
5 G( H' l, b3 {- r* y- D: P - (session "size" size)$ F1 ?7 s8 X- o. `! g. s5 ^3 e% v# Z
- (session "step" 0)% h4 Y/ y( i$ Z* J1 ]4 ^! a
- (erase-buffer)% d' ^9 T4 D% h4 K
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))! v; K1 V1 H: L0 m; Y
- (dotimes (row size)9 A1 o' R. u6 z$ V* M$ U/ ?* X
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*))))); W* `5 A6 Z7 y( \
$ f6 G( o/ j3 D @! A7 o4 Q3 I- (defun board-contains-p (y x)
4 K+ V5 L( c, N' E( c - (let ((size (session "size"))): ?2 V: k- \5 {& f' g( v' v
- (and (<= 1 y) (<= y size)
3 H/ i* i, b' ?/ q2 O, g' a6 b: i/ M) ^ - (<= 1 x) (<= x size))))
" o3 L; J, Z4 j" r- n9 ~4 g7 L - + ?3 @4 r/ ^( d \" K
- (defun board-toggle (y x)
( k, {! ?1 L; U6 Y) E - (when (board-contains-p y x) Y1 t" R% u* x; q( T* F/ k3 P
- (goto-line (1+ y))
4 |$ G0 g# ?) V* k0 F - (beginning-of-line)
; E5 i% M- m# l1 s/ X% { - (forward-char x). H4 G8 _0 r8 `
- (insert (if (= *wechat-5x5-white-chess* (following-char))
z* a4 v. f! [: \% f* L7 Z; u - *wechat-5x5-black-chess*: o( F# y9 n% f- x
- *wechat-5x5-white-chess*))
; n, T& c U0 ?$ X2 Y' { - (delete-char 1)))) Z8 A6 T+ _; [4 G" j
- ) g5 ]0 r& U- m7 P/ G, W$ P
- (defun board-put (y x)
. H$ `4 r2 U# {5 o, p - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
H4 ^# I" m/ a; f - (board-toggle (+ y (first dir))
: P6 l' T. t, A9 H4 p - (+ x (second dir)))))
/ o7 n5 a: m9 j: _& w U0 {5 f
# b0 H/ M* Q% U2 @& v- (defun game-over-p ()8 f7 Z" {4 Q! ~& w- ]& z6 i
- (beginning-of-buffer)6 \. R( M& d' y( V
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
7 z: P4 v; z3 w Z% I1 q
0 R l/ [% \. v8 B8 B- (defun board-show ()
$ t" r9 U" K% q2 n. _ - (with-board
; H( ?/ U" g: x' N4 H7 ] - (concat (buffer-string)
6 w: C1 a9 K8 d; R6 E - (if (game-over-p)
8 O* r* P. T1 I r3 O2 c8 i' H - (format "共%d步,输入任意内容返回大厅" (session "step"))
. c: x# u# o' }4 ^0 x5 e - (format "第%d步" (1+ (session "step")))))))' }6 e y+ I" s e1 y
) m w& m1 L/ A" Y8 p" Q0 y- (defun board-position-parse (cmd)1 }7 H3 k& I0 c1 e0 l/ _
- (if (= (length cmd) 2)* v. T ]) D4 o' Y. W
- (list (string-to-int (substring cmd 0 1)). e0 z, y9 q6 b
- (string-to-int (substring cmd 1 2)))$ }" r F5 U5 E
- '(0 0)))4 y9 O$ m: O+ H1 e/ t
- ; l& Y8 A, F' Y5 |4 ]
- ;;; 游戏房间# D% q( x) t! S! `
- (defun game-room-init (cmd)1 J3 [! {/ ^$ u8 Q4 @; J
- (let* ((middle (string-to-int cmd))( E3 v2 U P" h9 J" Z
- (size (1- (* 2 middle))))! o0 `; F T( r7 R o/ @( T
- (with-board
8 u) Q3 R3 t9 S4 R' U - (board-init size)
! h3 O' t/ e1 |' y9 f9 }7 q. Z - (board-put middle middle)))
# S" @4 r- K: \ U8 G - 'game-room)% u) U( D4 {2 n) W: w
; v. v$ Z% l" U% i* R- (def-room game-room) L+ N G" v/ T# w2 E
- #'board-show' t) |; M) s0 i2 B1 w B0 {
- (t (lambda (cmd)
( r5 ?- N# q0 p - (with-board
; K+ I/ H0 Q4 o9 P) {* o - (if (game-over-p)
, D' H: `0 n7 v" n - 'living-room
! Q; I* S, ~; M6 {6 z: o - (destructuring-bind (y x) (board-position-parse cmd)( ]7 ?4 ?" ]2 Q& |
- (when (board-contains-p y x)
* B* a1 R) ?5 X; B - (board-toggle y x)
, _; O+ H' K! I! d4 S& D& A - (session "step" (1+ (session "step"))))8 x- u" C& K% h& ~& _
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|