wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
; v/ N) `2 n2 M- C" D1 h& t) n借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
$ O' Y1 c2 T( D1 b- <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;">;; 定义新的游戏地图* ?* n% _9 w4 K( Q) u- r. e% F$ j
- (def-map "/game/5x5.el" ; 对外开放的URL
5 l/ c$ e5 |2 ?3 ?& G. T! [ - 'tutorial-room-0) ; 默认的入口
* ]: |. t* l; i - 6 \7 f) W- ^6 G; |1 i
- ;;; 游戏大厅3 g! V2 X7 a# H" F& h0 ~
- (def-room living-room
0 o9 t# Q' k, J3 C6 O7 \ - ;; 进入该房间后的提示语
% w& E4 v8 Z" u9 M2 x8 Q* x. }9 } - "1. 教程
1 L5 g3 C0 U6 ^ - 2. 入门(3x3)
T: s& J0 i( D% p8 _3 {7 u - 3. 初级(5x5)4 B, j; R( I: E* E- K7 c, H3 \
- 4. 中级(7x7)2 J) }" G9 @; c" u6 h" O
- 5. 高级(9x9)
2 V: V: @5 V) ~& v% w - 0. 关于作者
1 M$ k$ B% p* r3 C - 请选择1-5开始新游戏:"% ?( n, r: M9 G/ j
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名9 o2 d& v1 d" n3 A9 L/ v
- ("1" tutorial-room-0)
C. m$ C0 k6 \2 z) T9 y) M# ^ - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
; o. |; E/ l/ U6 C' |! u# j - ; 相应的返回也可以为函数,动态返回房间名
$ A2 Q' r1 y% _ - (t living-room)) ; 如果条件为t,为永真4 j) I$ s0 I" r5 E
- 8 z M- ^8 X8 n4 P( c8 K2 x
- ;;; 作者信息! }# Q, p& K! z
- (def-room about-room1 r. x% v% L7 L9 G1 \! h1 d1 R0 |0 L# W6 i
- "作者:redraiment
% A1 a( \ y$ H* Z1 `; n - 微博:http://weibo.com/redraiment
. T) s" L+ Y! a2 c6 H - 有任何建议,欢迎在微博或微信上联系redraiment。
$ [& ^/ O# E2 B0 W; j; x- Y - 请输入任意数字返回游戏大厅。"# i& w0 h+ X5 A2 B& I
- (t living-room))
5 y& z8 \7 V8 J7 d7 w$ q) k
! G: X1 `: p! y) e6 a. G- ;;; 教程
. ~) o. y6 c3 N9 B0 i4 T* R - (defvar *wechat-5x5-tutorial-rooms* 07 Z3 z; _4 g, M& ^* S
- "The number of tutorial rooms")
# s& ~9 y1 Z: h. T' W" y) M
! O- h5 `) Y+ X" }8 W- ;;; 简化教程的定义& l/ o' p- b2 _! t
- (defun string-last-line (content)( a& A( T9 b7 ~" [" ?# T# ?6 O
- "多行内容组成的字符串中的最后一行"
% A( |8 D0 P4 K2 r. ] - (with-temp-buffer
, y5 _1 A2 L* P$ Y - (insert content): `! d" [ i9 g
- (buffer-substring (line-beginning-position)
( _9 g, e9 x$ _; _ I" J) c7 E0 h - (point-max))))
. H( e/ ]" I4 T! u- _
/ w" U/ w {6 D+ a( s3 d- (defun def-tutorial-room (prompt)
5 b0 Y; c8 t$ F( u - "根据提示语自动生成教程房间。
# @0 @5 d* M$ W& Z: |
( r! D: r2 \( I% T! W& ]- 1. 提取最后一行作为问题;
& z4 l! d( Y* [8 q& u - 2. 分析问题,获取期望用户输入的内容;
. f7 L( d$ k" [1 [& S$ l+ m" c - 3. 定义教程房间和重复提问房间。"
+ H4 ~7 u# C8 b$ D2 f' K. K$ {; ?9 G - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))0 p$ d. _3 h+ C. _2 G
- (repeat-room (concat room-name "-repeat"))
# ]+ H% \" a4 X6 I, e7 w - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
1 V2 J% c# e' B1 y1 Z - (question (string-last-line prompt))
& ?) v. S0 C" [% u5 v1 [( z" y2 T - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
$ Q/ j* y) r. f7 }0 K" |4 E! q - (match-string 1 question)))3 Y3 F. W7 a8 ?, {/ A5 F. N
- (doors (if except( t7 _% d2 |) t7 K( A9 f0 B. D
- `((,except ,(intern next-room))
. q: ~ d; j I8 y - ("q" living-room), u8 K8 Y. a- O
- ("Q" living-room)
+ _$ W( f8 `9 R' s - (t ,(intern repeat-room)))% s3 w( T9 y1 y2 r+ M
- '((t living-room)))))- ]+ J: w1 t) G9 O! n1 n
- (def-room-raw (intern room-name) prompt doors)0 n+ I1 X0 f; r% V- R
- (def-room-raw (intern repeat-room) question doors)))8 j0 f) A- f8 @/ s( Z/ x
! D5 q5 k2 [9 I5 k- (defun def-tutorial (&rest prompts)
6 u* e1 ?2 T% e1 l9 ?! ? - "批量生成教程房间。"; r# Q" {1 y [/ t6 |
- (dolist (prompt prompts)$ X1 T- a* U y9 r/ k' I* y
- (def-tutorial-room prompt)))
* t' j; z3 m/ ?$ s
+ ?) p6 O2 }4 u- W7 ~- (def-tutorial
g4 k$ [2 X7 @" A - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
" P0 O5 m# N1 |+ Z" r E: w - 1. 教程$ R. T3 [% K7 U& B V
- 2. 入门(3x3)
, [. ]' I; B. a( N Y - 3. 初级(5x5)
2 M: X& A- u; F7 c$ N9 ]) s - 4. 中级(7x7)8 P! \! x! W; S2 u
- 5. 高级(9x9)
7 P, a \6 m. ~$ w# O9 L& ]% y - 0. 关于作者
% [6 `9 S i8 L4 \+ i - 请选择1-5开始新游戏:) p( K: n9 |7 c4 x7 C' e, Y
- 您现在正在游戏大厅里。
2 V! s" g x$ { P1 _) ]8 N - 请输入“2”进入入门级房间"( e2 F2 _+ F5 E5 U! D# l. {9 A l
- " ①②③$ _$ e/ A; z4 ]( J8 O$ j) R4 `0 q
- 1 ■
1 r/ v. ]3 H) X* M5 a - 2■■■
9 p# W \! p- b6 _4 S" c% O - 3 ■ 2 I8 {0 P% C- J
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!6 J5 z6 Z* G3 s. _5 W
- 请输入“22”来关闭第2行第2列的窗户。"% e; N% J) y W2 r* b! x$ s2 N
- " ①②③
- g. t7 e. D5 X& A0 l3 R - 1
& P$ @4 l0 l% f - 2
' t. K9 o8 F, v, x - 3 % b% h) P% \0 |: {/ T
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
* n+ M U; M+ K. q" W - 请输入“11”,试着开启左上角的窗户。"
1 }. i4 k7 y: f* o2 P7 W - " ①②③
" H) J/ @- a4 G1 R- ~ - 1■■ ) L" K1 h. ~8 n% w$ r: _" E% `
- 2■ ) S6 R: n3 n* j
- 3
! g1 b# i( v. p0 ~5 R3 @ - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。: V/ `6 {2 m; W4 `0 f) }5 Y0 c: B
- 请输入“13”开启右上角的窗户。"
' w0 O; a6 D- p4 P8 {: q4 k - " ①②③7 h8 ~5 N) q a
- 1■ ■
0 e( v$ I1 ~3 E G - 2■ ■
; H0 p6 r+ f+ G$ w - 3 & I# i; V/ B5 s! p
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。8 R2 G+ J6 k: Z
- 请输入“31”开启左下角的窗户。"4 F( n5 _9 `) Y
- " ①②③
7 K: n# R! J7 s; a4 ^ - 1■ ■
$ o1 p5 d9 q f! S2 ? - 2 ■
' J' A; d. _0 ^; {7 D - 3■■ % t4 Y9 x' ]( r! M! j, m; u+ n
- 此时,总共有5扇窗户被开启了。
: D: V3 F# `5 _6 g8 p9 z& b7 G - 请输入“33”开启右下角的窗户。"
# m9 `; A7 f9 X4 }) _) L; U7 \ - " ①②③; Q1 [5 m* P4 V; ^2 M9 Z8 p7 y
- 1■ ■
# l: M j- M6 U* d Q+ _& R, H - 2
4 I0 @" f+ d. x6 i) a0 m# x - 3■ ■+ c8 z+ q8 s0 F9 `
- 现在,只有四个角落的窗户被打开。
9 J" D5 T! i. X7 ` - 请输入“22”完成最后一击!"
9 \9 f% e e" G x7 p: B - " ①②③
- I& r+ s+ q* [2 Q6 y3 b. K& h8 U+ X - 1■■■
& j3 F5 P( o! o0 _, Z - 2■■■
6 K6 i9 D$ z/ I - 3■■■
9 a* N) y: {- K4 f7 X" k- D1 a - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")' K9 ^1 @% L8 D$ y8 S+ \) \
- 4 B' X* ~2 \9 g6 \' i5 T. r
- ;;; 棋盘$ b) r, _9 i( o& `( j
- (defconst *wechat-5x5-white-chess* 12288
5 a. D& o* T# M% z; k - " ")
7 J# x' u0 l# d2 ~ M; W( N - 1 J: f* i9 |- ]4 R
- (defconst *wechat-5x5-black-chess* 9632
5 c5 U) u1 o0 a7 V0 j - "■"). _! t" m1 m* W6 q: ^, n$ i
- ; c, m$ w; [5 R }8 ~
- (defmacro with-board (&rest body). q; \, L6 u6 P8 X* {, [( X3 Q: i9 u
- `(with-temp-buffer
! C/ u; S! e' L" w! n( S) A i/ h - (unwind-protect
7 @1 N1 z3 z% U6 h# T5 n - (progn( g" s" d7 z/ a
- (if (session "board")$ Q% T f3 q- Q+ F) g
- (insert (session "board"))); D' K9 v4 N% J& d, {5 N7 L
- ,@body)- A& a/ Y) ?& s) Y$ a4 L; \$ W
- (session "board" (buffer-string)))))
: a' i0 b/ Q' L% G5 g2 |
/ U, s& T8 ?8 V% x# C7 ^: Y- (defun board-init (size)9 C- j+ W, m! r" m$ F0 A
- (session "size" size)
( g) _) r7 q' G! X4 S: w6 g% _" y - (session "step" 0)8 t8 e! U8 @: J- c7 e9 q4 a
- (erase-buffer)2 |# S% ^% t: u5 a5 f5 G
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨")). s2 p+ t% ~2 Q8 ~
- (dotimes (row size)
2 r2 Y/ ~; b7 d2 {5 C/ U& Q - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*))))). U# j5 U. K/ G7 [4 W+ G& ^* w
- % s) D& O/ K, e; @, E
- (defun board-contains-p (y x)5 D6 \; a1 R4 O1 _. h
- (let ((size (session "size")))
' _: m# c2 S" H: T2 n - (and (<= 1 y) (<= y size)
( a3 S) A8 u9 r7 h% ~4 ] - (<= 1 x) (<= x size))))9 s- |9 L$ h( {/ b; ~. P2 S5 w
- . f/ Q: U) r. k; w' Q: R# U6 I
- (defun board-toggle (y x)
4 I; A7 o4 z) s6 E/ p' n @ - (when (board-contains-p y x)/ w" Y$ t7 t- `3 T% i7 x/ j
- (goto-line (1+ y))2 v) x- h- o7 T0 q5 @% M- W
- (beginning-of-line)
: b/ p5 Y( Y% O. i/ ~" @ - (forward-char x)& O C) v$ z4 G5 x
- (insert (if (= *wechat-5x5-white-chess* (following-char))
" d8 M4 X. j2 {% n - *wechat-5x5-black-chess*
0 q: V% f2 x* n - *wechat-5x5-white-chess*))
* O" ^4 c; m5 a2 h0 h9 ]4 K7 } - (delete-char 1)))
4 R! P6 V+ B2 e9 P2 l- I - ; @9 V1 X% j5 e: E
- (defun board-put (y x)
) A; D* p' A% ^! i. U - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))) r! i# m+ L5 I+ k* y' k# G0 Y
- (board-toggle (+ y (first dir))
. z. T( ?. M& @ {. k - (+ x (second dir)))))
. a9 V' F/ @* s4 H - 3 ]$ J- v5 w0 B, G
- (defun game-over-p ()
+ ~3 L& A7 ]# I' ]" t! _2 h - (beginning-of-buffer)
$ Y# Y. F: ^+ N3 Q: _* r - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))" O L$ h7 n( R; Y8 v* p/ d
- - @9 U1 D+ d- _% g' n
- (defun board-show ()' O9 |4 ]$ B( X( c# @) j- V
- (with-board+ L$ V9 J( B/ W' _; V
- (concat (buffer-string)1 I' B( v6 O! w
- (if (game-over-p)
/ `# S9 P& d4 X$ j - (format "共%d步,输入任意内容返回大厅" (session "step"))& }1 Z+ b$ X* O2 y4 ~9 `
- (format "第%d步" (1+ (session "step")))))))7 X* O+ s8 Z' A: F, \ W( z$ {1 d
6 h1 d: U$ k4 B9 B- (defun board-position-parse (cmd)% s& S+ s7 u7 Q2 Q" \& ~$ D
- (if (= (length cmd) 2)/ A- h. G# i. I
- (list (string-to-int (substring cmd 0 1))% f- \, P3 Y1 m4 Q$ J
- (string-to-int (substring cmd 1 2)))/ [; T$ S. R- q$ \& Y& T
- '(0 0)))5 c- Q: W4 x1 \9 M* p# N% i
- ; Z, F! f4 A8 k: D0 ~
- ;;; 游戏房间
/ A& ]- w( P/ v$ S. L/ _ - (defun game-room-init (cmd)# W$ Q& O) p/ F# [* u) p9 u
- (let* ((middle (string-to-int cmd))
* X! E7 {' Z% A7 T' I; M5 ? - (size (1- (* 2 middle))))1 |/ V. t1 Z# L& f# ], e
- (with-board
, M& p" @, W5 i3 v- O/ s2 `6 d - (board-init size)- F% Y( o, f% V# Q
- (board-put middle middle)))
" q' P' O A% d9 v - 'game-room)! J! n. e: p8 E7 m* \$ }
% X, U% i/ j ]- |1 q7 b- j- (def-room game-room
( r4 z, H/ }3 K; I. B6 r - #'board-show
5 B! e6 v, E4 ?+ [, W! ` - (t (lambda (cmd)
, c; K8 J$ O) S! Q) [( k/ a - (with-board4 K7 `# c$ V# F2 W) P' T% @# @
- (if (game-over-p): {0 w& U* |- u- L v5 k
- 'living-room2 d3 |, C" R$ L V9 m3 U4 U
- (destructuring-bind (y x) (board-position-parse cmd)
( G) |, W% Z0 L' ]# c - (when (board-contains-p y x)
2 t/ A3 P e) w5 D' d6 _ - (board-toggle y x)4 S9 Y' ?4 [9 G4 f7 E6 W3 ?
- (session "step" (1+ (session "step"))))6 w4 T$ K1 d: P! b+ z& j
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|