wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。7 W" c) N( p/ y8 ?& B
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
4 T% G: n6 r1 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;">;; 定义新的游戏地图3 H- u# e9 L' L3 P3 N4 L8 z
- (def-map "/game/5x5.el" ; 对外开放的URL
7 m; N' b! y. Q$ D( j/ p7 N - 'tutorial-room-0) ; 默认的入口( S" G8 a0 Q% C& M
3 p* E& f) y4 n; |! X2 v- ;;; 游戏大厅; D& v3 s6 r1 f$ x- \
- (def-room living-room8 Q# n7 h* m- s+ N" |
- ;; 进入该房间后的提示语
& W( \% w% F, E" Z. F - "1. 教程 ]2 A' G- Z% [! |
- 2. 入门(3x3)1 u5 N; `# Q" ]8 v# C
- 3. 初级(5x5)3 J* {- ?! [6 F, l
- 4. 中级(7x7)& n5 }! q, T* h" p4 q
- 5. 高级(9x9)
, z. k1 E3 S4 P - 0. 关于作者2 [% H3 |! s# J+ f
- 请选择1-5开始新游戏:"
5 ]- w- G: z/ E" P; T+ Y( D* U* i - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名$ y w! Q, t0 s' t$ P. W" q
- ("1" tutorial-room-0)
3 _7 z! t( k& J - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配5 C* F& U2 q, X+ L
- ; 相应的返回也可以为函数,动态返回房间名; ?+ O. ^& L: _$ n6 z6 M
- (t living-room)) ; 如果条件为t,为永真: U3 A, |0 [5 q. t6 x/ b
+ c* A1 d9 `$ e/ v, i- ;;; 作者信息
! z8 N5 t0 B m - (def-room about-room u" p" } k& n* }- k# N
- "作者:redraiment
2 T+ `. l$ L2 N3 M - 微博:http://weibo.com/redraiment
2 q$ `" c+ ?$ _( H - 有任何建议,欢迎在微博或微信上联系redraiment。
$ e+ m$ H4 T; p8 ?6 v: W - 请输入任意数字返回游戏大厅。"
: ~5 L& B. i% _# _9 ] - (t living-room))
& @2 ^3 _* i6 u7 y8 q
; R# Q4 o9 g8 H- [! o. ^: R0 Y0 e- ;;; 教程
2 i. z2 M+ F) j- E3 ^6 H- i - (defvar *wechat-5x5-tutorial-rooms* 0: u P, c1 R/ `% Q8 V0 l
- "The number of tutorial rooms")
% [2 P. D) i) s# i1 B6 ^# R- d5 B
; [ g9 s1 D1 [: {- d# ]% h- ;;; 简化教程的定义
0 K$ t3 W/ O3 @; j+ ` - (defun string-last-line (content)
9 U2 i ]4 R( z" q* D6 `$ U! S - "多行内容组成的字符串中的最后一行"
) Y2 Y- n$ p0 r0 i- t) X6 t: g! Y - (with-temp-buffer* a! q# S- s, ^ @0 `5 [3 y
- (insert content): A9 B' W8 v) E. X' \# r/ s
- (buffer-substring (line-beginning-position)
U' {1 f C( z4 W) G& g - (point-max))))
8 X3 G2 R; j5 V/ P6 J% r$ J - ' _ V5 p" z1 b+ ]) q0 {
- (defun def-tutorial-room (prompt)
% h: ]* ~+ H/ a, r - "根据提示语自动生成教程房间。
& x! |' x: n& b) \
* T- z4 g1 A! D: T( L) J" x5 D0 z f- 1. 提取最后一行作为问题;# F. y% Y( U! V* B, y2 \+ f8 m
- 2. 分析问题,获取期望用户输入的内容;
! q3 O: z8 E- {" S# } - 3. 定义教程房间和重复提问房间。"/ a9 G& m& w2 _* f: {6 v
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))$ i& j* e/ q% q$ {5 K2 A J
- (repeat-room (concat room-name "-repeat"))
i7 W9 {; B" b3 ^2 d6 H - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))7 n& K" v- M# H# w
- (question (string-last-line prompt))
: Y+ ]5 l2 k* W4 B' f! Y3 ? - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question); n0 d! X9 |: w" I- w* I b) O
- (match-string 1 question)))4 C+ b s7 o1 u& k$ N$ m) s8 e8 Q
- (doors (if except% [4 |' t. w) W# {! w4 v
- `((,except ,(intern next-room))2 y' e. j! I/ i- h; v1 @/ z
- ("q" living-room), v7 u: i ?* P' k2 I7 U
- ("Q" living-room)
- d3 i1 O) P3 m. ]- l5 Q - (t ,(intern repeat-room)))7 a' C6 n+ l% D& C5 g) T
- '((t living-room)))))/ h8 c3 Y" I: o% [
- (def-room-raw (intern room-name) prompt doors)# n9 a3 ~8 b( w
- (def-room-raw (intern repeat-room) question doors)))
+ k8 L6 {" t7 f7 B8 f3 F* E i
: H$ O9 z H6 E# }0 d- (defun def-tutorial (&rest prompts)
# x9 H; _ U! l, g2 U - "批量生成教程房间。"
% o6 B h9 M, h - (dolist (prompt prompts)! E9 ~" y9 Z: x) j j; H
- (def-tutorial-room prompt))). r. L( H1 z" ]" z
7 u! n6 N0 Q! J3 s- (def-tutorial
% w5 K: ~: f! N% k! X, h - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。7 t0 f) ~7 Z! L1 t
- 1. 教程
; r8 H* l6 v1 y5 G& E7 [+ ^* ?! d - 2. 入门(3x3)6 e% v. d- Y" j
- 3. 初级(5x5)
- X& f ^, m: n& h8 f - 4. 中级(7x7)% K# G% T* W( f5 u- y/ T
- 5. 高级(9x9)5 _ e# G6 G" w7 F" Q
- 0. 关于作者
4 j9 O. ~4 g0 ]* Z3 y - 请选择1-5开始新游戏:
3 h2 ~( z* e! G+ D3 }& f( @+ N - 您现在正在游戏大厅里。
* M5 g+ l. m; F8 K& t# ~2 H1 r- D8 ? - 请输入“2”进入入门级房间"
# g% e( t: n3 x& \1 R C/ T6 A - " ①②③& e8 n: L" }+ V3 e- s% A
- 1 ■ 7 k9 O1 M) o4 k# }0 d6 ?) \
- 2■■■
% x% l. W& I! n! b6 R - 3 ■ ; F- a6 o& E" T. |$ o; A+ i
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!2 Q2 f6 i4 B' q3 j. i
- 请输入“22”来关闭第2行第2列的窗户。"
. t+ }' d4 ]- ~5 s6 y - " ①②③
+ t, n' W* ?* Q# f7 N; E" F - 1 # y. t; X( m9 q* C/ X/ ~7 I) N9 d
- 2
: S0 v8 v' N5 Z4 ^* c - 3 2 d+ P6 p+ x2 n( m# p( p
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。: u" |9 L1 @6 C, F- s3 |
- 请输入“11”,试着开启左上角的窗户。"
. z4 u2 U: q# U& f - " ①②③
- Q* b$ B3 Y1 G: O" T - 1■■ r' L% |8 c/ q% p
- 2■
& Y8 i- l! T$ M. w$ \& ? - 3 " n$ w/ X( f0 S2 \: A9 j
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
$ B' P6 K0 E4 B3 L8 E/ J - 请输入“13”开启右上角的窗户。"
3 ]5 Q* b& x' Q" v) a& L - " ①②③( m3 `0 T! ?2 e9 v6 y0 C3 B
- 1■ ■
2 S5 c6 |- Z& l/ ^$ W( l - 2■ ■$ [) D% t( A D! [" H
- 3
: m" r# Y/ m; c. X8 R - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。0 m( g+ C; M0 d" \. r6 w6 ^
- 请输入“31”开启左下角的窗户。"
4 A8 \6 d5 O4 Z7 g - " ①②③, u* p/ q5 R h3 o q/ g5 t7 B
- 1■ ■, A0 O, c+ h- s
- 2 ■1 a: S. ^$ {: o0 A" ]
- 3■■ ( N; E! j7 ?, L. k( ]; V; j) e8 J
- 此时,总共有5扇窗户被开启了。
# _" u. v# Z$ ?5 L. k6 H - 请输入“33”开启右下角的窗户。"
; c. l0 m+ i7 K. J9 q - " ①②③
/ A" U. R$ ]" G1 \- n3 j - 1■ ■
) X) R$ K' D' } s - 2 . W9 e& j( }. z3 D, ^. Y ^6 y
- 3■ ■
" i/ M9 r/ E* L9 j$ B0 y5 [ - 现在,只有四个角落的窗户被打开。( `, x/ X9 r% N
- 请输入“22”完成最后一击!": y. ^/ Z" ^( z3 E8 D0 A3 N, h) F
- " ①②③
5 G2 L0 K( Q8 F& D" j( X. { - 1■■■$ F, N6 F" t+ h9 m$ v/ ]
- 2■■■
1 T, S! t3 M L, Z! R1 V - 3■■■
a7 b+ Y, b6 Z# N, K- L6 B - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
& u! U$ S! R3 [( y% E( A) B
0 q6 O, {% j3 N0 m1 @- ;;; 棋盘% p# h7 ?4 _' z7 `. ~+ M* u& {
- (defconst *wechat-5x5-white-chess* 12288* }7 C# N0 \6 E, N. J* u0 M" j% K
- " ")
$ `# |" F- G/ a, l2 H - 6 Z) f8 g. t! g1 v0 Z5 b
- (defconst *wechat-5x5-black-chess* 9632
; P: z0 V( \+ g4 v! @ n - "■")
. F% `3 k" L: q K - S6 W0 x) {& H$ D! x
- (defmacro with-board (&rest body)
- v# e! d/ a' v+ U5 N' i - `(with-temp-buffer( P8 q: l- f9 z( L
- (unwind-protect
% Z; |& m. o" b0 q - (progn. |( G% p0 p' P3 Z
- (if (session "board")! @4 L# y# g- O; {' p
- (insert (session "board")))4 U' c. I! _) D# @7 B! R" M. D2 P
- ,@body)$ f5 n2 I( p% t( q3 c' ?. U6 [
- (session "board" (buffer-string)))))# s3 [& s1 D# v* H! _ E
- / B- _- l# K) l. m& f. k# l# V
- (defun board-init (size)
, L7 S9 M6 e0 ^; Z - (session "size" size)
. [/ h. W7 |: H1 X: A - (session "step" 0)
! |' k- Z% d% y* A - (erase-buffer)
! e! t$ Q2 _) p! v. O - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))* N7 p: M: f% _
- (dotimes (row size)
; Q% ?& |# J' a5 b; p& E - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))8 z7 A- R) c; d/ t% b" ?( J; @) Y
- , o8 S5 \) V( U6 _$ _) x/ {" l0 z Z
- (defun board-contains-p (y x)- I/ C3 ]1 b) p" S& v
- (let ((size (session "size")))
4 z! }0 h( S/ l8 L) d2 _ - (and (<= 1 y) (<= y size)8 L3 p6 S O6 x. j! k
- (<= 1 x) (<= x size))))
& G4 K9 H6 Q9 N4 E3 y4 ~! r - , a9 ~5 A( d# O# ?' ]( H; {
- (defun board-toggle (y x)
7 B& C% f3 T3 T6 y - (when (board-contains-p y x): t& q- h2 {8 H2 W' q A7 D6 R" u$ ]
- (goto-line (1+ y))
8 G% y7 Q3 |5 M2 c# x' R+ _ - (beginning-of-line)) o0 G$ e1 I8 f B B
- (forward-char x)
4 s( r$ `7 L; b( Z& I$ N - (insert (if (= *wechat-5x5-white-chess* (following-char))
M$ D2 A8 _1 U% G/ } - *wechat-5x5-black-chess*
0 S8 c. f3 J& p% Q; j+ l# t7 z2 f - *wechat-5x5-white-chess*))! a7 f9 b! j" ~# ]
- (delete-char 1)))0 n3 M1 q4 H$ ]+ ]
- . A- y7 }& i) s
- (defun board-put (y x)
) M; }. X$ I4 v. v* S3 Q - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
2 ^6 f. k7 i3 M. |# q" F - (board-toggle (+ y (first dir))# V& r* S) A6 ]) W
- (+ x (second dir)))))
" ]/ B/ Y! ^/ {7 O% ? - $ S) e* ^0 p# V
- (defun game-over-p () { d1 k% E- y3 u& h& i
- (beginning-of-buffer)
; F/ j* H [' x2 D5 F9 J/ A - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))5 ~7 R3 r: j; R6 X+ y
- + ^( W; y* U0 ~; w$ m3 J {
- (defun board-show ()
7 H* g9 E9 s# u3 ]( j% \* g& u: j - (with-board6 B# Q! Y8 Q/ z. u
- (concat (buffer-string)
g1 d/ x) X4 Y/ O - (if (game-over-p)
1 ?+ v4 g/ v, _, w# t2 S% t - (format "共%d步,输入任意内容返回大厅" (session "step"))
% ?1 |+ D3 g; e, L) B - (format "第%d步" (1+ (session "step")))))))( H" u4 e, \$ [( c$ c+ n1 R4 z
3 t4 f% b T, F7 ^- (defun board-position-parse (cmd)8 K8 f. ?. Y0 \/ D+ [3 u) a) w
- (if (= (length cmd) 2)8 e$ M. ^% t2 o2 h; |
- (list (string-to-int (substring cmd 0 1))( [/ U- U1 G6 f" N7 L; B w9 L
- (string-to-int (substring cmd 1 2)))# Y- @2 ]0 C L3 ?3 k' P
- '(0 0)))/ C6 b: d8 i5 u: |" A* d
- / F" r* ~- K/ @9 S! {3 k
- ;;; 游戏房间& U6 O) j3 g% E) K0 _
- (defun game-room-init (cmd). Q: b( }3 N/ R0 b
- (let* ((middle (string-to-int cmd))
( A M3 K/ W! D" q - (size (1- (* 2 middle))))
( x; P# [9 A- q5 v3 |- { - (with-board
6 ]" M5 j6 E5 B. ]4 K - (board-init size)
, X' m% c+ u+ U# r# @' O0 `4 D - (board-put middle middle)))
( u; q% n* k( b3 |1 l - 'game-room)
1 u' T! C3 s: H) |
- }: V1 N7 Z* r# [- (def-room game-room
% ]4 W3 g, |6 k4 D5 B7 n7 C - #'board-show/ ]3 } N" O: W+ F; w8 `" r8 c
- (t (lambda (cmd)2 m$ k+ I" l5 {
- (with-board
# V$ C& N7 A! q: q2 h& }) B - (if (game-over-p)
, A) ^$ L; `, @- W* W0 { - 'living-room
. B' x8 D# ^. q. O% u1 m: B- | - (destructuring-bind (y x) (board-position-parse cmd)$ K% @+ C4 K0 D: N/ b
- (when (board-contains-p y x); e2 S) n$ m$ A
- (board-toggle y x)
/ W/ ~% u& C G" |3 C* ?2 d7 v4 G - (session "step" (1+ (session "step"))))
( ~# g3 p k5 @# i0 g - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|