wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
: Q' ]8 s7 u" r+ S- q% f X+ y' G) @0 ^借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- : G+ C. |& C5 k# w! t: ]* C8 P+ C
- <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+ ?' ~+ G0 y. S) X( d
- (def-map "/game/5x5.el" ; 对外开放的URL+ g1 U7 G3 N/ f: h- o. }4 D
- 'tutorial-room-0) ; 默认的入口. `+ P9 a0 |9 V# j; e" X3 b
+ i- t; s3 I! A9 l, ]' {, }- ;;; 游戏大厅
0 ?" T$ k' O3 N5 D2 L) y - (def-room living-room
/ p% W, s' n2 ^ - ;; 进入该房间后的提示语* b% K% E4 |. H6 {1 W
- "1. 教程4 X% T2 o. L' h9 [ F4 @# ~7 c
- 2. 入门(3x3)
8 q) g8 o, u E, u; y6 L7 d - 3. 初级(5x5)
. ]5 i: H4 h- L8 w - 4. 中级(7x7)
6 X9 m7 {" F" O5 w - 5. 高级(9x9)
5 z7 B4 n9 D6 k* Z - 0. 关于作者" U/ v/ H' l3 p {
- 请选择1-5开始新游戏:"! b6 P4 F5 D$ i7 Q4 Y6 c2 U9 a+ |$ q
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名9 Q5 q$ k3 M v7 `8 W8 M
- ("1" tutorial-room-0)
/ ]0 z( |+ `" u; u- [/ C - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
( L; N2 @5 b/ r5 [# ^# m& E1 I - ; 相应的返回也可以为函数,动态返回房间名
5 r4 p- ^* ]: K1 Y' w, _ - (t living-room)) ; 如果条件为t,为永真3 V3 p6 {8 ^' F/ u( x
" [& d8 H N* \, Q0 Z: w* F- ;;; 作者信息
& L0 k+ B2 J6 X9 F1 C) n3 I - (def-room about-room# x5 D3 b; g* V
- "作者:redraiment
' N. m; |/ r: B) ^5 r - 微博:http://weibo.com/redraiment. y( ]& R- [9 l7 Z( z
- 有任何建议,欢迎在微博或微信上联系redraiment。
8 i# W* ?* y7 ^# s9 t# ^ - 请输入任意数字返回游戏大厅。"& x6 ~3 F4 z8 p
- (t living-room))0 F& N* J1 C" t+ E# u3 p
5 |! c; B( V3 J- ;;; 教程( X: d+ A# N; q$ Z3 _! N& g
- (defvar *wechat-5x5-tutorial-rooms* 0
: O# q1 Q. ~5 V( [6 e) k# k% c" M - "The number of tutorial rooms")
/ A% M& p% M' b( D9 f0 v1 {+ O
8 m/ }1 Q6 u- E- ;;; 简化教程的定义
4 H1 z7 X, S% t( z2 L; t# f3 K8 g# Y( h - (defun string-last-line (content)
0 ~) U9 W* s0 K4 L2 D - "多行内容组成的字符串中的最后一行"" }/ v; [( s" T, _1 k, v
- (with-temp-buffer
) h; j7 L! S/ R* _ - (insert content)
" J+ f9 F8 e# X) F. u - (buffer-substring (line-beginning-position)% U' Q- j8 u* k6 Z/ L7 m
- (point-max))))
: ]; F) H* m- U; @: g8 ?4 m6 S - 7 p" u6 f4 n. }6 k1 t
- (defun def-tutorial-room (prompt)
7 t& I V# B9 q9 c" ~, t, s - "根据提示语自动生成教程房间。2 Q* h1 H" d8 _# u1 K9 Z( C' n" ~" [
- x& }0 y7 |9 o. T
- 1. 提取最后一行作为问题;
/ q! @- c8 C3 W" y& @- d+ e: c - 2. 分析问题,获取期望用户输入的内容;" G5 {" c( z/ A, H0 S! P
- 3. 定义教程房间和重复提问房间。"
* A* Z3 @" }% f2 Y& S - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))% k7 k% a" P! C) [6 J' A0 H1 J& r
- (repeat-room (concat room-name "-repeat"))( k* S4 p$ G* C2 J# B: v4 U8 y
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
" M9 @# X; r- U - (question (string-last-line prompt))
4 J# ^. G5 o# d& x, P5 { - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)% a/ a' O. r% U, Q
- (match-string 1 question)))
7 T) p+ z' R' v7 l8 r3 N - (doors (if except
0 [5 f/ h/ M7 b9 d - `((,except ,(intern next-room))
7 w/ }0 f- Z& K1 x! G - ("q" living-room)5 d" o7 w9 p5 j8 T* F7 E9 W! z& U4 _3 Q
- ("Q" living-room)4 }0 ~1 V+ S/ j* I: `
- (t ,(intern repeat-room)))3 x/ W6 J2 [7 d$ z% _" Z
- '((t living-room)))))
: A5 k: V/ b! }' j4 n ^9 ]2 ^ - (def-room-raw (intern room-name) prompt doors)# E0 i) e0 N9 s1 ]
- (def-room-raw (intern repeat-room) question doors))); f- y8 V% u n* e a
- ! K0 _5 E* B/ P; i1 j J) ?
- (defun def-tutorial (&rest prompts)3 `1 `; c) X) N4 I$ a! m4 m- @5 ? r; t
- "批量生成教程房间。"$ C. u" N7 K: o6 f
- (dolist (prompt prompts)
2 G: d( V7 U0 Z - (def-tutorial-room prompt)))
- u; t S' a3 {$ c8 \5 Z
5 y8 L: J3 m/ f9 k- (def-tutorial
$ l3 I8 u3 q: D& s: Z: f - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。. p( s, `: `2 l
- 1. 教程
; q( F- K& H" G3 K+ `1 _) X - 2. 入门(3x3)7 r$ o" H5 u3 K. @
- 3. 初级(5x5)( D; \7 E- u' Q# l8 D
- 4. 中级(7x7)0 L. Z* T6 S W2 t
- 5. 高级(9x9)
5 D' B' C! a/ i/ J0 } - 0. 关于作者: [ U" O1 @' ^- A1 s, M
- 请选择1-5开始新游戏:
: `( c+ X. r5 ]4 Q3 U! `1 m - 您现在正在游戏大厅里。 _9 T$ c5 n; _" D U
- 请输入“2”进入入门级房间", B ^1 f) }) j; M: i( w
- " ①②③# H i8 A2 m6 q& r+ v$ y
- 1 ■ " x8 q* {* u% K% I5 z+ V/ r
- 2■■■
$ t/ i6 E# g: Q4 I/ r2 N - 3 ■
- v& `8 q2 Y% |" }, |2 U - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
( b2 P1 e# X4 ^4 j9 {% W - 请输入“22”来关闭第2行第2列的窗户。"
1 E/ j( ]3 ~- c* T - " ①②③
$ r! D+ p; f& \: ]1 l* u - 1
# d# t7 w5 l8 I/ D - 2 " U6 w6 T5 G7 ]
- 3
1 m* D6 Q; }9 b4 d8 V9 P - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
' J8 Q7 l8 ]5 }1 Q4 V+ A0 y. L - 请输入“11”,试着开启左上角的窗户。"; G, i6 v2 k( z3 t; s* ^
- " ①②③
8 s. w f2 N4 Z; m! c4 t8 q! T - 1■■
* L4 M8 g. K& |- ~" ` - 2■ 8 e8 Q) |1 Z) m
- 3 @7 n0 Y. o' O- n C3 |) r
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
+ i# u5 q6 \- v( x - 请输入“13”开启右上角的窗户。"8 C3 x2 f, y* [ b" M
- " ①②③2 [* x& w h- ~, }
- 1■ ■7 Z8 H( r3 O y* ]/ ?/ v4 G: G
- 2■ ■7 o, p+ E2 o' L+ H& c" [. c1 a1 y
- 3
: Z* Q5 Q9 X! r - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。8 s# b: _5 N' E
- 请输入“31”开启左下角的窗户。"
2 e( j5 a# O3 E4 a- @ - " ①②③& w/ r& V# {3 R( H) O4 F/ M
- 1■ ■( X8 Q6 F7 a+ x |" d; n) z# P
- 2 ■5 K @- k4 a2 {, N$ X
- 3■■ 6 C5 ]7 i) j, G" k+ q
- 此时,总共有5扇窗户被开启了。. O0 [+ `& F) M4 e/ Z
- 请输入“33”开启右下角的窗户。"2 e3 t" c2 Q; q2 J
- " ①②③
$ l6 }( r1 z `" n! L - 1■ ■
5 {+ g! T0 v* Q9 s" J" t! c5 C6 B - 2 8 A0 N/ J$ K& O3 V
- 3■ ■0 e1 n- ` T% Z+ X
- 现在,只有四个角落的窗户被打开。6 ?. Q- i5 I' j; s- R0 b) Q6 q: _
- 请输入“22”完成最后一击!"7 B4 J- |0 M; z$ ` M: r
- " ①②③9 b+ Y1 B% V& Y) z5 F& T; k8 |
- 1■■■
$ ~: v7 Y2 ^& d5 O( ~$ Y - 2■■■$ M) e T6 Y M9 q! d/ S I% i
- 3■■■" ]' y4 w# o( Z' q( |4 s0 v$ W
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")7 j7 G: f7 D( E5 Q8 N2 ]& ]- ?& p
- 1 Y q% P! T4 E( a- \+ v3 D# w7 Z1 l
- ;;; 棋盘
/ j/ d: E9 [% p5 r8 r; M. G - (defconst *wechat-5x5-white-chess* 12288
% ^$ u+ m6 _- m5 B; a - " ")
. F1 n' e. \8 V1 E; |7 B
/ I8 L- ~: c7 A) d! W: e7 j0 C- (defconst *wechat-5x5-black-chess* 96325 v0 V- F! ?- S( d2 {7 L/ p" Y1 I3 b1 u
- "■")- o$ g/ c4 i9 n$ N" V& t( x) [3 X2 W
- " z% k1 l* ?. f( s; }9 k. Y
- (defmacro with-board (&rest body)7 _8 Z* b1 v; Z. Z* E$ x
- `(with-temp-buffer
& h o8 V+ }8 A. z- R( d - (unwind-protect7 X0 k5 t* X2 ~7 }7 }
- (progn9 T8 u. Y2 X' [
- (if (session "board")' J" w$ t" l0 y5 J( x1 ?
- (insert (session "board")))
$ p2 Y1 ^0 Q, q6 b8 z - ,@body)
3 u/ u% _; h8 C k - (session "board" (buffer-string)))))
9 [+ [) ~* n& ?. Q0 y, J
+ `3 z" O1 g, C0 T8 J9 F- (defun board-init (size)" \* A) m4 e9 D, O3 E: Y
- (session "size" size)
' b% P2 R! s% M) c - (session "step" 0)' |- O7 m5 A, n$ `; w6 p
- (erase-buffer)4 X) C! E" c& \5 [
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))2 w. S2 q2 ]7 e. \
- (dotimes (row size)% j+ v, o) D3 }4 v- |" k
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
, r8 @% T* [3 s+ a! {$ h6 m - & V( [: q7 O" [6 I+ F
- (defun board-contains-p (y x)
* |" C; I- h6 P$ ]6 B- B - (let ((size (session "size")))! x# D4 ~/ }% K6 @% `9 l" a7 y8 X
- (and (<= 1 y) (<= y size)
+ z9 }- [) F2 Z' J% ]7 i - (<= 1 x) (<= x size))))6 M2 ]# i6 J% [( i
l S" D; D1 ^6 N# A. \2 M% z; j8 I- (defun board-toggle (y x)8 ~5 y5 v0 i$ z3 P( I
- (when (board-contains-p y x)! c% b" C& a- l! f; E' _0 }1 \8 m
- (goto-line (1+ y))7 w/ ?$ e. j3 c; T0 T; Y
- (beginning-of-line)# a& K" g1 k6 l$ F: [3 H8 z: C
- (forward-char x)
/ m8 A5 Z9 k& ] - (insert (if (= *wechat-5x5-white-chess* (following-char))/ B4 S6 ]) b' b/ \
- *wechat-5x5-black-chess*" ]. |( A p' v* j8 O( i0 Y9 m
- *wechat-5x5-white-chess*))
/ Q' L! ], G. A8 J2 y2 W1 c' a - (delete-char 1)))9 {& H) c- W. I% K/ k2 R6 f
3 ~/ p: Y' \, y/ ~- (defun board-put (y x)6 L/ q) t0 a2 w# V M7 e3 K
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
: g& U1 W, J+ x m p$ u - (board-toggle (+ y (first dir))* J% f2 M4 c2 T, k4 Y0 V
- (+ x (second dir)))))
/ X0 x2 U: S' R
( F9 l$ h- n- X" `- B2 r4 k- (defun game-over-p ()0 V$ j" I6 l* r% c) p' k
- (beginning-of-buffer)
8 Q' x2 M" E7 A/ \! E - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
, t8 v; L5 U+ P5 P1 ^. ?/ Y1 c8 t
: l8 ]- h8 j2 G# k* ?- (defun board-show ()
' F ^8 @; M0 l - (with-board \. Q9 V" m, i5 K# n l9 B. y
- (concat (buffer-string)
5 S* q' z( ?) q# U - (if (game-over-p)
8 @( O" Z, ]8 T: U - (format "共%d步,输入任意内容返回大厅" (session "step"))! X- @+ y5 T% \, o" A, F
- (format "第%d步" (1+ (session "step"))))))). i+ E& j% U! s7 s" O7 O
" S. w1 ~5 R0 L: G$ C- (defun board-position-parse (cmd)6 f: u9 W* P; A8 |. G
- (if (= (length cmd) 2)
$ X" E4 O( t L: d; p7 S& K5 Z# k - (list (string-to-int (substring cmd 0 1))- J6 G# q" L1 j. n. [! W% v
- (string-to-int (substring cmd 1 2)))' Q/ t, U, n+ r! p1 P
- '(0 0)))
! [7 i* I6 w4 w5 S
6 S# ]1 L+ c c$ U- ;;; 游戏房间
5 Q: K$ y( g0 u! r' \ - (defun game-room-init (cmd)$ ~* g) E! j, ?1 ]
- (let* ((middle (string-to-int cmd))! ~5 z1 S: b* F) ]' k
- (size (1- (* 2 middle))))7 u) v. P8 B- K9 F" Q! M, L: Z
- (with-board
# y7 p1 r3 Q3 C; N# m- b, F - (board-init size)4 G. ^8 \. O5 x w, _. E
- (board-put middle middle)))
, g3 S; k0 I- y1 u2 M* W - 'game-room)
9 t& E) r3 W- l' H- W - 9 J6 L4 C4 l4 S9 I. o& ~4 O
- (def-room game-room8 J, n% e. ?9 V. J* m' T' w
- #'board-show& J4 `9 \4 m% H' i% D
- (t (lambda (cmd)
+ ?3 D( ~. L+ ?7 u2 Q4 Y - (with-board
- T/ b) i# q% C: Z6 ` - (if (game-over-p)
5 m3 C- u6 H9 U9 B - 'living-room
, E3 M; d E5 }; K - (destructuring-bind (y x) (board-position-parse cmd)
- i8 F: |# Q5 I l - (when (board-contains-p y x)4 I3 U/ p# t. e
- (board-toggle y x); o. }- x2 X, Y" |1 U/ s. G
- (session "step" (1+ (session "step"))))
J4 I9 R* Y* {% K. `3 ~1 N - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|