wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。4 n% P0 z8 a5 Z. u
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
. U$ d% R7 Y9 a- <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;">;; 定义新的游戏地图9 G. _$ q/ Q7 |0 z, ^! B
- (def-map "/game/5x5.el" ; 对外开放的URL0 `' V5 ?+ Y2 {( z0 M# s
- 'tutorial-room-0) ; 默认的入口
1 Z% \* P9 K; b1 \; w) l- u - & Q* r" F% f! J- J- W8 W3 _
- ;;; 游戏大厅
2 L: k0 u3 r. n) B5 S1 F - (def-room living-room% c* Y% ]- u+ }: n% o
- ;; 进入该房间后的提示语, w% B5 e) [) h
- "1. 教程
, r- M* g$ |. G" U1 b' j) W - 2. 入门(3x3)! C7 ~' K4 [5 E( G' V5 r
- 3. 初级(5x5)
3 m$ O. w4 r; I - 4. 中级(7x7)! W" Y; {8 |# \- J7 k: t# c; U3 {
- 5. 高级(9x9)
7 ?- Y2 K5 S7 ^ ^3 | - 0. 关于作者* J1 N5 p! i7 _& r" m0 g) j6 v
- 请选择1-5开始新游戏:"
7 |; X5 {, L4 p u - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
& L5 s/ @- L ~( j; j - ("1" tutorial-room-0)
2 {- r( ~% W- b: d" r) W - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配. D7 l* W9 ~0 i+ D0 m2 {8 T
- ; 相应的返回也可以为函数,动态返回房间名: E' E/ Q3 g' Z, e1 F8 [' q
- (t living-room)) ; 如果条件为t,为永真
( I/ S; |) T+ \0 o6 F0 Q' |6 \ - ; ?% b+ ]. Q9 K9 M
- ;;; 作者信息
7 q- i: m2 V+ L6 q' X3 f3 }4 ` - (def-room about-room
* H6 X5 Q; W9 A3 q - "作者:redraiment
3 W4 A4 h$ d0 @) H5 U- E( m - 微博:http://weibo.com/redraiment
7 s$ h3 O' A! n& @ - 有任何建议,欢迎在微博或微信上联系redraiment。* S2 b5 w. ]2 u0 D& S
- 请输入任意数字返回游戏大厅。"# F$ K) A1 {. n* P g( t# H
- (t living-room))! X; L5 r) e5 s' {
- p$ F2 y; E6 h3 K6 P# T3 j- ;;; 教程
. g6 |6 ^6 I7 k0 o7 Z* ] - (defvar *wechat-5x5-tutorial-rooms* 0
( w. a, h# M1 D0 p0 f/ G# p - "The number of tutorial rooms")
2 k% ?5 B- b6 ~; {( q
* k9 U y0 l) ^) ?1 a8 B' y' ]* ~- ;;; 简化教程的定义
. ^6 u% G* M& e. } - (defun string-last-line (content)
* P$ ~5 d; e3 p! O: h6 t5 [2 ] - "多行内容组成的字符串中的最后一行"# a' G5 R$ I5 O# Q- f
- (with-temp-buffer! s% G: B* s) l- `( `
- (insert content)
4 ^% M4 m0 z9 M1 @' W3 O' i - (buffer-substring (line-beginning-position)5 c7 r. w% {' @6 \- m
- (point-max))))
, W5 t5 B* o" R- ]
: q7 s' |$ e$ e5 @( K% Q4 I1 p8 d- (defun def-tutorial-room (prompt)
' x, s4 z) M: I - "根据提示语自动生成教程房间。" W g+ [! [ ~
- : ?( X$ ]. {. S& [8 _9 ?) R/ A
- 1. 提取最后一行作为问题;
8 A4 m% A4 e, y! o( v - 2. 分析问题,获取期望用户输入的内容;
$ {) o! k2 a5 s% z. A; @ - 3. 定义教程房间和重复提问房间。", O9 q; `7 Q' S' j2 C
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))# Z( b6 e. s( W3 m( E! G1 a/ g
- (repeat-room (concat room-name "-repeat"))9 J( Z/ _6 F# n& F' G6 C' z2 A+ M# Y) w
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*)))) d, _# t* R1 `. R4 s& a8 A: a3 a; U
- (question (string-last-line prompt))
* s$ X+ i9 s# T F - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)" D, u* M! U) Q$ A* m
- (match-string 1 question)))% x- L4 \+ M; d0 p2 l2 [0 L
- (doors (if except3 a) _, V* d$ L1 M k' y
- `((,except ,(intern next-room))
: y5 e$ s* o; \" d+ \% P& h% J - ("q" living-room)* [- \, m# J! r1 ~* D5 q0 k
- ("Q" living-room), j# N- W1 H0 |7 a# ~( @4 x
- (t ,(intern repeat-room)))9 E6 \. i& `1 P7 p5 @
- '((t living-room)))))
9 R- M' j" t% B - (def-room-raw (intern room-name) prompt doors)- ?9 b' o# l/ z
- (def-room-raw (intern repeat-room) question doors)))
/ t4 y4 ` E' B# {, R
5 Y2 U6 S8 z. L. m0 l* ~" |- (defun def-tutorial (&rest prompts)
& A5 z* S- T- S0 e( l& j - "批量生成教程房间。"
# j2 N7 B- z! d3 A+ L - (dolist (prompt prompts)9 X4 S: e5 N/ b0 E4 y2 s
- (def-tutorial-room prompt)))
1 l) c6 P6 v0 l1 a5 N+ T
8 Z. e4 j) @ L- (def-tutorial
8 J2 D; i! A G5 F) G( J" T - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
7 t9 B. m9 B. z# w/ F - 1. 教程% v* q6 w5 z# d: L1 X' I! ]& O6 n
- 2. 入门(3x3)
" m+ {! o- n* q4 E6 ? - 3. 初级(5x5)9 J; z# M2 t4 Z7 R* L
- 4. 中级(7x7)- G) _8 j: r) O6 H
- 5. 高级(9x9)
. o+ w/ F' k7 q! }+ |" B5 w; u - 0. 关于作者
2 K3 v# r, `/ N' R7 H& V - 请选择1-5开始新游戏:1 O/ ` U# }/ I7 N; ^
- 您现在正在游戏大厅里。" U+ H& Y7 U# Q! p0 Z m7 F
- 请输入“2”进入入门级房间"
6 Y# [; t" v( T9 q - " ①②③
& ]/ _/ }- i9 R+ T5 c$ J$ [. t* F - 1 ■
/ Z5 D: p2 T- e {. B" z% ?" S - 2■■■
9 X9 j# I: I R& ] g7 a - 3 ■ + W2 r9 s+ L) X, @
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!5 d& A6 {9 v8 b+ o" F
- 请输入“22”来关闭第2行第2列的窗户。"
: ]3 G8 _5 e" N - " ①②③; t) I& w F4 `1 h( b
- 1 9 S D" m. {6 [; ]$ l( S3 p
- 2
. g a9 i4 x* l: a: d- l7 |( M - 3
0 Z" }9 H& N1 w) Z - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。- t6 E4 ]% A6 k1 j# }
- 请输入“11”,试着开启左上角的窗户。") y1 g8 [; b+ ? l
- " ①②③
3 H0 H( J. f8 ~6 a( c0 e' \5 l - 1■■
' @. F( O1 Y7 R% _ - 2■ 3 \/ b4 V9 b2 `, U) N
- 3 9 j: n% z% N$ c8 y' G) j4 ?6 l
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。" I& n" O1 A9 b( U
- 请输入“13”开启右上角的窗户。"" |0 E1 ~8 {8 f8 ?& T+ L, B& E
- " ①②③
% x( f9 M5 j! D7 t - 1■ ■4 T9 a1 l( T# W: l3 U
- 2■ ■
/ y8 w1 y5 z* Y9 M$ `1 k& q - 3
: @* Q$ S$ a9 R4 j - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。; c1 A. D3 ~7 @7 [+ h; y
- 请输入“31”开启左下角的窗户。"( n2 l% M G- J, P; E( Q
- " ①②③
: q( Y4 x0 v2 W& ? - 1■ ■( z. F/ Y8 p0 E- ]$ q
- 2 ■
D9 }( Q+ p. D6 x - 3■■ 3 v2 A8 I8 Q' [; J
- 此时,总共有5扇窗户被开启了。
# R6 d6 Z7 C; i( D - 请输入“33”开启右下角的窗户。"" U5 C# J( d9 S* y' y# `
- " ①②③% Z) a# p7 K: z0 _
- 1■ ■
: Q+ h/ r4 u; L/ B- ? - 2 9 P- x* q8 ~! [( p! w
- 3■ ■
: n ` {- |" f* R1 q8 p. b - 现在,只有四个角落的窗户被打开。
2 d) a3 k! C6 n! h* W; { n& J - 请输入“22”完成最后一击!"; i6 ]. m7 W, G: L1 {8 i" p# x9 M
- " ①②③
, k7 I. x% b8 d( N - 1■■■3 ]/ o- @- ?) }4 o
- 2■■■& E# A D8 q& v0 z0 B
- 3■■■8 K9 k9 _4 [7 ^9 S' D+ d
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")6 F* [7 S8 c9 T& d$ i8 V
- 4 y( A& Y' _# A% V1 }, M
- ;;; 棋盘6 q5 R0 s3 W' o# `% E
- (defconst *wechat-5x5-white-chess* 12288
8 J2 ?. f: M6 g - " ")
+ g' e9 @0 C$ f0 o3 U
% b7 Y) ^8 d# h% H1 l& s- (defconst *wechat-5x5-black-chess* 9632
2 ? ~4 \8 }) a+ k( f - "■")
( ] O0 A: K9 T" u7 }; F
5 ?1 \4 s" `3 D( ?# p- (defmacro with-board (&rest body)
5 E+ y- P+ w6 Z - `(with-temp-buffer
0 J% ~& @6 d A - (unwind-protect
# A+ }( D8 @' M2 J1 u0 V - (progn* K8 G* k' _# y/ y. T; h
- (if (session "board")6 r2 t7 P0 S7 v- L$ ^ p
- (insert (session "board"))). _- [8 P" X- D
- ,@body)" f# c, d6 p, x( H( x
- (session "board" (buffer-string)))))+ l5 g0 l# A* l, j
- , v2 ^/ _) _/ |3 N4 W+ T
- (defun board-init (size)
+ j: i& G# u4 k: d1 e - (session "size" size)% a8 e8 U0 m/ ~
- (session "step" 0)$ p/ m" u" k8 g3 L& `2 T
- (erase-buffer)+ X0 C6 V: }6 d/ Z+ o$ w
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
8 H# j: `& g# f+ N5 ` - (dotimes (row size)
0 [! z$ O9 j _& p/ x - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
5 f* b' K/ u8 T( \3 m$ u+ { - 1 C5 n( w" X2 b# y; A. R0 ?& f
- (defun board-contains-p (y x)
4 {4 H" s8 ^" D! l+ i# G( M2 @, ^. [ - (let ((size (session "size")))
( C% m$ p: ?# D2 n& t1 x) T0 H ~1 i - (and (<= 1 y) (<= y size)) j( l4 z# Z" s2 T n2 f8 j6 r
- (<= 1 x) (<= x size))))
, e) P1 W. J# T' {& g* R
5 p3 ^1 ~- l- H+ t4 l; {4 K4 n- (defun board-toggle (y x)
- |/ T7 O* T9 u& g3 F. n- I Y - (when (board-contains-p y x)* |- E; Q `; v% q" p6 x
- (goto-line (1+ y))
( S8 K# e/ q% c5 r - (beginning-of-line)5 v3 o8 k4 E( @/ o1 J/ r
- (forward-char x)
- x! L$ _' K% {$ t# G6 ~ - (insert (if (= *wechat-5x5-white-chess* (following-char))
9 @! D0 n6 P' N. U9 ]9 L" w7 Q. K - *wechat-5x5-black-chess*- M, v; Y2 m$ m
- *wechat-5x5-white-chess*))
. x* f/ \! |5 w' I - (delete-char 1)))
9 L6 }0 I6 H" o
( M5 H. Y I: E8 s! Z9 c- (defun board-put (y x)
" @" j& e$ \6 z( t# }# o - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))$ l2 g2 v5 s4 r
- (board-toggle (+ y (first dir)). d, d( x' N* }8 f
- (+ x (second dir)))))
4 s9 J- C7 d7 \- a1 B - 1 q) \$ e( y% c9 A6 n& g& r
- (defun game-over-p ()
P+ R. x: g: m: ^, s - (beginning-of-buffer)
# w: _% U$ O6 n3 z1 M* e - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))8 F* v) h3 S% B- U- w, |0 h
; |( \# Z, M n; Q- (defun board-show ()
6 Y* ~ s+ g% n. _! Z) j, g+ {) d - (with-board
% ~8 t+ ?2 v0 B% F( m% _$ A2 j6 ^6 c, l - (concat (buffer-string)
: f/ R& _, U5 P5 B - (if (game-over-p)1 d8 Z( w% u' q! d
- (format "共%d步,输入任意内容返回大厅" (session "step"))1 [6 b1 Q' u& d: x
- (format "第%d步" (1+ (session "step")))))))7 W9 z7 D% q( P1 m$ c- s4 v
- ; |/ U- ~ l) `4 E& x. z8 y( ]
- (defun board-position-parse (cmd)( T, q( p# e; P: @5 A
- (if (= (length cmd) 2)
6 \5 ^$ }- L# y2 S3 P& C% g - (list (string-to-int (substring cmd 0 1))' T5 Z$ M5 I9 `( Y# m; j
- (string-to-int (substring cmd 1 2))), E! N4 y. I# N5 I$ Q
- '(0 0)))$ a' N( D. d& q. }
- 3 W6 v0 H' [4 _4 g/ w: g$ K6 N
- ;;; 游戏房间
+ {- w5 j* u& d, ^" t- ~ - (defun game-room-init (cmd)2 R9 |/ n* X8 ?* Q4 N$ m9 u4 A
- (let* ((middle (string-to-int cmd))
0 C# n/ j" F8 B7 O - (size (1- (* 2 middle))))1 v9 Z9 f' p: M$ s5 P" b! a/ h
- (with-board. @. J5 |; ~$ F9 B
- (board-init size)' s+ x/ c+ f: l; C B {, e: a
- (board-put middle middle)))1 a9 c5 A$ g3 W( K% m
- 'game-room)/ J6 U3 P8 `8 u
) y$ v0 L# N2 ]! n- (def-room game-room9 W0 \0 x6 @: c# O4 z/ |* h% b
- #'board-show8 h! s) M0 Q' K! X
- (t (lambda (cmd)
3 ~9 K' ~2 b* c - (with-board
( b+ w* `: t: d! z. z j4 l - (if (game-over-p)$ ?# O5 M* a0 J0 K# q
- 'living-room8 U* h- k1 Y! \) \6 Q7 N. e
- (destructuring-bind (y x) (board-position-parse cmd); V5 l+ a; B5 A+ f* d) i, p
- (when (board-contains-p y x)& b: u$ d o5 u( U8 ~
- (board-toggle y x)
* F* s/ I# m: H( p7 ] - (session "step" (1+ (session "step"))))
5 _, F& K5 D: I+ a/ i% Y - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|