wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
# d" z8 v; O p, q) [( x借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- # a8 O" o8 n& K7 ~
- <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;">;; 定义新的游戏地图5 x W2 N) n; a7 i5 N1 u
- (def-map "/game/5x5.el" ; 对外开放的URL+ |5 `4 m( S' L3 c5 X) D9 o
- 'tutorial-room-0) ; 默认的入口
9 m3 C: u* H: Y* b0 w
. }! c2 u1 a1 r9 ~1 p- ;;; 游戏大厅( d" P. q0 `1 Q. l
- (def-room living-room
1 |3 P. e3 l8 Y; d. K - ;; 进入该房间后的提示语 w" x0 o2 y6 a7 @; K5 u8 W
- "1. 教程
1 N5 n6 [4 }2 A7 E) D+ s* ^6 G - 2. 入门(3x3)
- J) _6 u0 F3 k" I - 3. 初级(5x5)
3 m* O. h( e9 \3 w8 _ L0 S% Z& O - 4. 中级(7x7)
8 d! G' c( S* i6 \1 u+ }0 Z" ? - 5. 高级(9x9)
# N o3 \3 G0 T3 \2 Q: ^, P - 0. 关于作者
{9 w. D1 f6 ~ - 请选择1-5开始新游戏:"
+ n, H3 v& P* T - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名9 `4 n- w( H, o
- ("1" tutorial-room-0)* ]: o: e8 s4 Z. Y5 m
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配( F. e% E) Z! R
- ; 相应的返回也可以为函数,动态返回房间名
/ L8 }. Q4 G) G& c4 z6 a. w! Q - (t living-room)) ; 如果条件为t,为永真
. J. K* ]7 A' B1 j6 ^
7 I* j# U, X* L+ i- ;;; 作者信息% K' O" \, _& V0 f4 h
- (def-room about-room
6 ~- M4 U) f" `% x* i8 U/ L1 D - "作者:redraiment
9 I, {9 K% N0 P L) f' Y1 z0 j - 微博:http://weibo.com/redraiment! t# S3 {, J1 j6 N; s% M
- 有任何建议,欢迎在微博或微信上联系redraiment。& U; D! c3 o! l0 g! ^( ?* H, d6 b
- 请输入任意数字返回游戏大厅。"+ n! A. \/ m' @, e- t
- (t living-room))- H9 _6 L& `0 k
) @# ]8 T8 W7 z% H) P- ;;; 教程3 {$ l0 Q6 r: a# V- p- C- J- M! k% \
- (defvar *wechat-5x5-tutorial-rooms* 0
& h$ b# [3 ~+ L. q$ t3 M - "The number of tutorial rooms")( g3 i9 A% a: f U( f
7 E4 |% x, Z$ C- ;;; 简化教程的定义, N- L3 t! V9 O# [& m
- (defun string-last-line (content)
9 b' j+ Z7 x8 T, T* o% ]" b! [ - "多行内容组成的字符串中的最后一行"3 j$ C! @# R3 e2 v
- (with-temp-buffer
6 |! D1 c/ e" j9 S- U! b) e( { - (insert content), @' K& e% H! L. B4 w
- (buffer-substring (line-beginning-position), |+ h1 g5 E2 ]7 J: l
- (point-max))))/ |0 @* z* M8 [) t* _$ ~, p2 X
- " \7 N0 |2 M/ ?
- (defun def-tutorial-room (prompt)* _9 L0 b& ^8 Y! D' K( h. D
- "根据提示语自动生成教程房间。
4 A( F2 Y" C" W! D z9 u1 s
( R* ?; |' c9 ~- 1. 提取最后一行作为问题;' V O2 v2 z0 Y: A
- 2. 分析问题,获取期望用户输入的内容;7 d7 {. P$ X2 ?3 f# W8 w/ ^( i
- 3. 定义教程房间和重复提问房间。"9 f. b! f0 h* d8 j& Z
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
. S- B3 V3 J- z, q- O- B, {9 ^ - (repeat-room (concat room-name "-repeat")): k+ Q/ d* R7 C' q/ c7 s
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
, V: w# C8 q( o3 A3 [) L! ] - (question (string-last-line prompt))0 P$ H; Z+ A8 C" s9 K( A* E1 G* K
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question); X8 _3 W8 \% n; F
- (match-string 1 question)))
6 q" r: g5 {; V1 W2 Y/ l) F3 R - (doors (if except
4 Z0 X" Z/ ?$ N - `((,except ,(intern next-room))
* |: J! q# x. A+ d' d; ^, s- ` - ("q" living-room)
$ N/ v% _- s: v, y - ("Q" living-room), E4 G+ l: n6 V
- (t ,(intern repeat-room)))" P9 ^8 F& R4 M3 B/ d9 f3 e; k
- '((t living-room)))))& d/ L$ v h! \/ a3 f g
- (def-room-raw (intern room-name) prompt doors)
" ]# ?9 }& Y/ P7 {( ?! [2 u - (def-room-raw (intern repeat-room) question doors)))
3 v8 a/ a) ], c: l3 _, m& M - : U4 r D9 E6 I5 Q0 S& @
- (defun def-tutorial (&rest prompts)
4 d, w" D3 D, l5 N: U& A9 k8 b - "批量生成教程房间。"/ L, q7 T3 ?/ w, e: O3 o# |
- (dolist (prompt prompts)
% a- b2 G! _0 _( { - (def-tutorial-room prompt)))
1 l# i# J# Y0 M9 n2 A& P
6 W6 h4 M' Q* ~* G& ^7 `& [. s' D. e- (def-tutorial
# J- u" ^. W2 u1 j - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。0 V: u1 p, E6 L
- 1. 教程6 y* H' z% s. d1 k
- 2. 入门(3x3)
# M9 O& X3 P0 {8 Z% z9 H - 3. 初级(5x5)
1 J0 ?6 V, Q3 U# t1 @ - 4. 中级(7x7)
7 Q- j; z4 C- I - 5. 高级(9x9)' Q, W8 L$ m# N1 T5 C! |- @; |$ d% ]8 s
- 0. 关于作者* x" g8 x. f: R7 t' i& e/ P
- 请选择1-5开始新游戏:
" B9 k. V5 K% ?3 ~/ Q' K - 您现在正在游戏大厅里。
, S1 W* F5 r0 o9 C- J, ^ - 请输入“2”进入入门级房间"
! f, n1 a; |" r2 n$ t - " ①②③% @: R; [& N9 o& i
- 1 ■ 3 h6 ?* O! c; g1 ?* @
- 2■■■
. e* c4 Z9 I. I' k1 @& Q - 3 ■ * b/ I- K0 E& s4 W z
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
( ~$ G" |1 B5 K. r - 请输入“22”来关闭第2行第2列的窗户。"
5 V: M* ^0 e# K! e# h7 r - " ①②③, N$ X2 r' Q* X x0 Z2 u
- 1
0 K+ b. C% F% k4 ?. q" n - 2
8 O6 `6 n0 O% B/ W' q- O - 3 3 x; g* A* w0 F) O4 g1 @& h
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
+ g) i/ a2 Y& t' V3 {( P - 请输入“11”,试着开启左上角的窗户。"$ N( c* g8 h! z4 I0 o% t
- " ①②③
, V2 l& M& ~; ~2 J3 i5 d - 1■■ , q! B5 h, [7 Q* M' d
- 2■ , E9 p2 I ?& S0 _/ ~
- 3 & Y) C5 C9 ?5 o- ^2 l
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
! |- s, o' U6 w9 R) l9 z2 x7 e - 请输入“13”开启右上角的窗户。"
% |( r; x' A( x4 n. Q/ m/ f - " ①②③
& R/ u$ w$ m5 F* ^9 q S1 j2 K( U - 1■ ■
+ @7 w4 b7 _, ]3 Y" f3 m2 ]7 I - 2■ ■0 Q3 E# S3 `( a% e" f
- 3 " [- Q3 v0 e5 k' J! }# n1 k& n
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
j$ x' M/ A& c0 L8 u - 请输入“31”开启左下角的窗户。"' U* v2 b+ h9 ?, R* P; U* u
- " ①②③
# u1 \5 G& q8 I+ G - 1■ ■' I2 R2 |% S- l
- 2 ■
, Y7 T' R* u5 H; T2 F - 3■■ 9 G2 ?4 y! W9 w0 Q# q
- 此时,总共有5扇窗户被开启了。% x- G1 ?+ Z6 j, @
- 请输入“33”开启右下角的窗户。", V4 U& j( K& N) o+ I/ l+ q- v! Q
- " ①②③2 _, x% [& }* ^4 X$ l+ @
- 1■ ■
5 v1 v6 m/ g7 }* p4 y: l# e( ^4 G - 2
4 e' T) C) b X( m - 3■ ■
+ i; V3 H6 K7 W$ |2 f( j. a - 现在,只有四个角落的窗户被打开。9 Y- B0 S% K% }, O
- 请输入“22”完成最后一击!"
7 o5 s- {6 s4 n ]8 Z$ i# {; ^ - " ①②③
" B/ Y0 {3 J8 B7 f, J - 1■■■
+ Z8 U G$ @: n- `; v: C - 2■■■7 X2 F& ^! M% A! i
- 3■■■) r6 }+ Y+ X- A: r3 z- F0 E7 P9 w
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
& p- W$ i8 Z8 U) f5 c1 C) Z2 X - ( h2 r4 O. ~) Q2 v+ L9 c
- ;;; 棋盘
, e& {- Z: N, V# e- d- g2 c - (defconst *wechat-5x5-white-chess* 122888 f: @* @9 }8 w) ~1 t
- " ")6 a' K: W( ~* o$ T8 C* j& j
: Z% o, o$ L4 |' I- (defconst *wechat-5x5-black-chess* 9632) ?9 v& |" L" ?9 Z5 W
- "■")+ z% o0 r/ y" ~! W Q' t
- ! h1 I7 l2 ^* R7 ^3 H
- (defmacro with-board (&rest body)0 `& Q6 ?6 X( n Y s. a
- `(with-temp-buffer3 ]4 x6 C& a' C( Z0 m Y1 }
- (unwind-protect2 M4 W, i$ t2 X5 c, G+ F, F0 a
- (progn+ k: K0 ?9 q6 d. S( U. J% S
- (if (session "board")9 k" b- U' f) e
- (insert (session "board")))5 X5 w4 m0 I0 n) O8 r4 N
- ,@body)
5 v$ E8 x! ~7 _9 k! B - (session "board" (buffer-string))))) [7 L5 q1 q. H
- 7 n* x$ l: C+ J3 |* |
- (defun board-init (size)
: `( u6 J& I( U# m, v - (session "size" size)" c" d; ^ Q* \% K4 `& w
- (session "step" 0)
9 |- q% `3 Y; c7 g5 u - (erase-buffer)
8 K/ J8 S+ u4 j3 ^& F5 A5 Z | - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))3 M3 d, L$ s% P- Q5 v# }
- (dotimes (row size)
8 Q/ M& }6 V3 [$ q8 j9 ] - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
4 J& D" Q* S, P" ?
" D- x7 e# x% N7 b2 }6 d* a; w2 x- (defun board-contains-p (y x) C- n0 }6 |* q7 T; n3 G
- (let ((size (session "size")))
7 u5 J& a6 m/ h - (and (<= 1 y) (<= y size)
6 g& {, j! T% Y1 @8 x D# ^ - (<= 1 x) (<= x size))))
+ ]- T8 D5 p; Z - $ i' r W- A& A
- (defun board-toggle (y x)- ]" P- d4 m9 h# t* }* R
- (when (board-contains-p y x)
3 @) h% C& x- J& Y# @! \, G - (goto-line (1+ y))
7 u: U: H% d7 f8 q% W# u$ Y+ U2 [ - (beginning-of-line)/ E4 ~8 x( s4 x
- (forward-char x); N8 q: ~8 S0 e2 c$ U
- (insert (if (= *wechat-5x5-white-chess* (following-char))
- l% i0 y5 \' q; z3 V - *wechat-5x5-black-chess*
# e% g" A0 d! U5 B8 c* @ - *wechat-5x5-white-chess*))$ u) }. R5 c' ?0 d
- (delete-char 1)))
, \, }$ d/ g+ {# L, W
1 d; s' `7 c8 I, [) Z* e1 L- (defun board-put (y x)
$ j' F- E; o; L/ u3 r& _ - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0))). l$ a+ A( {; O7 u
- (board-toggle (+ y (first dir))
4 K2 ~7 Y( ^! Y, [' G5 N2 ?7 v - (+ x (second dir)))))
6 G S8 g! H4 \4 q
: R0 i2 o9 z ]0 t/ D- (defun game-over-p ()4 r) Z/ d7 X% W& o" H2 L) H
- (beginning-of-buffer)8 G, }6 S! F t0 w$ }6 Y* V
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
4 W! o3 m$ E" ?( x' ]- q6 F: C - ! p! @ j& P3 |1 t
- (defun board-show ()+ o( V# L" t9 u9 J; Y; F' j
- (with-board# o/ I% o3 I5 h0 Y) c8 s$ D
- (concat (buffer-string)2 l0 k/ u% k) ]& L
- (if (game-over-p)
) \% Z( J) O% X# m& R y' n. {/ [: k0 z - (format "共%d步,输入任意内容返回大厅" (session "step"))
3 x* V% s- i# R6 x+ a* r - (format "第%d步" (1+ (session "step"))))))). s3 Z0 W8 @4 k. `1 |( g9 c; l
; _1 o8 W$ j5 L+ ]! s$ I. `- (defun board-position-parse (cmd)
* D3 |& {6 L# a. z) r9 ]# U - (if (= (length cmd) 2)
3 H3 G3 f8 ~/ _6 o2 L - (list (string-to-int (substring cmd 0 1))% H- \* I( o6 G# d- g; O* l# U
- (string-to-int (substring cmd 1 2))). ^. N; m0 f- ~9 y
- '(0 0)))
# N) |/ p! }+ q* M$ U - - `$ j4 Z2 [4 \8 u5 K# H0 H
- ;;; 游戏房间9 P& K. {8 g: y9 U3 Z
- (defun game-room-init (cmd)
5 d8 E% }4 T; C5 }" C - (let* ((middle (string-to-int cmd))3 Q) Q( H# P) t( \. y: }
- (size (1- (* 2 middle))))# H. P$ E9 g2 `( w6 G' V
- (with-board7 }8 F) ~7 s; C4 w
- (board-init size)2 T9 L$ P4 J. J' a, b
- (board-put middle middle)))
* L5 w# o$ B) _5 x v' m0 ~ y- L - 'game-room)
! w6 ^' [: f! H, k1 p3 D' j
`* ^- a W. |0 y% x0 |' ?& a- (def-room game-room. K, s! M, }& B# p* d
- #'board-show( m _% N2 Q3 a7 A
- (t (lambda (cmd)0 n( e3 S6 d$ \8 U/ V6 R
- (with-board) S, Y# B8 p7 M& \& s$ |5 o# I3 }
- (if (game-over-p)
/ f' m" S+ R z - 'living-room' X6 ^$ o- ~3 B5 [2 C) e H7 }9 n7 {6 L
- (destructuring-bind (y x) (board-position-parse cmd)
( ?/ d: c0 C) g. L - (when (board-contains-p y x)
; }# U4 G) h2 Q - (board-toggle y x); |) Q7 I1 Y7 a, |: o2 F
- (session "step" (1+ (session "step"))))
! ?; U* r Z9 g8 c - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|