wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。: k! o( F+ s0 `2 u
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
+ O4 @8 A. V# N- T' 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;">;; 定义新的游戏地图1 z6 l6 k, f, L# d
- (def-map "/game/5x5.el" ; 对外开放的URL" X; d" D4 ?& @ w" I$ ~/ e
- 'tutorial-room-0) ; 默认的入口
* `; V% l' A9 [
8 S2 v9 a9 @, y+ w8 |/ P( t- ;;; 游戏大厅
* m% }2 i% Q( S) M8 x - (def-room living-room. V1 K, K1 g+ a* k. i4 R
- ;; 进入该房间后的提示语
: J+ F& i; b# D. y0 _; i4 [' l. f - "1. 教程: M7 Y( J6 o8 g( W- ~% ?
- 2. 入门(3x3)
1 f7 L% b5 V: w - 3. 初级(5x5)
% K' b3 e. R2 G7 v0 x - 4. 中级(7x7)9 k( {' T7 p O$ f
- 5. 高级(9x9). t$ b4 c: M9 L5 a6 L
- 0. 关于作者2 `- m* A& Y7 t2 G, g( H, ~! H7 q
- 请选择1-5开始新游戏:"
; \# X/ }+ u3 q - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
: B. i1 h. D0 d* i$ S - ("1" tutorial-room-0)3 f: `* |7 U1 M- s0 X! J0 x
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配4 Y. g# C' M6 a3 ]; D# ?1 |, S
- ; 相应的返回也可以为函数,动态返回房间名( m! `3 b) ?5 p3 ]( M& B' H/ J
- (t living-room)) ; 如果条件为t,为永真1 R8 @# K3 T7 `) q5 U- r
- 3 t& a: U1 F- u+ \3 Y* _
- ;;; 作者信息
+ R" c4 n5 }* X6 ~2 V - (def-room about-room S7 b1 g; e" c# ~$ [) Q
- "作者:redraiment9 E y7 b1 v3 Y
- 微博:http://weibo.com/redraiment
6 M# }" l2 n& l7 M3 Q - 有任何建议,欢迎在微博或微信上联系redraiment。 s7 u& S% X6 C, L' `7 Y
- 请输入任意数字返回游戏大厅。"8 c5 M6 W! S; j5 q8 o! \$ h
- (t living-room))
' U/ D& \; T, V# ?$ T9 o% g
4 {5 ?( |3 A1 R' |' o. E- ;;; 教程
& Q1 }+ p3 k. U - (defvar *wechat-5x5-tutorial-rooms* 04 p/ a1 J, @, I( Z
- "The number of tutorial rooms")
b1 r# p" \0 ]7 v" C d - . }- h1 ~+ s, P6 o+ Y9 ?3 C
- ;;; 简化教程的定义
" f" B+ W$ v) o9 F - (defun string-last-line (content)
4 v% e- b& S) l1 ]) g% U7 Q) [ - "多行内容组成的字符串中的最后一行"
* Q1 i: V- r( T - (with-temp-buffer8 o: ~! l) Z$ P+ N8 U
- (insert content)
. G" \) d( Y& @% V8 V - (buffer-substring (line-beginning-position) _: f$ A# H3 Z
- (point-max))))
% N1 e8 Q. r1 {6 _- u& V! a6 C# R! \
; o5 Z% @- p7 N$ g d4 t- (defun def-tutorial-room (prompt)
3 A% V# c3 h6 @' x( N, G - "根据提示语自动生成教程房间。
" F. p( z% @5 z# Y* a8 y T) t2 z9 W
4 X E- ]' P5 P- l- 1. 提取最后一行作为问题;
7 y, p2 W. G4 E5 p$ e - 2. 分析问题,获取期望用户输入的内容;
. R5 `3 l2 |- A! ]! T4 p3 M2 J8 T P - 3. 定义教程房间和重复提问房间。"4 R( z& u& v7 C
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))# y4 P! d u G a$ N; t
- (repeat-room (concat room-name "-repeat"))1 ]3 C+ O+ F: ^0 g4 I0 o6 F
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))% x7 @5 m: K4 G* n
- (question (string-last-line prompt))
9 d: h& r5 {0 M: z) C e - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)& a8 ]4 w: q M' E& s
- (match-string 1 question)))
9 [) G% N7 x: a7 W% i - (doors (if except( M3 W8 @( V6 T) Q% d1 i
- `((,except ,(intern next-room))/ s2 u+ V9 Z# x y
- ("q" living-room)
6 I0 e! v6 @2 M/ P8 ? - ("Q" living-room)" D; T* @+ Z9 U3 j& k
- (t ,(intern repeat-room)))
, R" {# m* p, y3 } - '((t living-room))))), n2 K5 a# q6 [8 g+ k# m3 v& a
- (def-room-raw (intern room-name) prompt doors)4 |/ H: E0 A7 g2 e" q
- (def-room-raw (intern repeat-room) question doors)))
$ G2 _! V" F, d
& t1 O& k, t/ n2 O3 C K e- (defun def-tutorial (&rest prompts)7 C+ t6 o1 S2 V3 `5 A! |
- "批量生成教程房间。"9 t% w/ X: t& ] m* E8 f4 M
- (dolist (prompt prompts)5 t0 N2 P7 w7 ?) I$ V/ a
- (def-tutorial-room prompt))). g9 ]* @' B5 ^6 I5 U
3 I' l3 Q1 ?; Y7 s- h- O- (def-tutorial; o$ j$ y2 o2 {3 k8 Y; }$ V
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
5 u" y4 d/ R4 Z# [9 H2 Y ` - 1. 教程
9 y" p+ U6 A2 a* O - 2. 入门(3x3)
% k& P0 l; B/ |% M - 3. 初级(5x5)
( X. }3 y k! e( G - 4. 中级(7x7)3 n( T7 O7 l. u5 F! F* x
- 5. 高级(9x9)9 ~* h2 n4 x: X5 a: z- G+ E" Q4 a" u
- 0. 关于作者
* c5 q9 V) y$ u3 K6 l' o( Z - 请选择1-5开始新游戏:/ Z' j4 s& R6 I+ O6 R! @; x
- 您现在正在游戏大厅里。" E2 A) o2 A$ w; [ t
- 请输入“2”进入入门级房间"
; E1 v) n) p! W3 {' p - " ①②③+ w! v6 V, |0 I) G& i Q
- 1 ■
* C& L% |. c% @! q - 2■■■/ B! ]/ Y, @( J9 }) M( Z6 [
- 3 ■ $ e) n1 p$ W5 _4 n3 {) h
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!0 _1 J( c* P5 ]
- 请输入“22”来关闭第2行第2列的窗户。"
$ I& Z/ M2 p+ w/ d3 ^$ Y3 [ - " ①②③
/ ]: Y1 D/ w# q. F! f6 B - 1 6 y" J. T' A$ q
- 2
# e) v' t- [2 B. D0 W2 k - 3 . p( ]. t j7 f& V( a' b# W7 T3 k
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。, S3 l1 h1 N. t6 L4 G5 y9 _+ F$ |
- 请输入“11”,试着开启左上角的窗户。"
% x/ P. s9 C- k3 W0 |7 t - " ①②③3 ?) _& I3 x0 @* H: {5 A. |! U6 s
- 1■■
2 e# R! i* e* e3 e1 Y" h8 f- I. L - 2■ 5 D5 r* E w2 j7 c
- 3
. t3 p% C" O1 Z' W0 L. C - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
- p1 [: ^# ?' W- V2 @4 N' G( f: ~ - 请输入“13”开启右上角的窗户。"
3 ~7 ^6 s3 f8 }& i+ i; j- Q - " ①②③4 Z( D1 C$ k! f0 `9 @& _4 z1 \2 o
- 1■ ■6 Y) Z$ R! s1 X) s
- 2■ ■( {( h+ x+ T6 U# x
- 3 8 ~) f2 B2 D( s5 m* S8 Y2 ^
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
N+ P4 U. W9 u- }, {3 }2 @: p: L - 请输入“31”开启左下角的窗户。"4 w+ r; V! ?' ^* p! n# l
- " ①②③, ~; g1 H; t; T/ C* }+ o
- 1■ ■
; ?1 M8 X* ]9 j3 G# H- S6 ? - 2 ■
+ b( t* n |! V" k4 O4 S- i/ g5 | - 3■■ & \- P: U4 _* \+ V/ R2 X
- 此时,总共有5扇窗户被开启了。9 `0 Y2 P. I( |& M
- 请输入“33”开启右下角的窗户。"
( F3 @! M: ~! \5 s% l7 v - " ①②③
% Y: [7 k$ E0 G - 1■ ■, e' j W, O! Z' K
- 2 ) S: p, t$ R; t6 Y
- 3■ ■
& W: M% k0 j) M) y - 现在,只有四个角落的窗户被打开。
! p/ S" y3 W9 D& j+ s2 M3 l' e - 请输入“22”完成最后一击!"1 E% k! J# M$ X+ q& N; I; n+ l
- " ①②③
0 E$ Z8 x, J, \' K3 `& { - 1■■■# N* @ q$ g2 J1 c' T
- 2■■■* b8 @& V- M$ x/ T2 o; ~% e+ t5 u
- 3■■■
) k6 s. K, c6 K e: s- O - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
u# b1 g: T5 E& ^ y4 s
8 o: [3 p3 q: b4 W9 `: L5 |& N- ;;; 棋盘+ f) j) m; H! W; H; o3 t
- (defconst *wechat-5x5-white-chess* 12288/ b; N, p/ @& k4 V& V" o
- " ")4 @4 C9 d5 U0 y& _3 }3 H% o9 X
- L6 L6 g: U5 y4 S4 N# Q3 t5 u
- (defconst *wechat-5x5-black-chess* 96323 Q. ?0 `' l3 ~+ {6 ~7 _
- "■")
9 a0 @, y6 S( B5 {7 K ? - 3 ^" P0 o; z1 k( Z$ T$ S
- (defmacro with-board (&rest body)
5 @0 C; b8 ]" x - `(with-temp-buffer" \6 I1 s2 ~" q4 E# m2 [
- (unwind-protect9 A+ m' {. V, ]+ S$ z5 {
- (progn+ |: M0 R0 ?0 ?
- (if (session "board")
# J! q. h4 N9 ]. L8 Q6 U9 G8 z+ [ - (insert (session "board")))
3 c) q! p. F- |$ s - ,@body)
! L& i1 @9 ^ z6 s# D. W, Y - (session "board" (buffer-string)))))
% ?* e8 G! j2 d4 u' R
1 k+ ?& H' o. t- (defun board-init (size)
5 F2 B8 _" J/ [% r' t/ P( Q& l - (session "size" size)
$ h' V3 T5 H% X" N' S$ @2 g; j( B - (session "step" 0)4 `2 i/ ~$ S9 h9 H7 e
- (erase-buffer). k" P+ z# K7 D3 g* C2 x! A
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))4 l7 M- t2 C4 F4 [' e
- (dotimes (row size)5 T: n* J( c' W( O& q' Q- H8 r
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))1 O8 ]0 d, Y2 G H/ j
- * c; Z& A2 w! C" B, [) \
- (defun board-contains-p (y x), K) \) o* @% ~
- (let ((size (session "size")))/ q/ y* I r4 s! N* P }
- (and (<= 1 y) (<= y size)' m) i% `2 u* B5 _0 ~) n" V
- (<= 1 x) (<= x size))))
- E! G( L9 j' g( v& o - 4 |1 K. C6 q! y2 e0 C4 M _- Q
- (defun board-toggle (y x) f# @9 Y2 U4 u: r, N4 w% @" I
- (when (board-contains-p y x)
) T: [ t6 |7 A/ J" P - (goto-line (1+ y)). L7 K _+ a N' d3 e# S& I
- (beginning-of-line)9 e9 L+ ]( l5 T( w+ v
- (forward-char x)7 { e3 B b9 G" M. p
- (insert (if (= *wechat-5x5-white-chess* (following-char))
7 }. D6 Z3 S' i& K- c1 B8 e D - *wechat-5x5-black-chess*
8 a; Z( z2 r# X - *wechat-5x5-white-chess*))8 q; L1 V, b1 ^5 q
- (delete-char 1)))
! n& [2 }' T2 J( \* c% Z5 l5 p, K - $ C* @2 V* }$ s) o/ H
- (defun board-put (y x)$ V$ U' Z2 g. b4 b! A1 \* s7 T
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
: O' f7 b3 V5 _; [9 k3 ` - (board-toggle (+ y (first dir))
* G: t; o& g, N: V( Z3 I6 t( k - (+ x (second dir))))): x K5 Y J+ d$ m2 ]
- ; k# o. }9 x+ C, z1 l3 ^) X" ?5 ]
- (defun game-over-p ()" I" C# s, m' B4 K
- (beginning-of-buffer)2 g- O% o; O. V% G+ T% z& I
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
- P8 Q7 Y8 q, w6 C* Z1 m3 H% d! e - * [7 B1 G, L' u- _4 v, B) J
- (defun board-show ()
: [3 F- f* K- M) V. f' h2 g - (with-board
" p$ p" @7 s. Y4 n: Y - (concat (buffer-string)$ t: S: e% U! q. I
- (if (game-over-p). s! F0 T" l$ M. P7 ?. ?& x
- (format "共%d步,输入任意内容返回大厅" (session "step"))
8 R" V; d; v" c$ U. }& O4 W - (format "第%d步" (1+ (session "step")))))))
" v: o% s( w+ d1 A# _: b - ; @8 p9 z3 O# Z# F+ X
- (defun board-position-parse (cmd)
% s" C. l2 l" g) ^$ f - (if (= (length cmd) 2)- }6 p1 j. j" { N" Q, i4 N
- (list (string-to-int (substring cmd 0 1))
7 J3 S* e( H6 t- ? - (string-to-int (substring cmd 1 2)))) d; X# T# D8 n7 p! N3 w9 w
- '(0 0)))8 A N& [8 F$ Y( z
, B" B0 l* h7 Y X' l- ;;; 游戏房间
! J; E. g b2 u: d" [+ `4 z - (defun game-room-init (cmd)+ \# _. S4 D+ `' o$ J
- (let* ((middle (string-to-int cmd)); d4 A8 S' X: O3 Z* V! c* F* `. q
- (size (1- (* 2 middle))))
- F2 D1 B$ Z+ ^. P - (with-board
; X i' z! F, A+ a' ^ - (board-init size)
8 f3 C. S* M0 s' Z: P& |; j - (board-put middle middle)))
8 P: [2 q+ ^4 D - 'game-room)8 t p2 t7 ?. M |# ~; i2 f
0 n& C% H' D& S3 K# ~ w- (def-room game-room
/ X' e0 }, _4 v. J4 k" C9 \& s - #'board-show
& E! Y7 c: Z# {* [8 g# y - (t (lambda (cmd)! O/ Q' ^$ Q" k) {! J
- (with-board. j4 E% a+ F* `
- (if (game-over-p)
) M+ l5 S8 }; I. U) z - 'living-room
* c, A0 g+ n) J8 y) n% | - (destructuring-bind (y x) (board-position-parse cmd)
9 N9 Y) N/ h6 P i9 U* [7 H - (when (board-contains-p y x)" ]: S* V, x; q
- (board-toggle y x)
3 u2 x- F9 b* q! V3 G, P& ]8 R - (session "step" (1+ (session "step"))))) U$ J% B8 r2 D! i# O2 N! Y2 z
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|