wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
$ ? t$ G# ?6 \5 t" V9 K借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
1 N/ ?# C L' N* m, 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;">;; 定义新的游戏地图
8 p$ t: f% T4 `! _2 w s1 ^ - (def-map "/game/5x5.el" ; 对外开放的URL; o/ {9 _3 H9 ]1 m$ m! ~" e
- 'tutorial-room-0) ; 默认的入口
7 w! r; w0 q- g - . ~6 B {9 y( ~0 v4 z7 x; f# F3 R
- ;;; 游戏大厅
2 j6 u+ [9 A* }5 a, S - (def-room living-room
% H! v7 f9 _0 [3 J/ N. l3 y - ;; 进入该房间后的提示语 h# I! `, k9 B9 V- P F
- "1. 教程
# |2 S8 I" ]3 ]* b$ \ - 2. 入门(3x3)! @9 k, R* `' f1 R3 `5 w# e
- 3. 初级(5x5)) w9 y* ~3 J4 f1 m+ x3 x2 O
- 4. 中级(7x7)
" O* } c7 L+ H8 K% h: D$ }. }" ? - 5. 高级(9x9)
- E/ r# U; Z( ?# Y4 j7 q" g) @8 K - 0. 关于作者' x0 e8 d( g& d' D# o o6 w
- 请选择1-5开始新游戏:". o; J+ _9 ?& G( y9 ]' X0 T2 y8 E( ^9 N
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名9 R9 b7 Z6 u3 I) d( A* s' X s7 p. Y
- ("1" tutorial-room-0). ]% m9 A5 J- O9 o: B% g
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
: B6 q; u, P- [, P8 U0 } - ; 相应的返回也可以为函数,动态返回房间名
/ F3 Q' f6 _& S1 K6 J3 J- _6 ` - (t living-room)) ; 如果条件为t,为永真
' O& J. l7 i; a3 K* D - R! I3 a# s9 U9 T
- ;;; 作者信息4 E0 M1 J- o9 H! u! b
- (def-room about-room' T% r0 h2 g M' |* K, f
- "作者:redraiment4 W' V0 h- L @' \6 t5 O/ m+ }
- 微博:http://weibo.com/redraiment
8 h, h9 T' ^7 F. w - 有任何建议,欢迎在微博或微信上联系redraiment。7 w( }3 K: M T6 B
- 请输入任意数字返回游戏大厅。"7 k% |) ]1 s3 d: H0 N/ j
- (t living-room))* ^+ i: g( ?9 T L1 }
- 7 n; y Z @0 n
- ;;; 教程0 }/ G% w1 o |4 G
- (defvar *wechat-5x5-tutorial-rooms* 0
. J3 r! z8 ]4 Y/ e5 C - "The number of tutorial rooms")
; w1 ^$ t. u# @" _* G: N4 i5 _# k! X - 4 {3 s" G s! R; [- C
- ;;; 简化教程的定义$ i# x5 h% r- S6 q( ]6 d
- (defun string-last-line (content)
. X8 A8 W7 [6 |6 [" p - "多行内容组成的字符串中的最后一行"7 j0 ?3 D5 L) n, d4 o% n
- (with-temp-buffer! N f! V; |/ t1 \1 D2 }5 K* z; C, O
- (insert content)# W9 @1 R+ l* d- l
- (buffer-substring (line-beginning-position)) ^) r3 S- ?" U. g
- (point-max)))), U0 H% S0 G+ G
9 a$ c, n! f% Q; I! H8 g- X- (defun def-tutorial-room (prompt)
& w5 Y% J$ [. n4 ]1 t6 E1 | - "根据提示语自动生成教程房间。) `. l( b7 Y" ~
- 1 i3 A( D1 B' W1 a7 w; k1 l
- 1. 提取最后一行作为问题;9 w- ~; z N3 }3 Z+ L6 p2 l7 x5 o- D$ S8 n
- 2. 分析问题,获取期望用户输入的内容;
% o. J& k9 l: G7 g - 3. 定义教程房间和重复提问房间。", i& g2 d5 r# r
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
9 E- d( O. A! ^6 d1 s" w - (repeat-room (concat room-name "-repeat"))
3 L. y) }' A4 f" f) v - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
; I* t" K" J/ q2 B; ? - (question (string-last-line prompt))5 V, I1 G# l8 T3 w
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
p. D. [! {: X4 n9 N - (match-string 1 question)))6 n- L$ A- W7 o# {8 e2 `% [
- (doors (if except
. m4 o6 U/ ~! Y; H0 d+ _6 V - `((,except ,(intern next-room))
4 N; m9 z9 Z* \5 C. s - ("q" living-room)
6 I5 E; k, b3 A$ S2 P - ("Q" living-room)8 Q0 C& a z. \
- (t ,(intern repeat-room)))+ s+ |7 n8 J! D. ^- k
- '((t living-room)))))5 m: R! j* q% W9 G& w4 W* y# [6 ]
- (def-room-raw (intern room-name) prompt doors)" h5 |7 \: L' w
- (def-room-raw (intern repeat-room) question doors)))6 G4 [5 g" W1 x+ ^" G1 s2 K
- . O# w" A5 \# i, U$ d
- (defun def-tutorial (&rest prompts)
% Y- K8 }; ~0 _; x0 }7 t3 _; ] - "批量生成教程房间。"
, u) v- k; |8 k4 A, E - (dolist (prompt prompts)
/ y6 [9 m: p( q - (def-tutorial-room prompt))) \9 p: b! r* `, x- p% k5 H( D
- 7 Q) ?! b! ?" A2 g7 j: u4 C
- (def-tutorial* _% w+ T. c8 K1 j1 z- c% t
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。6 d) d3 }% s6 K C l
- 1. 教程% A" i) M( s+ p
- 2. 入门(3x3)
2 o0 Q7 N0 F9 F% a0 U - 3. 初级(5x5)7 T4 `) @+ U5 a6 i# ~ J7 q& ]" T
- 4. 中级(7x7)
" ^; z& L; C- D- q) R/ b% k/ L' c2 Z - 5. 高级(9x9)& v; l4 g/ X& h
- 0. 关于作者
9 ?0 X# Q% R3 L - 请选择1-5开始新游戏:
0 X$ d" O; D. D+ F; l* s$ f. ` - 您现在正在游戏大厅里。
- x3 `1 r( p; |& Q4 X - 请输入“2”进入入门级房间"
6 f& J4 N5 Q9 I$ ? - " ①②③1 C; A- e w7 p. c& b* P
- 1 ■ 1 l2 F5 S7 p2 v" c: T5 ?' j
- 2■■■
) {% G) g8 `) B/ y ~+ I - 3 ■ & S$ y3 Q5 _" _; ^* v
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
) z9 Y& c8 ^, q - 请输入“22”来关闭第2行第2列的窗户。") z* t' v& I+ n% A3 X3 r. ~
- " ①②③
7 [3 X) J& U2 C" L5 g9 }7 \ - 1
; x/ z& D3 L) f" Y2 \& K - 2 4 ^- @" |; ~; h" f9 T' a/ w ^
- 3
# o) s. X; x" R - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。; Q. O" w, B0 f; n
- 请输入“11”,试着开启左上角的窗户。"" a# |. ~" X& [: h* F" H% |6 j
- " ①②③4 s6 V' Y2 D3 v! r+ c0 D+ c
- 1■■ 0 B) W- D0 C& v$ t! ^9 a
- 2■
" q: q# @, f2 j i/ i+ q5 d - 3
* L, b# m4 H; \/ R5 Y9 S - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。$ C9 I5 g& G. v& Q- Y2 V
- 请输入“13”开启右上角的窗户。"
, z/ W/ z8 V8 W3 V- H8 j - " ①②③1 m! B9 P' V' f! y2 K
- 1■ ■8 D0 x0 N, `" {
- 2■ ■
6 s0 [0 u& J3 M - 3
K1 [- u0 G7 P% d5 I - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
* f- h) R+ J4 h0 Z- w, p - 请输入“31”开启左下角的窗户。"& C2 K8 |+ v2 L$ F* \; S* R
- " ①②③. n( \8 U5 K+ s8 ~5 C: A
- 1■ ■: U9 U' {& ]( m) f' l
- 2 ■
; Q7 m C: S# s, C - 3■■ 1 p$ w& K0 A) w) Y) @
- 此时,总共有5扇窗户被开启了。7 J' {6 g1 s4 z7 e& y7 x( j8 C
- 请输入“33”开启右下角的窗户。"' e3 ]! X% N8 k2 [- [" k
- " ①②③
( X. `) d4 X9 e1 [ - 1■ ■
3 x; n0 Z- R. ] - 2
& Q, |) S5 |5 c2 r7 Q$ Y! m - 3■ ■
* a$ u( k+ w" A9 [ A/ | - 现在,只有四个角落的窗户被打开。, b5 C: G/ f- S0 \- {
- 请输入“22”完成最后一击!"
" R3 W0 L" Y- i, o3 U3 x I) s# ? - " ①②③
4 o" o4 e- z; C8 l- ? \8 K& h - 1■■■
; Q0 w3 a" f: h" u - 2■■■. Q' t# B8 V: W9 N
- 3■■■
- X6 }- c3 d' _" ` - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
- X( {3 F7 Q% d: N7 P( ^/ d - ' u& ? w8 u# o, z7 q
- ;;; 棋盘( q$ x9 g/ Y; _! G! R
- (defconst *wechat-5x5-white-chess* 12288! t: l3 E* k* l* d& H ~- {1 \0 S
- " ")/ s n. P; `+ U
- 5 l! J/ d9 P H2 l2 j- e
- (defconst *wechat-5x5-black-chess* 9632
7 `7 ~+ {- f; Y4 @4 w0 m - "■")3 ~) p$ Q, M: C: u' b- U% x
) y. n" u7 g1 K U! u- (defmacro with-board (&rest body)
& R: N$ G# F& Z; Z0 ^5 r. Y - `(with-temp-buffer
% Y- q7 t# m6 |6 P5 `' c! U - (unwind-protect+ K8 ?% l, s* Z6 X# d+ B, k/ q
- (progn3 F6 \- h/ D& B; Q
- (if (session "board")
0 |) w/ F! _- X9 O, D - (insert (session "board"))): u9 Q% D' A- u- X- U
- ,@body)
L6 g; |- o, D. y9 t - (session "board" (buffer-string)))))/ l2 o5 G6 b! d% @+ \
- 3 R/ ^: n5 U; l+ n
- (defun board-init (size)
0 ^8 P& P- g0 J1 c4 K3 v6 X - (session "size" size)' s u1 ]0 @) J5 u4 E. v
- (session "step" 0); n2 f8 p* y3 ~# H- d: B
- (erase-buffer)
; G1 S, n' b W - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
* A4 R3 M. |' O0 L; r0 Y/ H - (dotimes (row size)
6 i' L; N' b) j: s% I" M. D - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
[' h( T1 f2 r" o* ]
% f t% x% K1 V# d- G- (defun board-contains-p (y x)
$ y5 v2 X( {6 b' p$ U; [1 q - (let ((size (session "size")))& l+ z3 V( I4 f6 A, G3 Z
- (and (<= 1 y) (<= y size)
$ H) [! w) [4 w9 z' a, I - (<= 1 x) (<= x size))))
# x* E* x4 v" I. w4 I q$ N
, n' c7 q, @: \. `9 O8 u1 r! Q- (defun board-toggle (y x)
- I6 k! e: ?$ Z9 I1 w+ @6 @2 n: Q) q - (when (board-contains-p y x)* x$ a' K0 G7 V0 |- o* ]- p0 M
- (goto-line (1+ y))/ U) q, D+ n8 n2 Q
- (beginning-of-line)/ _9 X: H4 m: R, [: f" x9 s% `
- (forward-char x)3 ^& ~8 @7 }& }
- (insert (if (= *wechat-5x5-white-chess* (following-char))
4 n: P; T; C6 d( \" U/ l! w' Y - *wechat-5x5-black-chess*) w6 @# R# U/ e
- *wechat-5x5-white-chess*))
B! k7 d! X+ O; @ - (delete-char 1)))
, N7 c! |4 R' v9 _. U! {
1 U( v1 ?2 ?, L" _5 C- (defun board-put (y x)5 t: r5 B; v& n1 r+ i
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
# B4 }7 K3 }: I7 M) b4 D - (board-toggle (+ y (first dir))7 M0 z2 v! B, P O3 B5 l G# s
- (+ x (second dir)))))- Z! n5 \$ l/ a* e* } \
- ' W8 b2 u/ I& x- C8 N& p* X
- (defun game-over-p ()/ s. ?4 | n9 \# E D+ d6 f
- (beginning-of-buffer)/ X( ^; ~: i" S% ~1 P
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
& m3 d( Z7 I2 w/ \
7 l9 o1 E4 R, O4 Y$ V- (defun board-show ()
, ?5 B5 Y( V+ ]5 t$ U8 ` - (with-board8 |2 M) h6 |5 H5 }
- (concat (buffer-string)
4 g- j. Z3 j6 r! Z9 M/ J8 F - (if (game-over-p)
* b7 W! |7 Z2 {3 `5 |7 T8 }& x - (format "共%d步,输入任意内容返回大厅" (session "step"))
- g5 f- Z; L! B& j - (format "第%d步" (1+ (session "step")))))))( s. {/ h4 T4 ~- t9 |( K2 `6 R
- & ?& _/ K* Y" B" z- l
- (defun board-position-parse (cmd)
$ K5 O) |: Z. y% u, E/ T- n6 s - (if (= (length cmd) 2)
8 g1 V: P2 B9 l8 o- j4 N8 ^ - (list (string-to-int (substring cmd 0 1))7 U# ?+ G4 e2 K1 H, c
- (string-to-int (substring cmd 1 2)))
" M( ~: M, R+ g3 o4 E4 S- K% { - '(0 0)))$ Y2 V9 p+ v4 M; [& s) g( K
% Y/ ~" V, v0 @* p8 m* V- ;;; 游戏房间
6 `2 p: `4 V0 M* V2 N- O - (defun game-room-init (cmd)
, z8 J8 g4 o; ~2 g# M - (let* ((middle (string-to-int cmd))( C. B$ M5 `& ~0 M* D
- (size (1- (* 2 middle))))8 Y, W {0 h2 D
- (with-board
3 ?7 O, }% D2 W. P9 X$ ?' D - (board-init size)' U7 e+ a* s }. {- P& f
- (board-put middle middle)))
) V) ~$ a3 r$ f - 'game-room)
. [+ x' v5 q) X5 C- p# j" g$ A$ c W
- {, Z' \$ y! w" k2 H' M- (def-room game-room$ y( M/ s3 K0 y7 q* U
- #'board-show& R6 j. M$ f8 {
- (t (lambda (cmd)
) b6 z; _+ ~# _, H, t' l - (with-board- ~$ X, U. s# E" Q0 s: k/ @7 _
- (if (game-over-p)( {3 a6 l. q$ V Z! `9 C
- 'living-room& S+ T" y0 P# M: j9 O2 o
- (destructuring-bind (y x) (board-position-parse cmd)6 L/ u- l8 N8 W8 h/ Z; @9 K: w
- (when (board-contains-p y x)
8 w2 X. {: J0 _2 X5 q, K - (board-toggle y x)7 }" o% |9 j7 A8 N
- (session "step" (1+ (session "step"))))
% k& G9 @3 T- m% K4 a. h. c6 h - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|