wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
" H( c/ v6 H6 b# N+ P9 f K借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
0 K& y3 h. n* ^& ~! j4 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;">;; 定义新的游戏地图! c- ?. u/ _% N% j6 j
- (def-map "/game/5x5.el" ; 对外开放的URL- U, M( o/ a( u' K
- 'tutorial-room-0) ; 默认的入口
$ |7 y5 J. v0 [
; w( K( ^5 @0 D; [- U- ;;; 游戏大厅5 ? T& P0 Y& M0 C
- (def-room living-room2 { v$ p. x9 v
- ;; 进入该房间后的提示语
# W) A( w4 B1 L6 D6 I0 F0 f - "1. 教程- ?8 q- F# ~1 m) D
- 2. 入门(3x3)
$ I, B4 Z6 H& d" d' [2 k1 O - 3. 初级(5x5)
. T/ w' q9 I" R& k8 ]# A* R0 H4 i - 4. 中级(7x7)
, }- N! x3 L/ y% E2 U m) u - 5. 高级(9x9)
' o* J# N m( T2 Q7 M" u - 0. 关于作者
2 s# d8 Y8 V9 k4 j - 请选择1-5开始新游戏:"
3 x7 i* D3 Y8 `: A% V - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名, b% S: P5 Q- F- G& I& k3 C4 a S
- ("1" tutorial-room-0)) I( t |% e; K
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
' y! ]) Q% y5 I5 j - ; 相应的返回也可以为函数,动态返回房间名1 D. n& V) [2 ?
- (t living-room)) ; 如果条件为t,为永真. p6 W3 |+ _8 d w g
3 J! H& s; ?. V- ;;; 作者信息
- I# J9 P; F: b& R- ?& b - (def-room about-room, M+ B: |5 Q9 a7 H! L2 a
- "作者:redraiment8 h0 m( Q; @/ O2 [6 |+ j" n
- 微博:http://weibo.com/redraiment" O: U2 J5 Q- k& T$ H9 M- ^
- 有任何建议,欢迎在微博或微信上联系redraiment。3 z t4 F9 ~( h. W7 |* l
- 请输入任意数字返回游戏大厅。"
( \4 i$ C' H; K6 ^ - (t living-room))# q( }& h" Q1 y
/ n9 v: f2 b& N9 F* K2 y3 T- ;;; 教程" j s* _$ r; T$ m0 C
- (defvar *wechat-5x5-tutorial-rooms* 0: M0 A* v8 y G- q% q
- "The number of tutorial rooms")
, P- P" {2 E. d( {; B - * x. k2 l6 V; [
- ;;; 简化教程的定义: o! K: P' S: n
- (defun string-last-line (content)
, a: }, v& G- Y& E2 c: v - "多行内容组成的字符串中的最后一行"$ d% _1 D+ I' R2 m: h5 _3 i4 K; p
- (with-temp-buffer
; u' Q6 C, v. h2 q - (insert content)0 S# b; N! Z$ v; Q2 }9 U
- (buffer-substring (line-beginning-position)+ U, i# N6 |( z+ }$ @$ y5 k
- (point-max))))
( K' M% H6 D' N! b o w9 g o- d - : N' t0 V4 g0 t& C0 K) ?2 x: c- I
- (defun def-tutorial-room (prompt)
7 S# E: Q3 X: L: Y4 y( | - "根据提示语自动生成教程房间。
3 J+ a# [1 q: u# R! s; \; ~ - 9 _" I) o9 g) `' w0 r5 @ n
- 1. 提取最后一行作为问题;) S6 U! x" Q) {! N( ?
- 2. 分析问题,获取期望用户输入的内容;
, s U: |2 m( m( O0 {% A - 3. 定义教程房间和重复提问房间。"
$ q3 X* ^4 |; N; t! | - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
& O; h9 ?5 j: B8 B# J. n - (repeat-room (concat room-name "-repeat"))4 Q k7 I& M+ J: |; y u
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))" M* T8 ?3 _6 g! p+ ^2 a1 B7 }
- (question (string-last-line prompt))
' N/ @3 v8 C' Q - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)7 i! E. E7 O) L2 `2 o9 K+ h, x! d
- (match-string 1 question)))# s t$ F8 Q3 v9 R% _
- (doors (if except
0 w% i5 ?& {7 [ M3 q8 _+ W - `((,except ,(intern next-room))
- [/ L2 S F2 b+ t& t1 @ - ("q" living-room)
) s/ i( R8 K8 I3 \" | - ("Q" living-room)& R) o* ^5 c' q% t
- (t ,(intern repeat-room)))
4 m& g3 w8 F) { - '((t living-room)))))7 f" W* b2 Z; v3 V- k
- (def-room-raw (intern room-name) prompt doors)
& ^6 ^6 \1 i( D( v4 C, q6 {8 H - (def-room-raw (intern repeat-room) question doors))), O+ I6 }1 @( n; h, \! j% X
3 \5 j' ?, _3 a4 p1 b, v5 j- (defun def-tutorial (&rest prompts)
8 k5 g' I. B' s# } - "批量生成教程房间。"
6 m B5 y& ^" `5 _. q( l4 a - (dolist (prompt prompts)7 y: a! Z* `; w6 M: \9 P% ]7 ]
- (def-tutorial-room prompt)))* J+ ~2 N* y9 C# f- W! O. N0 Y+ r; a' \
$ Y6 Y& ~! h- H9 S8 j: r0 i- (def-tutorial, k5 M3 L/ d1 Z" W2 ^; k% \$ R+ U7 r
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。, q, l1 `7 M8 \% p
- 1. 教程+ z5 V _8 e/ ^* W
- 2. 入门(3x3)+ q1 Q0 i; B, S: Y# Y. H$ f
- 3. 初级(5x5)
: v) J/ i, D8 }4 S8 D3 P1 Z - 4. 中级(7x7)! V/ v' r! [) T6 A' {4 C
- 5. 高级(9x9)5 i8 ^: S! g' |# B) h; z9 r
- 0. 关于作者' i# E' c/ U) q. A" ]
- 请选择1-5开始新游戏:( @+ e; s6 S% c( E6 g) y0 V
- 您现在正在游戏大厅里。/ g" n1 O$ W3 W ]/ T* u! H$ Y
- 请输入“2”进入入门级房间"
! e9 w% R" ^. S3 J - " ①②③7 {7 W1 E6 u9 I. t2 }$ W
- 1 ■
% A5 I& Q; v& Y' {) V - 2■■■( W, |0 ^. [- `6 i. B$ e6 i
- 3 ■
^- w% d- C* {* w - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户! @. v A" h2 b* c9 W/ V9 i
- 请输入“22”来关闭第2行第2列的窗户。"
: ^; d7 e, ?9 d( t* c5 I - " ①②③9 [0 Q7 R, i0 x' Y7 \7 s. |9 J
- 1 7 j; a% J( m [7 v# u& O/ s7 Y- B- I
- 2 * g2 t/ B J2 I$ `& ]' r" M9 T' G
- 3
! M4 G3 Y1 R$ b - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。, w! l; v4 n& u+ q- r
- 请输入“11”,试着开启左上角的窗户。"% F6 r# i8 t. l* D S
- " ①②③
: z2 u, n q! [ - 1■■
3 k j5 P* Y* Z$ |+ f - 2■
. }7 F3 Q c& n' S7 ?; p+ c - 3
% j4 ]* o6 F! [: K% R - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。: |" {: H2 T# k" r6 e
- 请输入“13”开启右上角的窗户。"1 w! s$ u- Q o1 Z- m( M8 o
- " ①②③
" d! M& \- }4 a8 Z - 1■ ■; R2 F/ P2 S4 u1 ?0 }' O
- 2■ ■
! J: D! _2 ]1 w2 n - 3 - \0 c4 E5 w u* y& ]3 g: P) ~
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。/ U; [+ j6 s& ~* G8 ]" ]
- 请输入“31”开启左下角的窗户。"
& o8 y* D( o c T, W* w/ c - " ①②③7 s" ^3 q- @4 h4 \. d+ f
- 1■ ■' b) u6 ^$ y- W, B# N, A
- 2 ■% }0 P# V' H2 }6 s& b
- 3■■ * L7 F/ k. n' i# P$ _+ r
- 此时,总共有5扇窗户被开启了。- L5 k/ z' o. a* @" W$ z4 k
- 请输入“33”开启右下角的窗户。"
# |# | z+ M- F# f) }3 N' z( r' _" M - " ①②③
: M0 j. k8 Z9 p( E' n; p& Z - 1■ ■5 ^0 x9 q' t3 G
- 2 ( k/ ?& h5 ^' o' ~( D+ \' H
- 3■ ■; V5 G S8 F/ ^1 t& v/ `
- 现在,只有四个角落的窗户被打开。
! z& `' S! j0 [: { V$ L! D - 请输入“22”完成最后一击!"0 ~0 Y, @" I2 j
- " ①②③; P k. T y1 j* x
- 1■■■8 C2 d' G+ T7 p3 a6 |
- 2■■■! e B3 l/ q, G. m
- 3■■■
/ n% _% d0 |9 e* | - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
1 Q W0 e& D) b - % N! O' G; U7 ^$ L
- ;;; 棋盘
. ]. z) K- r7 n! D+ c+ z/ L2 b - (defconst *wechat-5x5-white-chess* 12288* K$ C* V- _) D" v# a8 u$ R: `
- " ")
3 z' g% |* u9 Z2 r! D7 X2 ]
8 {3 P+ C: i! H/ y- (defconst *wechat-5x5-black-chess* 9632* `9 E) _" l# o6 N& X
- "■")
+ y7 {- _4 A& I1 H, L, E' Z: q. {
; f# z* F. G$ i- (defmacro with-board (&rest body)
5 x! b$ M3 P# I& ^) F - `(with-temp-buffer
, W' y8 ^3 n- m5 |1 Y" N: r- [ - (unwind-protect
+ f/ k& a& G# P2 m1 e q2 W - (progn, M( Z) j( |7 o' v2 S) Y
- (if (session "board")
3 O" f8 m% k3 c& e7 F - (insert (session "board")))2 |' x- S! ^! b
- ,@body)
( L; q/ m( n) g/ w/ M - (session "board" (buffer-string)))))% [- a; p5 }' l+ R5 I1 c
$ H7 ?8 |5 K3 F, g: E9 I- (defun board-init (size)* q/ q* n+ d& s4 i7 B% H r
- (session "size" size)
8 o# m& U7 R4 m - (session "step" 0)4 O) Z R9 s, u# _, F
- (erase-buffer)
; K# h/ O& _& q) F% h; |8 ` - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
) q$ L/ |6 g% c3 U$ r - (dotimes (row size)
% o6 R6 l( {# E3 W6 Q: \ - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
3 z. X6 b2 [* Z* I2 g, O* v5 X
+ @' J1 M: ?2 A, `- (defun board-contains-p (y x)
( g3 s, C) y" ] - (let ((size (session "size"))): n; G. K9 B9 Y r, v
- (and (<= 1 y) (<= y size)
1 K( A% D) `* d, b - (<= 1 x) (<= x size))))5 p4 I8 x4 O9 w2 {3 h( o. t8 f
. n/ R: E. o% L1 r- (defun board-toggle (y x)% o2 V0 h5 u) `' N
- (when (board-contains-p y x)9 l! f) l H! {1 b) a9 ^" v' B- f
- (goto-line (1+ y))* Q9 A# S( |" O3 ~
- (beginning-of-line)$ N3 ]6 L! w) M( ~( [, k9 D* j1 X
- (forward-char x)0 m1 r9 Y+ f* \1 L, }6 _8 R4 u6 R
- (insert (if (= *wechat-5x5-white-chess* (following-char))
; W/ j- n8 v/ D* X" w - *wechat-5x5-black-chess*
+ w5 ~- p$ O6 `: t" L% M - *wechat-5x5-white-chess*))
! }$ |, Y3 A O }$ G1 ^6 q( m7 N% I: c - (delete-char 1)))- D7 t8 L$ \+ W
4 M6 l: S8 H- s- (defun board-put (y x)- T" [0 P( t4 ~) z' t' s
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
" ^$ L7 s5 N+ [7 y2 \: r ` - (board-toggle (+ y (first dir))& j7 p6 J7 U2 Q7 A9 V' U3 v, K
- (+ x (second dir)))))
, m# D p7 v4 b- p4 U4 J7 R3 w; e# \ - ! a' s, x- Y( j: U
- (defun game-over-p ()0 @% x" W& f/ ?: U* d) v
- (beginning-of-buffer): n/ T9 G. q; E
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))" L) d' g$ b' c; p) c# R
- d$ y t* U- V1 Q+ S- (defun board-show (). l" g3 Q5 H% @* P$ I0 |
- (with-board
; o0 w% F' ~$ p% I% s4 d - (concat (buffer-string)
9 ~- C/ [" k1 X8 P7 `- l3 ` - (if (game-over-p)! y1 U/ x1 R" k4 C3 q
- (format "共%d步,输入任意内容返回大厅" (session "step"))
: @7 B1 ?" |) K - (format "第%d步" (1+ (session "step")))))))
, p9 K0 [2 o6 U1 K: R. \ - % |3 V2 x: e! c% R5 j
- (defun board-position-parse (cmd)
' I' b' h6 e# J; `8 Q* i9 W, l - (if (= (length cmd) 2)
4 H, Z; R1 _# K' ]0 t - (list (string-to-int (substring cmd 0 1))( P" Z/ ~9 C- Y7 x
- (string-to-int (substring cmd 1 2))), Y* a4 r3 {$ T* r
- '(0 0)))
, a7 A" l' u( F. I4 F' w3 w9 H - 6 |; n! h# Y% ~ q: h" ^
- ;;; 游戏房间" _( ~ u5 C8 V: W. A
- (defun game-room-init (cmd)
) r* R* l: P) H3 K+ b& {) x4 x! j - (let* ((middle (string-to-int cmd))
. W& V! U, [7 `/ E) \ - (size (1- (* 2 middle))))
. K* o9 H9 S, w, o8 b - (with-board
) _* G! `! c. Q0 x& p( L - (board-init size)0 x* |* Y0 G) S8 s0 O- [
- (board-put middle middle)))- e0 m7 E6 S; [# Z0 T6 r
- 'game-room)
# Z1 @7 H& H9 c) d+ L# S9 p - ' B5 D; t2 |1 x# x: n5 w
- (def-room game-room1 @. u! @' c3 f: R& g1 C% A
- #'board-show
3 Z) J9 V4 d( U! r5 v5 U. O# H& d - (t (lambda (cmd)
" l, ?8 y* Z. A, y- O# P7 ? - (with-board
% y5 a+ ]1 l. i: B& i - (if (game-over-p), n3 {" c. Y! N, n& D7 x
- 'living-room
3 v. X8 U7 S, H' `, g - (destructuring-bind (y x) (board-position-parse cmd)
- W3 ], m+ B, j Y9 {$ R) u) I - (when (board-contains-p y x), R! V9 ^: i% W& N* s! M
- (board-toggle y x); a, E8 @6 _$ Y, q* h3 w+ }2 s
- (session "step" (1+ (session "step"))))1 U6 s" K! g- F; S' ~/ f3 @) H. q
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|