wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。; [- s$ e2 z3 n
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- ! N) b* m7 X1 h# W3 P* I
- <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;">;; 定义新的游戏地图
4 j2 N( o- o4 v& A - (def-map "/game/5x5.el" ; 对外开放的URL' o9 ]6 ?( Q- ^8 m: s
- 'tutorial-room-0) ; 默认的入口
& O" M3 Q( j# U& T& V3 o
: B! `4 X7 o2 O$ _% @' k& {- ;;; 游戏大厅+ U0 c/ G: T+ J( n9 ?! @9 B
- (def-room living-room
7 L5 d" z4 v. Y4 _- d - ;; 进入该房间后的提示语
/ X. R. z' Z" H6 U - "1. 教程0 l! O, N% e" y/ P' W& ~( v9 O
- 2. 入门(3x3)
7 Y) }4 ~- P3 B8 c0 B2 o - 3. 初级(5x5); @% V0 E& ^- ~
- 4. 中级(7x7)- @: y" L& W, a0 e7 }) B5 N
- 5. 高级(9x9)
# X" L# Z7 I( C/ q6 r* j B4 T! p7 T - 0. 关于作者' S4 t4 ~5 Q5 Q M5 W
- 请选择1-5开始新游戏:"
1 z M+ c' H: C8 v - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名7 n$ N4 ~. n% |9 u* O
- ("1" tutorial-room-0)
1 G! B5 y: _* r- G& r - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配$ d" k8 [1 I& Z5 ?0 E
- ; 相应的返回也可以为函数,动态返回房间名, N4 C& H6 Z2 [1 j; U( U, d6 l
- (t living-room)) ; 如果条件为t,为永真3 k8 ]' _9 S, ?! k# g) t* H, I4 K
4 p( Z6 z+ {7 b* R/ c8 k9 f- ;;; 作者信息
$ R. x# S& Z7 l: I, b- ` - (def-room about-room9 W* }" |/ a6 i
- "作者:redraiment0 Q$ `0 \) `. h) Y, x- t. m; F
- 微博:http://weibo.com/redraiment5 v' U* Y0 }: r/ N& T
- 有任何建议,欢迎在微博或微信上联系redraiment。
& p. Z n# t) `) L - 请输入任意数字返回游戏大厅。"$ e! Q0 b: D% Y. v9 q- E
- (t living-room))1 v1 ?, I5 F0 d/ l2 [" w- O
* C2 D; m- ]1 Z0 L. v3 I- ;;; 教程. O+ D+ ]7 E5 ^+ D: D9 t1 x
- (defvar *wechat-5x5-tutorial-rooms* 0 j& m( }( k0 \& T, W$ t& j
- "The number of tutorial rooms")1 U8 K0 y, p6 c2 h, \! z+ i
$ o. U. u" k2 a9 y9 |% s6 U! d J- ;;; 简化教程的定义
- m/ v Z- j3 P# { - (defun string-last-line (content)8 \* r* i4 [% E" X. C. d% ?
- "多行内容组成的字符串中的最后一行"
/ G9 z' _' Z/ E( _3 j9 X2 E1 T# e - (with-temp-buffer
3 b4 ~, ?, N# ]) T) r2 f - (insert content)
! s5 O# M7 A/ G% z7 W - (buffer-substring (line-beginning-position)
2 {# I# h1 l# V2 m6 Y - (point-max))))
9 w. V% X' h) R( M) p0 \
! v) q. W& ?; V: \: Y- (defun def-tutorial-room (prompt)+ D8 k6 z. j' `8 B0 l _- m
- "根据提示语自动生成教程房间。 }+ u9 s. G4 T! T' P
, B/ Q8 P6 v) s- 1. 提取最后一行作为问题;
7 R) v/ |' c" Y& d& S9 B w/ {" f( k) ` - 2. 分析问题,获取期望用户输入的内容;
& g- h" U# S" _2 @ - 3. 定义教程房间和重复提问房间。"+ M2 O n6 v! r
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
( d# U7 H& d' A! { - (repeat-room (concat room-name "-repeat"))( W3 W; ~# F0 P# W
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))) s5 X, L+ V' b7 S
- (question (string-last-line prompt))
2 c1 {/ W7 r* q: \& l - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
3 f8 I1 y2 @$ _ - (match-string 1 question))), z* i) p' R0 }! o" i% y
- (doors (if except
1 r2 m# G, u, _ - `((,except ,(intern next-room))
6 B2 Z+ Z' r' G - ("q" living-room); r$ N5 I) E( d5 J4 N% w, I+ Q7 n
- ("Q" living-room)
: k3 l9 M% l) Z( K% `; g - (t ,(intern repeat-room)))6 u, T. }) f9 C
- '((t living-room))))), t' w' s! P* y" @& y
- (def-room-raw (intern room-name) prompt doors)
i) Z" S, Q4 A1 z' i' H! d, ^ - (def-room-raw (intern repeat-room) question doors)))% c7 C* y' N" k' t8 D* x
& X* z9 J; H$ j- (defun def-tutorial (&rest prompts): ^9 [9 J+ v2 f: x6 l" S z
- "批量生成教程房间。"% L8 {6 Q' J, q# p1 q; y* b# Q
- (dolist (prompt prompts)* C, @" i4 |* q# b: }' h1 [. ^ l3 t
- (def-tutorial-room prompt)))$ i/ ^: h7 {, D: m: j$ ^2 a
3 m4 c7 r' t8 S6 n- (def-tutorial+ p" [* r, g! |8 W, @
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。7 V- F9 U* H4 t8 b
- 1. 教程
- H8 h8 J3 z0 k - 2. 入门(3x3)* y( T7 Q4 G1 ~6 s3 _1 Z' V; p$ G
- 3. 初级(5x5)$ F! c* L4 P; W, @: U
- 4. 中级(7x7); x# p: h5 P% w1 ]- y6 c
- 5. 高级(9x9)9 u1 y/ _/ S" E" j# f3 x' F
- 0. 关于作者' P# r( d0 s/ _# \' R, D8 v0 c
- 请选择1-5开始新游戏:
% t$ A ^2 v) p- o8 L' W - 您现在正在游戏大厅里。+ i) }* S/ L! r" q4 u
- 请输入“2”进入入门级房间") V; I! B$ I2 D a& U# C4 X0 `
- " ①②③; `& E/ f0 e8 U1 d$ J4 m' [9 L! d
- 1 ■ - U/ M. v8 w# Z
- 2■■■2 @3 R7 \8 V% g |5 E, @
- 3 ■ , J4 A3 a0 j" E; y( Y# Y* W# [
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
2 r- E, H& x ?8 P1 r6 L8 X9 X& `& P - 请输入“22”来关闭第2行第2列的窗户。"
4 n# p1 q4 t4 q. |1 H7 S - " ①②③
5 H [& R3 r. h' O - 1 4 M( F8 W! i- P- ?9 h6 I( _& n
- 2
6 H+ O( H* r/ x4 M - 3 ; b6 P { l0 s, ~
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
; d' r8 {3 c' s - 请输入“11”,试着开启左上角的窗户。"
# t) S7 c0 d5 ] - " ①②③0 j: _5 o6 [7 Z8 \+ @
- 1■■
! O* h) r- K0 ]3 M% J+ M - 2■
$ y- @/ @' n7 d- B - 3 1 A# |/ _% y' O+ A# U J/ T% c
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。$ j! U* Z( Q( e5 T+ \& m
- 请输入“13”开启右上角的窗户。"; }$ ?, u" z5 Q# O
- " ①②③
! _5 {; J/ g- W2 [0 r k* J) _- [ - 1■ ■
; Y9 h/ t7 w2 V( b1 H$ D - 2■ ■; p8 ~7 U/ F s# E- V
- 3 + T, w0 l; f' b
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。6 P5 V: e, N3 |7 _ X- l
- 请输入“31”开启左下角的窗户。"
* p0 R! \' l# Y6 J/ V; ~1 e - " ①②③
0 X; j$ G+ i& H6 f, \8 [ - 1■ ■
* I0 u l# [; k- h! n: H) c - 2 ■
4 Z, [9 d! n6 z# E - 3■■
+ d2 h x5 `# H1 c; A1 L8 l4 L% s2 N" l - 此时,总共有5扇窗户被开启了。- ]+ F2 v* \8 H4 G6 N
- 请输入“33”开启右下角的窗户。") ~5 Q8 i$ }6 ^- P- E( Y k& J* Q
- " ①②③$ s1 {( g; @, R C
- 1■ ■' U5 F' r; u( f
- 2
* s2 ?# ?/ x# _ - 3■ ■" m& j. V1 y3 a" z* _
- 现在,只有四个角落的窗户被打开。
5 [$ N5 k1 S8 A+ U - 请输入“22”完成最后一击!"! n7 p6 o, _0 v
- " ①②③- h7 A. g! O5 m$ l7 _6 B$ _! o
- 1■■■! I: ]; a- d: H$ c/ P' l2 E( B; L! p
- 2■■■
- [7 B* @( F6 s* f( v! L - 3■■■
5 r7 T! p- Z' k! K: V - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
" P$ i; p- b3 x2 ~% q0 y8 h
2 r# D: A0 H+ Q9 Q1 v) E" P& M- ;;; 棋盘6 v% [! s# D' O* ]5 u- l/ \" D
- (defconst *wechat-5x5-white-chess* 12288$ }' D& ?+ U2 D& Q
- " ")
, h2 n% I( Q/ G; D% }/ M$ _ - : N/ f( ^2 j! U$ c' k
- (defconst *wechat-5x5-black-chess* 9632
4 x1 U0 z& b* J4 N" a6 F2 u* { - "■")
7 v& p; z9 P8 b% A8 D" M' d9 O6 {
6 j1 w( q0 ` F# F) s- x1 f E- (defmacro with-board (&rest body)
! J6 q% A' a2 E; X - `(with-temp-buffer
) [) U7 c q7 v6 x0 [% t1 J - (unwind-protect6 H! o3 @, U: D! |6 _$ _9 g# |
- (progn1 R' k$ s, j5 c( v" q% o
- (if (session "board")1 ]" H4 k" s- l
- (insert (session "board")))3 s* Z/ O" a* H( k, A5 Y& c7 z
- ,@body)1 Q- D7 r1 M( K8 C1 S9 I3 p* y
- (session "board" (buffer-string)))))
, Z2 F6 F/ n( n' Y - : m6 p% S! i6 |$ H1 y9 K3 U
- (defun board-init (size)
' Z# ^2 j9 d: `4 A5 N - (session "size" size)
$ H# x( M( w2 E/ h - (session "step" 0)$ G i/ P& T2 X1 a7 Z
- (erase-buffer)
& h: t! U) I3 @1 H/ p- f# S3 K - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨")); b- d* I" ?: e: U1 h
- (dotimes (row size)
7 f( V0 x6 |2 j% Q - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*))))), V' D0 o5 V# F) Q& B0 G
- 9 I) N0 W$ k' l- v' r B# B
- (defun board-contains-p (y x)( ^0 |- L# _ M! x$ j
- (let ((size (session "size")))
# [) W) {/ J6 ^8 Z) B - (and (<= 1 y) (<= y size), s* L- H- g+ ~
- (<= 1 x) (<= x size))))
! W' w/ Z5 z( d% c* M' ^, J" r# G - ; B* O% }+ t: t; [0 [
- (defun board-toggle (y x)
: V& a! D) T6 j: h - (when (board-contains-p y x)1 G( C! b) J; n& K$ e1 u
- (goto-line (1+ y))
' k, X4 ^/ q: J+ h* Y% q - (beginning-of-line)# D. M7 k- j) r {4 T( R$ O9 Q O
- (forward-char x)
* I4 D5 X7 o, |& g2 w9 j - (insert (if (= *wechat-5x5-white-chess* (following-char))9 n# i5 A, Q) K$ W
- *wechat-5x5-black-chess*" ]! O) {' e, Z' Z/ v! H' L
- *wechat-5x5-white-chess*))
( e) L9 Y& F, \! \$ z- E o - (delete-char 1)))
?+ G3 b# w: E+ D4 B0 M
9 {6 g' T0 F1 j5 Z+ ~$ d- (defun board-put (y x)
* U" j. F3 E7 C. T - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))- m) @" L) M' [
- (board-toggle (+ y (first dir)): n f4 A) P" C' i
- (+ x (second dir)))))8 }! h2 M4 u( M& Q: u. L9 a
& U0 [) s# F( i& M- (defun game-over-p ()
" u5 r1 r0 L5 K: |: b. Q* a" I - (beginning-of-buffer)
$ T0 u$ d) V) C' h3 X+ N/ s - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))& [. b0 [" [. n; C% d+ q1 |
2 u n3 v4 R% P3 k; B f) z/ J- (defun board-show ()4 s: C" S$ v( q; r. g$ \, S
- (with-board8 t% d( f: d% r+ P! W
- (concat (buffer-string)
7 _7 s7 o6 o1 G/ r' z - (if (game-over-p)
' }; l# `: C, E3 \, f - (format "共%d步,输入任意内容返回大厅" (session "step"))+ o0 ^; s% x3 k) [/ P. Y- W
- (format "第%d步" (1+ (session "step"))))))) o/ j ` B/ T5 E+ [4 M
- % c# {% H0 g& K! Y
- (defun board-position-parse (cmd)0 z) N. U1 Y& e9 q" m
- (if (= (length cmd) 2)! A& m2 i F6 M" C7 o% ? z3 z
- (list (string-to-int (substring cmd 0 1))
2 ^6 Z6 M, W4 x. B, \ - (string-to-int (substring cmd 1 2)))0 c( H: x5 ]* z2 X4 w! D3 _, I
- '(0 0))) g* `, U" Q8 j' ]# Z- R W
- , z0 \) A* J, p+ C6 t, O9 U: j
- ;;; 游戏房间
7 \# d% a4 ^: _ - (defun game-room-init (cmd)
* V, m1 M j, [6 N - (let* ((middle (string-to-int cmd))
+ `7 B2 c o2 C2 d - (size (1- (* 2 middle))))
1 n3 M( Y. z& @1 h - (with-board
1 w3 R" M8 |2 S9 R: P6 j# W9 s3 W - (board-init size)9 O5 F2 L Q. F/ J" g
- (board-put middle middle)))
l+ Z' q2 ?4 X3 ^5 o/ i - 'game-room)* H4 m) b; K, c% a, W+ K
- , n' d. E; I1 Q, p/ \& s$ e
- (def-room game-room$ Z9 S' R. \8 W H
- #'board-show g! ]- V* ^# {$ z/ c
- (t (lambda (cmd)/ c* A# i4 y# i% a
- (with-board
7 J0 D4 T- e' D - (if (game-over-p)* @$ t4 t( ]1 o' C! o5 d
- 'living-room
4 U( e* s0 @- n7 E. y3 g: q - (destructuring-bind (y x) (board-position-parse cmd)
$ {: i0 U6 h8 J1 | - (when (board-contains-p y x)5 U* l7 T9 s- W6 R; ~+ ?6 M' w
- (board-toggle y x)4 s" o% W9 l# q6 D, T3 D& B$ n' k
- (session "step" (1+ (session "step"))))9 d2 v9 @9 Z+ a# T2 U
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|