wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。- K" \! n3 e z* `
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
" x2 O. ]8 B; l3 T- <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;">;; 定义新的游戏地图
) j" C( j( a9 G& [9 j/ m0 w* y - (def-map "/game/5x5.el" ; 对外开放的URL
. |7 `# @" Q3 o9 A" d& ] - 'tutorial-room-0) ; 默认的入口
7 I& v( Z% }4 O7 j: ^ - ! P/ m& d. O& B5 W7 G8 g- F6 l& R7 q7 U
- ;;; 游戏大厅
. q3 x: U4 M$ `' H9 h- b - (def-room living-room9 S ~) G5 h1 F& k5 g k$ x
- ;; 进入该房间后的提示语6 Q! g. g& h j, c* Q
- "1. 教程- Y# Q0 q0 U" N) W4 ~( X
- 2. 入门(3x3)8 `+ M' Y1 s* \% J
- 3. 初级(5x5)5 v! k" y4 x# A7 y# [* d d
- 4. 中级(7x7)( z, v4 e+ Y6 h
- 5. 高级(9x9)
$ t* O/ b2 _4 g - 0. 关于作者4 F! b0 K6 Q+ u5 ]! `, j0 e
- 请选择1-5开始新游戏:"
$ y" a, ~4 [6 P - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名* w2 N/ |) s9 Q/ B" v# U1 G" E
- ("1" tutorial-room-0)+ \+ I( |5 I% H( @7 K8 I- E# H
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
6 O: m7 R; {6 y& D+ s, | - ; 相应的返回也可以为函数,动态返回房间名& w" ]/ E- f* {0 g ?
- (t living-room)) ; 如果条件为t,为永真* P4 e# Z! ^5 t" O& @+ x/ w
- ( P1 ~; @; u' @+ w- ~6 N
- ;;; 作者信息% Z( V# ~7 d- k9 z5 G1 n" v$ z$ e
- (def-room about-room
* p! ^4 R5 R2 r: y0 c* Y - "作者:redraiment
& Y9 J9 J0 r! Z8 H& b5 u - 微博:http://weibo.com/redraiment, U% a, w6 V2 Y6 o
- 有任何建议,欢迎在微博或微信上联系redraiment。
0 n9 G2 m" ]$ O7 g0 w( r8 E) n6 n - 请输入任意数字返回游戏大厅。"# K( I' Q' ]% t9 K+ }( I5 \7 t
- (t living-room))
. \" R6 R `2 X" r% [; e
1 Z! Z% V! `" T- ;;; 教程
: J" N9 _( @" |1 [: n6 m% r, G - (defvar *wechat-5x5-tutorial-rooms* 0* p! x% ?; c6 h4 }, H2 V5 X; @, H
- "The number of tutorial rooms")
' e# O2 p v$ C5 q - 8 L# ? a) v; I
- ;;; 简化教程的定义7 E Z: A0 o9 G! U3 x
- (defun string-last-line (content)* K3 j% k% B& _5 O
- "多行内容组成的字符串中的最后一行"& e# E$ `+ y$ O$ S: o
- (with-temp-buffer
2 b* k9 ?7 ^4 N7 O9 s/ H/ k+ X - (insert content)
- l. E" h# J1 x0 d - (buffer-substring (line-beginning-position)" ~9 @3 Z, W. \: y
- (point-max))))
9 q8 s6 L- L8 R - 6 s* S* G9 P1 G( Z
- (defun def-tutorial-room (prompt)( x/ ]( w3 A. C, f3 \, v
- "根据提示语自动生成教程房间。, m4 r1 ~0 L# |3 z' U; \; i: |
# P2 p. Z+ d- q- 1. 提取最后一行作为问题;
; ^; G. x$ N* Z' d R7 T6 w5 w - 2. 分析问题,获取期望用户输入的内容;
+ V6 M- d4 s% t - 3. 定义教程房间和重复提问房间。"
# [& c9 b. ]6 Y& k @3 W2 K - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
( \3 U% I3 i6 q: j/ g" Q - (repeat-room (concat room-name "-repeat"))
$ G5 p+ B8 M* [! Z0 H - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
, m+ J# {( e. s. ^5 ^ - (question (string-last-line prompt))' r" F, v4 C8 [* t0 X1 x C5 S. q
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)1 i& P8 t d; a, j
- (match-string 1 question)))
& \' t: Q R" ?- t) N, V4 n - (doors (if except
8 y4 ?8 f3 I' u( I' t$ ? - `((,except ,(intern next-room))3 w1 Y2 B- B% r6 N
- ("q" living-room); D* U. x3 L( o% @9 _3 q$ M; r
- ("Q" living-room)
1 a5 M. b' C9 V4 B - (t ,(intern repeat-room)))" }* I8 s' L# \0 _* P3 U
- '((t living-room)))))
9 P; r8 \, T* Z: a# ] - (def-room-raw (intern room-name) prompt doors)9 ?. G/ f6 g5 a; j# Q
- (def-room-raw (intern repeat-room) question doors)))
6 q- E5 B: q) P [; D3 }
8 \3 T2 s; B" K8 h1 D: Z4 L4 Y- (defun def-tutorial (&rest prompts)
m# C: H( z4 | - "批量生成教程房间。"
7 q& P n7 o" _& G; X$ r' j - (dolist (prompt prompts). s: o. r) V T" y8 R( W
- (def-tutorial-room prompt)))
6 s8 P3 f# H1 P! R q& N7 l/ f
4 C( s I+ I8 h# X) G- (def-tutorial
& E2 q9 G) G6 n, O" x5 S" s - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。5 d! U; \ R( l; n9 s6 D+ n
- 1. 教程
3 _4 ^; H! \6 E - 2. 入门(3x3)
3 k3 Q4 D7 S% R* B8 ?# K - 3. 初级(5x5)
" @7 K8 t( Z$ l/ A: A - 4. 中级(7x7)
' }7 J4 q$ C8 F; I/ X( M - 5. 高级(9x9)/ V1 Y4 G: T' t2 _5 k# e* v) _$ W
- 0. 关于作者
. g, I! q' @9 @) L, H0 E - 请选择1-5开始新游戏:
6 _: X2 f7 o% ?- N* Y. ?1 l, _ - 您现在正在游戏大厅里。
6 m A& M+ o, m0 \ - 请输入“2”进入入门级房间"
9 c' j7 ]4 _- u- p# Y! Q - " ①②③8 G" o0 E" u1 D- J$ v) @) H
- 1 ■ 5 G# H% e c/ u+ }
- 2■■■- \6 |. Y- n j
- 3 ■ ! Y, v! E2 _5 w4 p6 a, U: N
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
$ X3 E6 U" b! G5 H1 r0 }1 S - 请输入“22”来关闭第2行第2列的窗户。"
+ x' f2 C3 @+ `' ]' F9 J; P - " ①②③
3 |5 m4 {" l% e - 1 9 b, r4 f9 a% Q" [$ ? Z' Z
- 2 ; f8 P+ V" ]/ n- `" C3 @( v, ^) ^
- 3
0 Q) M; s. c2 }; i" {0 \9 w$ p - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。& A( g+ W! {( F9 W4 {9 f
- 请输入“11”,试着开启左上角的窗户。"
7 w5 c/ Z! M0 {7 C r, D - " ①②③
) H4 Q) d+ k; s" M0 U - 1■■ : s0 o. q+ |- W K
- 2■ # r5 I; M6 x4 J. v7 U
- 3
0 q2 ~9 p$ j' L# e) a( C, G - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。) {9 @( u/ q1 N) @3 R7 `3 x
- 请输入“13”开启右上角的窗户。"
2 l8 T* E E# T6 d$ b - " ①②③' |3 A. @! s' l( {) M
- 1■ ■
; g" ?( r7 ^' ?$ q' S+ L - 2■ ■
" {! ?$ A& z; a - 3 2 [6 U3 Z' |) ?9 X. h5 n$ z: K
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
" N1 r9 @& W- Y. _% H4 }' I - 请输入“31”开启左下角的窗户。"
4 v( i% e3 t8 r8 f+ z) B% i - " ①②③
9 r8 o4 r$ v2 A - 1■ ■, L* L+ _, R9 W4 c
- 2 ■: e( x% F9 F5 G, ]
- 3■■ 5 e# K- B9 R( C+ m4 E# d" K% a- J
- 此时,总共有5扇窗户被开启了。! ]9 H, Q4 u8 W) v5 u" G0 D" q/ c
- 请输入“33”开启右下角的窗户。"
* A& f. i a+ X l - " ①②③
6 y1 {* p7 ]; B, I" l9 N - 1■ ■
0 T. x" ^2 e& A" w; J" \$ Q - 2 & U1 }% ^8 u6 D6 L( k
- 3■ ■. @3 w/ U& O) i; X' A
- 现在,只有四个角落的窗户被打开。8 c! X) l7 k1 S; ]3 b& P" G
- 请输入“22”完成最后一击!"
5 w; Q1 j' b% @) T' c3 V$ t - " ①②③
s. P2 D, B( z$ D6 m* l; i9 s% K4 L3 C - 1■■■
, l& m. U3 k& b G; {- g+ U - 2■■■
# Q4 n7 d* q( \: K( Q& }8 y% c - 3■■■
7 O, V. ]3 f' a - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
; e- s/ @4 }/ `' U - 1 D2 P$ `3 y+ H/ U2 J
- ;;; 棋盘
. r3 C' ?2 K8 D- q1 L - (defconst *wechat-5x5-white-chess* 12288! R8 d/ u: l+ n" n
- " ")
$ u4 h, p& Y7 [: ~( v' q6 T
9 f( Y- {! ?; O7 o4 a4 U- (defconst *wechat-5x5-black-chess* 96327 s& R0 \1 }; u/ H" k4 E/ A4 V
- "■")' `1 a" T; E7 e0 x2 w* G: f2 r. f
' [8 G' s$ n$ O" O6 T- (defmacro with-board (&rest body)' s4 K6 X( y4 V/ R+ V
- `(with-temp-buffer
- q0 ?5 P6 N) q+ o3 X& C, \ - (unwind-protect
! m' G% m# W. j$ z# E5 o - (progn
0 e$ K/ T+ P G8 J9 m3 {) v - (if (session "board")& h9 E2 n, ^: d7 [ d' E3 J
- (insert (session "board")))' v9 ^" i) w7 }1 J0 B7 W
- ,@body)
( a1 L' p/ H$ s8 W2 t - (session "board" (buffer-string)))))
2 Z2 d/ r& [" l/ p$ m4 [- p J
! C: u& A. F0 n2 g( z: d- (defun board-init (size)
9 U# M3 z1 t$ o o D( V - (session "size" size)
# |% S! i6 a$ J8 ~5 Y - (session "step" 0)+ B7 B- l! y; D
- (erase-buffer)' G# s8 e0 R5 t+ `
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨")): T' p% G4 q5 K1 t" ]6 k) f
- (dotimes (row size)5 x$ d& W0 m$ x$ g! K
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
' Z- T4 K, e6 c0 ~: u4 l
" D S- C! M. L; Q7 b- (defun board-contains-p (y x)6 c0 e) R3 G+ N
- (let ((size (session "size"))), o0 r. g. k2 Q$ F% d( X4 u* P- n7 C
- (and (<= 1 y) (<= y size)
: `4 c; O) s+ K3 T# l% K, l - (<= 1 x) (<= x size))))
7 s& |; Y& o0 ]9 I7 `0 {7 {
8 h+ K2 j% N; l$ V- (defun board-toggle (y x)6 D% L( {( Q! B7 I' H) D
- (when (board-contains-p y x)
- Y1 \$ n* D" T( C - (goto-line (1+ y))
. R8 q8 |8 |6 b5 c- L, q - (beginning-of-line)
. C: h) N+ x6 k7 `$ [/ z6 T j - (forward-char x)
( X1 p' T9 ~& p* m - (insert (if (= *wechat-5x5-white-chess* (following-char))$ Y7 f" `* K5 v" n/ Q' k! i$ R/ |
- *wechat-5x5-black-chess*; W$ b7 W* V' u D& M6 V
- *wechat-5x5-white-chess*))
' M0 R# U9 Z3 G) T; @' ] - (delete-char 1)))
6 G8 v9 B5 i S# j' D( X* a
) } L* Q& ^6 X2 N3 y6 i4 m& S8 r; `- (defun board-put (y x)
3 {% y" D9 Y F! M. ` - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))2 j1 Q, R$ c/ V5 n0 H2 Z
- (board-toggle (+ y (first dir))
2 G9 c6 U9 d+ v% C5 p: ? - (+ x (second dir)))))4 l' ?% j7 f5 x* K
+ u7 b. ~: ~" w* Y/ d- b' U. M5 L! S- (defun game-over-p ()5 x% ^# J1 h+ G Y
- (beginning-of-buffer)
+ W& C! l% l' i - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
1 e# H! a0 P" m- ?6 d$ e4 J6 Y2 ^ - : v; ^- j% N M& V
- (defun board-show ()
- s& B" [) s5 C: X. S- u1 z* f - (with-board
; x0 C5 J+ b, X0 f - (concat (buffer-string)4 _5 G+ j/ Q+ }+ ]' q% I
- (if (game-over-p)
$ h d0 y F2 k- {8 M - (format "共%d步,输入任意内容返回大厅" (session "step"))
9 I3 ]: d0 }- x7 h - (format "第%d步" (1+ (session "step")))))))2 [6 r3 ~8 U; H3 z
- 2 p+ D6 ^& ] l% L+ |7 F
- (defun board-position-parse (cmd)9 }2 F; _" w7 w5 S: L
- (if (= (length cmd) 2)
6 G' `0 M' \; H9 x* r9 H8 E - (list (string-to-int (substring cmd 0 1))
) G \; f- a n" N6 q) D0 O+ l# Q - (string-to-int (substring cmd 1 2)))6 Y9 Q* M9 P& M1 H. d: t
- '(0 0)))
/ S2 W2 s1 s. x- ?( P( g( q
4 E& X8 [/ q0 d' k# f6 E" j- ;;; 游戏房间1 R% n! _" w4 D/ [
- (defun game-room-init (cmd): E2 b0 }# b: C" I" a2 ?6 ]8 o
- (let* ((middle (string-to-int cmd))
" d8 }" w4 {: x. {4 P O - (size (1- (* 2 middle))))# u+ z! _7 ~6 A/ p2 B \9 A
- (with-board
2 B M! a1 R" w$ w; D - (board-init size)
& m( X/ `( J: D& [4 W+ P - (board-put middle middle)))! P1 R- c3 [/ E3 E
- 'game-room)
" K5 {! [7 z# @. r - : H7 }; i. ]9 g; _& k
- (def-room game-room
: B1 F( {: T+ u2 o - #'board-show' A0 B2 F0 H7 e5 j2 |! H+ \( F
- (t (lambda (cmd)
/ c+ C2 u( p6 t4 J' B! m - (with-board. W& E7 r$ V( i+ J& m: }
- (if (game-over-p)
3 E8 m! k% e* M. H b: w' ~ - 'living-room! }1 k9 H- P( h& L+ N3 |
- (destructuring-bind (y x) (board-position-parse cmd)
# ]8 `8 F& b0 h& p& ]3 k - (when (board-contains-p y x)
( x6 N# n' @; Z - (board-toggle y x)& I! r' n8 \2 h
- (session "step" (1+ (session "step"))))' @7 T8 E" V8 N8 A" k: P7 q) ?
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|