wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。. i' V6 b! @! P3 l7 B- o
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
+ L6 T4 e Q, b! U, V- <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;">;; 定义新的游戏地图
1 j3 X8 q* v5 | - (def-map "/game/5x5.el" ; 对外开放的URL
7 B; T, p0 u* v- Y3 f' x& \- w7 @4 Y3 Z - 'tutorial-room-0) ; 默认的入口
9 X. M( ^# @# t8 y
' b8 K5 X) w' x0 h- ;;; 游戏大厅
8 Z5 q) P: C& {; N3 `" }$ F - (def-room living-room( J# f8 i9 s% A# L! L
- ;; 进入该房间后的提示语, e4 ?9 v m) Y1 b P! ~( H$ G
- "1. 教程* d l( I" X9 e* ` h0 h
- 2. 入门(3x3)% z' J X4 R. t) H
- 3. 初级(5x5)
7 t, D! f1 w0 X2 J7 `# J* j8 G% n - 4. 中级(7x7)
) u6 @4 |/ U9 R+ B9 Z - 5. 高级(9x9)* m* {# X2 F: l: p
- 0. 关于作者) t1 ^/ i2 ?( u% D: B6 }% z6 T
- 请选择1-5开始新游戏:"
- i+ v3 S/ l5 f B- I6 @ - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名# I; e2 o3 l& ?: l9 Z) i9 E: O$ Y
- ("1" tutorial-room-0)
6 ?6 U: E* U" H) i9 I4 X, |8 n5 f - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配4 G: a- G3 n1 h' J j
- ; 相应的返回也可以为函数,动态返回房间名+ ~& Q2 S9 E9 D6 s6 Z
- (t living-room)) ; 如果条件为t,为永真' s! U! \ _+ Y- @% E0 t
- : G( d5 m0 @& T0 N* c8 X
- ;;; 作者信息
$ h+ L$ Z( \: C7 Z6 J- X - (def-room about-room* W( G9 N" e! F, i. F- M
- "作者:redraiment
% `3 x" J+ c: q# w0 \ - 微博:http://weibo.com/redraiment) z" F& M E2 Q7 O3 m
- 有任何建议,欢迎在微博或微信上联系redraiment。; A1 @9 B- Z, }/ `+ ?$ X$ S) q
- 请输入任意数字返回游戏大厅。"( d! T! \; c4 s* ? F/ C
- (t living-room))
% R6 `, E4 c6 e" h
9 [; Y: Y/ H9 i) U, ?" e- ;;; 教程
/ s( R; D2 |8 x( Z9 i0 n$ s - (defvar *wechat-5x5-tutorial-rooms* 0
# P9 K' p1 u4 L5 w9 z - "The number of tutorial rooms")
8 G* F$ g5 k: ~4 _$ p; O0 v - 7 {0 o2 V6 T9 w0 t* s4 |
- ;;; 简化教程的定义
Z5 P" I6 m( [8 O4 Q+ [ - (defun string-last-line (content)
6 J* ]) K# b: c - "多行内容组成的字符串中的最后一行"7 r5 c$ z, H. f" A" x) z% W
- (with-temp-buffer9 G0 Y" F& X" @7 ~( ?0 t, Z
- (insert content)- n- e- B7 _6 r
- (buffer-substring (line-beginning-position)
0 B" m5 T* Q: L5 d" V - (point-max))))
+ Z, b9 ~( w4 G$ J - & U9 c/ S2 h. O2 s% e
- (defun def-tutorial-room (prompt)% c. t. ^) j9 b7 \
- "根据提示语自动生成教程房间。
" Y2 `2 ]3 `9 x2 v8 c1 P1 M) Q - ! l( }1 n# W4 d* J
- 1. 提取最后一行作为问题;/ c0 v+ n1 N1 O' s0 ^2 r$ ~6 y
- 2. 分析问题,获取期望用户输入的内容;9 t( _$ Y& h7 o: ~7 w
- 3. 定义教程房间和重复提问房间。"
. W* u. Y6 d* v- W- L: B - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))! h: a! W+ _1 w8 [, Z: `
- (repeat-room (concat room-name "-repeat"))
2 g" `- s8 K6 i# ] - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))/ [' h5 H+ G3 S* J" ~0 b' _3 A" g
- (question (string-last-line prompt)); U8 T/ \5 w- i6 l z
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
* N8 z) o+ H/ f" X9 c, {9 ?7 N - (match-string 1 question))); N1 E8 i% h" |: q- _3 ] d0 L
- (doors (if except
2 I. B$ E8 n1 b( s! r" ^- ` - `((,except ,(intern next-room))& q8 G, |8 C m
- ("q" living-room)6 Z" f1 p6 Q3 \( |5 x: V+ l, ~
- ("Q" living-room)
# ^7 {) k: X5 D0 Y - (t ,(intern repeat-room)))
7 Z" Q+ |( m$ y* d1 P* N - '((t living-room)))))( \; j# V& n8 _3 s- B
- (def-room-raw (intern room-name) prompt doors)
5 D9 `8 D# o8 ?: s9 N# K - (def-room-raw (intern repeat-room) question doors)))* M- p! Z( X4 V; o0 T, T) e Q
- ( k9 v V$ r8 s0 k* {
- (defun def-tutorial (&rest prompts)# Z0 A0 W& s. C# f- r( A( w _% s
- "批量生成教程房间。"0 l1 c; Y4 ]" b$ _0 b' c! ?. k
- (dolist (prompt prompts)0 u. l* S: i2 G/ ]
- (def-tutorial-room prompt)))
]; U% C8 x2 o- U - ' {+ _7 E, K+ M* B
- (def-tutorial# p ^. X+ {' Z M- D, }! H
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
: G: c' b* _ n" c - 1. 教程
, ^' R8 r, j8 F1 T: w$ r - 2. 入门(3x3)
$ L! |; l9 r B$ [. w - 3. 初级(5x5)
3 A' V) i/ H' \* h" z - 4. 中级(7x7)
: c% }6 s. L. o9 _% h- z6 Y - 5. 高级(9x9). I: z5 [. n7 |# z3 W% P% n) i
- 0. 关于作者
5 r3 Z! @; n% P+ s/ Y0 e4 l( A - 请选择1-5开始新游戏:
- E! z2 U0 ~# ?/ B3 g- N V$ Q - 您现在正在游戏大厅里。
3 E5 V5 D: t: X8 y - 请输入“2”进入入门级房间"
: l2 o/ m, S8 U+ z( X& c - " ①②③
1 o6 V& _$ E% z& E. }7 W$ Q p# K - 1 ■ ! [5 x; O) s& }2 p/ E" O
- 2■■■$ I. p! [6 v- z( M. f, ?9 Y
- 3 ■ 1 M- c( H; I' ^5 E
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
7 M5 b9 ?( Q/ m0 _' l& e/ a" N" O9 Q! C - 请输入“22”来关闭第2行第2列的窗户。"( p# r% T' |" ^4 S
- " ①②③
\4 H' v/ \$ L, r* J ^/ V3 l - 1
) y2 F7 C! o1 K% q9 f8 @. F- M. w- D - 2
" ^8 v" }* G" d$ y - 3
; H+ h* u: ?6 n% V! t) X - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。/ K+ e% N" q4 h8 @7 [, v
- 请输入“11”,试着开启左上角的窗户。"" g& Y( K6 D, R3 ^
- " ①②③
8 E% n, Z' ]7 ]- V% K - 1■■
* S4 W* A f7 B0 W9 U, n - 2■
2 z; W) {: O1 a- x; ` - 3
6 ^7 b3 L4 r1 ~7 q0 B3 v& O p - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。7 I; m6 u; _7 d) h8 c
- 请输入“13”开启右上角的窗户。"
; h9 n" d/ T$ e - " ①②③
2 m" p7 k" G/ N: v3 A# e - 1■ ■7 V+ y; j- z) c1 F# p7 y
- 2■ ■
) g0 n# T. o+ y, X - 3 1 b% E" E0 D. G2 b6 h5 K# {$ h1 W ~
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。( A9 T, d/ |7 ^* N0 @" l! d
- 请输入“31”开启左下角的窗户。"
$ V" y/ q& B& t6 h - " ①②③. p5 U8 b3 Y4 L2 F$ l9 K6 P9 Y
- 1■ ■
4 m. e1 L3 d, I1 h6 X% } - 2 ■
. c% [( U- }: o# C4 u! {1 A/ N0 q2 b - 3■■
5 U: G2 z1 \# n4 R( z* n2 L - 此时,总共有5扇窗户被开启了。
# V, @: _8 K; y - 请输入“33”开启右下角的窗户。"
7 V) o" H+ y1 V$ V! E$ S1 }0 z - " ①②③2 `$ R' @3 Y. U f( ]0 o9 E% s& F
- 1■ ■! J% g) V' K& F. f# ?( ^! j
- 2
- q3 d' r. R; r9 F8 t( `4 G - 3■ ■
8 s( n) e+ K; O, o' b - 现在,只有四个角落的窗户被打开。
4 P: T u4 |: n; K - 请输入“22”完成最后一击!"
: }+ ^! i2 \* Q+ X+ x - " ①②③
" G6 `, o+ g) O; G: `5 A5 c, r - 1■■■
/ Q& Z5 g+ V8 r2 [ - 2■■■$ N: X# ~$ i! L: g
- 3■■■4 P3 s; o; u5 j8 r
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")1 {# \ A/ [% S( t$ w0 P7 G. n
- - a6 j) m0 d6 S% Y7 z6 l9 Q" i
- ;;; 棋盘2 V0 M4 `& b0 ^5 V7 O
- (defconst *wechat-5x5-white-chess* 122882 B$ N; I, d( E- q) L- @
- " "). E i% w4 `. w4 q ~
- 3 X$ f* ^8 H6 w U
- (defconst *wechat-5x5-black-chess* 9632
# _9 v6 B; C0 ]3 C' V4 N - "■")9 J6 {3 s I" p9 M, P# T
! g. {3 N5 u1 S* z8 P% v- s& M- (defmacro with-board (&rest body)
% `% \# e2 E+ m/ S! S - `(with-temp-buffer$ M1 P$ U& v( Q7 p7 w1 K) z) j
- (unwind-protect( p, y( V0 R+ v
- (progn$ W1 h6 t0 I* |
- (if (session "board")
" y+ @- J9 \2 O: g - (insert (session "board")))
# T, w; S/ b" N" e - ,@body): X5 Z" g& s' }) Z2 s8 j
- (session "board" (buffer-string)))))- {( V, v8 }& w$ S$ x
* C* h* K0 }7 a' @: X- O U7 K- (defun board-init (size)
" {2 I3 J0 a3 I' X/ f - (session "size" size)
( O+ w( O( e2 J2 v k: z - (session "step" 0)
+ W1 ?' j3 W! u6 W - (erase-buffer)% O% t4 `! D6 C+ P1 a! U* U# v' d
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
& Z: S/ F1 y- a - (dotimes (row size)
8 A: f# K! Y: X0 t - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))( ]5 O) J @2 M' u8 y$ A( r- E4 P
; n7 c( \$ s q5 N8 [& ?* C- (defun board-contains-p (y x)2 M7 f/ C8 V5 x( G8 p+ P/ y% i
- (let ((size (session "size")))
. o- n! L4 C0 P m" ]: d2 ?2 ~ - (and (<= 1 y) (<= y size)
; W' b7 W4 B8 R - (<= 1 x) (<= x size))))
8 \9 _) c3 M9 J - $ _9 @1 r3 K' ]8 z: F
- (defun board-toggle (y x)# v5 q+ o* o) D' V# O, l' Q
- (when (board-contains-p y x)/ f7 @( j5 J$ j: r2 w8 g! q. [$ Y
- (goto-line (1+ y)). ^# {" R m1 Y( L0 r
- (beginning-of-line)
! R" P) a! ^; R8 _9 U& W& z) I - (forward-char x)
: Z+ }9 G z6 B" {, h - (insert (if (= *wechat-5x5-white-chess* (following-char))
0 u! ?% I# {1 x2 ]; K' ] - *wechat-5x5-black-chess*/ Q$ G) {! L+ o5 A; B% n
- *wechat-5x5-white-chess*))
: B1 k; K# J) U% K6 x8 M+ M+ P - (delete-char 1)))3 t6 Q, p" d! ]* J; K1 g, ?
- # J0 l+ Y8 R; w* Z" j* Q* w3 t
- (defun board-put (y x)
) G# t- K2 |) ^* Z: ` W7 _) ] - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))2 E; P M) j- ~3 u+ o9 }5 j
- (board-toggle (+ y (first dir))
g( Z8 H8 B0 p; K - (+ x (second dir)))))+ a7 C; P, |5 _
- - T6 O0 [6 c* ~+ Z+ b
- (defun game-over-p ()
: c, O# r7 n: b - (beginning-of-buffer)) b) n+ p, I" P7 M- a0 X
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
# r. ~" F( P7 `. f% G! W
; s- K5 N3 F2 {# D- (defun board-show ()( W. X' F" h3 B1 u0 [; o
- (with-board
7 U# B* Z0 C' t - (concat (buffer-string)+ P% `4 V' e, j/ K$ y+ W- h
- (if (game-over-p)
" s# h6 g6 E; c" v1 H5 U - (format "共%d步,输入任意内容返回大厅" (session "step"))
- \& w( T1 ]0 {! {7 j1 L/ \5 C7 M - (format "第%d步" (1+ (session "step")))))))3 u, o5 i# z! y% y3 B+ \
' b1 \& f& c( D0 e" v: l- (defun board-position-parse (cmd)& _1 O# H7 M& b8 l
- (if (= (length cmd) 2)
9 q7 k1 h: j* M$ Y+ f - (list (string-to-int (substring cmd 0 1))
0 H7 `" w8 T! g - (string-to-int (substring cmd 1 2)))
1 e) S# T3 l/ q4 k% i3 g - '(0 0)))
5 Q* P0 B3 v& X
( O1 a; V2 v8 `3 O# q9 E) A- ;;; 游戏房间
* l0 p% R* m5 D7 w - (defun game-room-init (cmd)- E. J9 r5 Q1 h3 @ I4 a
- (let* ((middle (string-to-int cmd))" _ m$ _( g7 l3 o+ I6 v0 M
- (size (1- (* 2 middle))))
! w7 s5 u6 F# R+ M6 l" t - (with-board3 B- p1 t/ }7 H; N; p! q- p
- (board-init size)
) Q; p9 Y# b- f( K - (board-put middle middle)))( s0 o$ P& H6 }
- 'game-room)
' y5 O. m$ I1 d) i1 i& g+ I- X - - c0 p5 \0 o9 I( J' |9 T: ~
- (def-room game-room( a0 o `) s. p
- #'board-show
9 A: L6 w) ^, W7 o: o1 ^( h8 D - (t (lambda (cmd)8 c$ S" L+ ?" p! X2 V* |% t
- (with-board' H- [! U5 W1 }6 c9 P3 z
- (if (game-over-p)
; G' N: X1 D; r4 r - 'living-room
, M f: M5 L2 A) P' h - (destructuring-bind (y x) (board-position-parse cmd)
1 ?/ |' }) d6 ?$ Z7 O$ ] - (when (board-contains-p y x)1 d' s2 p* ^# k' T2 D" D
- (board-toggle y x)
# f c7 r u! l. X( h - (session "step" (1+ (session "step"))))
% E5 n' J4 e P" s5 a; f6 k - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|