wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
& A# {/ s! v9 U. {: |# V3 I. }借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
$ Z! C! _$ ]& c7 V* e b- <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;">;; 定义新的游戏地图4 o# K% @: c5 M- x( g: A
- (def-map "/game/5x5.el" ; 对外开放的URL
0 Q/ F. f0 A; i" B3 y0 l - 'tutorial-room-0) ; 默认的入口
& m) v7 n9 T: v+ l4 v
3 @( M0 i% y$ `4 Q- ;;; 游戏大厅+ I- f: ]. l2 _1 ]3 ?0 y* W
- (def-room living-room- Y/ w5 y8 D) W, n5 |
- ;; 进入该房间后的提示语
& i! e: u$ }) t3 h: s0 V - "1. 教程4 M4 C" h1 y+ O& i2 o% i. M
- 2. 入门(3x3)
+ o! k( \' e# n3 k0 j# d6 c4 t - 3. 初级(5x5)
5 z+ t" [0 N* ?2 I/ @1 j( y; a - 4. 中级(7x7)
% D0 w, l/ Q) z4 v8 U1 @/ J* p - 5. 高级(9x9)
6 m: G$ o3 D* K/ u% |! y - 0. 关于作者
8 T! @3 ^# t: b0 i7 F c# C. q1 I$ L - 请选择1-5开始新游戏:"% C9 ]3 {& v7 X5 A
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
1 ^( s/ v0 o. T X8 T! \0 J9 \) H - ("1" tutorial-room-0)' b. i$ P- ^, }& p9 q6 r
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
U+ @2 g# s% T: Y' K8 I, W+ f1 V - ; 相应的返回也可以为函数,动态返回房间名/ T( o# A: ?9 N: h7 E
- (t living-room)) ; 如果条件为t,为永真
) Z: _ a9 i. p$ s
4 n) a# h; c1 P; }& C- ;;; 作者信息
1 G5 g5 t7 I9 z& R - (def-room about-room
7 P. n4 I7 {) \ F8 Q/ x7 B% M - "作者:redraiment" c4 g k; s) v
- 微博:http://weibo.com/redraiment3 J7 c# d* x6 |4 L8 r2 z3 n8 \' K
- 有任何建议,欢迎在微博或微信上联系redraiment。" p$ p7 {/ f# B% A/ N, u
- 请输入任意数字返回游戏大厅。") z3 [9 P& R+ O& i3 }
- (t living-room))7 {9 A" U; t) r( u1 D- u
- ; ~; H! u1 ~1 y( X" u. A
- ;;; 教程+ M0 X- W/ g7 q: F
- (defvar *wechat-5x5-tutorial-rooms* 0- F8 i# F8 L/ N1 Q+ ]6 @5 _: S% O8 T
- "The number of tutorial rooms")$ S& s' O" a7 c8 R% H
% P- j1 t- d% z* f; R% c% m- ;;; 简化教程的定义6 p3 O/ k+ v! m& u8 r ?! V
- (defun string-last-line (content)3 K5 _: ^7 F) s! A4 M
- "多行内容组成的字符串中的最后一行". ~# A8 q6 R& r" }/ P
- (with-temp-buffer
7 J0 L! E% l( u l3 f - (insert content)
. }2 D* S3 f3 [! ^9 L1 H8 H( w9 a. K" d- D - (buffer-substring (line-beginning-position)
4 N+ v, S7 N7 X# c1 I# r - (point-max))))
$ a! A8 c( G. m+ A; S3 d& g - ! ]& h1 W& _2 K% J" ~
- (defun def-tutorial-room (prompt)
- s; Q9 z% N) N2 ^3 v - "根据提示语自动生成教程房间。
( R; z+ d+ B1 j( @$ _
D z0 [: u( p8 a' H" { d; c; v- 1. 提取最后一行作为问题;
+ M8 ` j# C9 e- \) t% z- \% C6 ? - 2. 分析问题,获取期望用户输入的内容;
6 M& r; ]: c' E2 [+ _7 E [ - 3. 定义教程房间和重复提问房间。"2 l, Y7 J# Y; e% B
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
z" j) A( l) A8 e0 U& A; C - (repeat-room (concat room-name "-repeat"))
5 E3 S1 T5 |4 @3 k9 e - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
. m1 q0 y( L' P# y# [0 d e - (question (string-last-line prompt))
! U4 x4 R) \2 ?8 P0 e, G% J# L - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)! B/ k3 o# U1 Z, q) o
- (match-string 1 question)))
* r8 ?. M- D- G4 T% d" E1 t4 h - (doors (if except
# j1 z* W* _$ D7 B' m - `((,except ,(intern next-room))
* U+ Y& p, X# j; }0 @, x - ("q" living-room)7 y9 f1 b& A5 R! N5 c. L" M5 b
- ("Q" living-room)
; ? E0 B% }; ~% q( y( v - (t ,(intern repeat-room)))
) Z% G5 g6 Z3 x* C - '((t living-room))))) g n. t, c# K! ]) H J. _
- (def-room-raw (intern room-name) prompt doors)
% b" O( R" Q* U$ @& V V - (def-room-raw (intern repeat-room) question doors)))5 M( v8 j: j: j5 V! V! x
- ; }! O! Y: w4 R3 o
- (defun def-tutorial (&rest prompts)
; \& {$ X7 W2 b' t a - "批量生成教程房间。"
4 l9 I' b. c {7 u7 [! U - (dolist (prompt prompts)
0 M8 v# p5 ^( ? - (def-tutorial-room prompt))) F* e. Z/ M, m7 {# `5 I' i
- 5 b& r R6 p. G0 f$ D# Q& K
- (def-tutorial
! e; M# b, M) X' u8 | - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。8 f( |( R: Y; R/ g. {; I: U
- 1. 教程
( [& t, v. A+ O: A - 2. 入门(3x3): m6 W; F/ V2 ]3 _, i5 F/ P2 E% p
- 3. 初级(5x5)
$ g* D* |+ P- W0 X, R! ~4 y! \1 R - 4. 中级(7x7)
( r( [5 }5 B1 }+ A; C6 p - 5. 高级(9x9)9 `3 M& p$ M5 W# w1 a7 o+ {1 R" I
- 0. 关于作者& {) }' n' s7 t' `
- 请选择1-5开始新游戏:- x; Q0 v; j( X7 F( V, T% l
- 您现在正在游戏大厅里。" U1 l9 c/ U5 V3 @
- 请输入“2”进入入门级房间"$ I5 D5 D& X6 P' O
- " ①②③
# x: o. r1 i8 F& a9 @, z0 d7 K - 1 ■
8 n b( ?) N: ~5 r; }& C - 2■■■
4 C y; C! B0 F5 h - 3 ■ 8 w' h/ r" N: a* `2 n7 r
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!3 D! X* H4 x1 a$ d
- 请输入“22”来关闭第2行第2列的窗户。"
9 G2 i( _( s8 L& m) G - " ①②③
$ H9 u. @# [( j - 1 ' u* `$ h, j) q; }
- 2
, n+ O9 ], ~9 t7 h$ z0 u7 r; G+ }- k - 3
& Y+ G4 I7 @9 o - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
/ v% W) x: L* Y( g - 请输入“11”,试着开启左上角的窗户。"( A: U5 o3 C* ]/ W" a
- " ①②③, j2 z5 J9 R) X
- 1■■
& W R5 i1 G# |/ W5 y& D - 2■
9 a* V O# v4 d- B v - 3
0 b& s: q5 f' n. b/ k3 d - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。7 c4 y$ z3 \$ Y$ y
- 请输入“13”开启右上角的窗户。"& }8 v2 `5 ^5 G& D" ^5 h I
- " ①②③
* n/ P# W1 ^, u S5 n - 1■ ■
' y% d5 X# S2 v - 2■ ■: L. [, i. c/ e+ K8 [5 Y
- 3
" t/ Y/ s' {! f+ z" f& \ - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。, t, R1 O1 _) u
- 请输入“31”开启左下角的窗户。"
1 e- v! N2 R6 X! U2 l - " ①②③
$ S' Q9 Y+ i" P+ f. V J4 w6 D - 1■ ■
7 I! O9 f$ a+ Q& k - 2 ■
' j+ G y ]/ o- v6 J% t* H - 3■■
/ E! \ }1 B3 n! D' p - 此时,总共有5扇窗户被开启了。
7 z7 s* K+ \4 W: D$ D - 请输入“33”开启右下角的窗户。", v" k1 e( r2 l7 B) z, E" w
- " ①②③/ ?- G% n$ j s5 }
- 1■ ■
3 l3 Z# ], t y {2 W! d! \1 O; r4 d - 2
/ e* c0 H; d( [& `9 a - 3■ ■
% e' }3 v: d$ U1 A - 现在,只有四个角落的窗户被打开。
( G0 ?: c/ E2 {' M - 请输入“22”完成最后一击!"
8 d2 q6 A% R) Q0 k - " ①②③
. Z0 _, Q2 m j: u2 m - 1■■■
; W# @/ h t# c4 \- }& h. E+ z - 2■■■
U2 J( f8 d- | - 3■■■
' y- L; ~0 p# Z0 _9 j - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
( Z7 U4 S5 x+ W" `4 r l& _5 `
0 l" p) v1 T0 @7 E% d- ;;; 棋盘
) T9 v' W Y9 L" X2 u; M, Y - (defconst *wechat-5x5-white-chess* 12288
0 w w' I0 ^2 g - " ")
7 y( C) O+ z% s/ E' D* T' q4 G
, `! s* R4 p& c- (defconst *wechat-5x5-black-chess* 9632
% \7 B) Y5 G3 |5 m- A; b+ U - "■")" Q7 m8 M( H6 f" m1 Q
, u) f! K8 u/ ]' q9 F- (defmacro with-board (&rest body)/ J! N$ F6 C+ Q) E2 G/ w
- `(with-temp-buffer/ y7 @% Z- S. w k2 ?/ ^
- (unwind-protect
. g0 ~/ l6 [7 c& d3 r - (progn8 B. Z7 x- n, E- Z
- (if (session "board")
6 R! K# U2 P" R3 x1 n4 Q) p( M) z: d - (insert (session "board")))) Y" M4 U: U& h0 o- ^
- ,@body)
* C5 U& a6 U: Y7 A n% Q+ C - (session "board" (buffer-string))))) b2 G. o* L/ e5 l& J
$ Z8 M! O% Z i- P- (defun board-init (size)' l) z0 W3 u0 s
- (session "size" size)
; h9 b! c1 T, | - (session "step" 0)
/ g) |0 l( Y5 X: J9 S - (erase-buffer)6 u% d6 K# I' _- }8 X" d
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))% \/ w! ^- D5 L: J% k2 s- ~
- (dotimes (row size)6 j; G* N6 Q* T1 ?4 d
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
1 |' e2 r3 P+ x - ) e v8 S- B4 q0 K6 l! |( G) B
- (defun board-contains-p (y x)& i# L4 o% X: d- A8 |* b
- (let ((size (session "size")))
4 B6 B$ t2 a" R, z6 l6 |' K - (and (<= 1 y) (<= y size)
* @4 s4 k) W' r7 X+ c - (<= 1 x) (<= x size))))
1 {/ B0 T7 Z' ~3 I
+ b5 A+ v b# Q4 c$ Z- (defun board-toggle (y x)
* Q0 w+ n0 E& j- d, m! T& C - (when (board-contains-p y x)
; b$ z, n5 E p7 {* N - (goto-line (1+ y))
* [6 [8 N- M6 R6 I - (beginning-of-line)0 v& ^4 e* r8 }8 k8 z7 r$ y6 q; q/ P
- (forward-char x)
( o. k9 }% Q+ Q. j. i) Y - (insert (if (= *wechat-5x5-white-chess* (following-char))) L1 `$ t$ Z$ V$ w4 H0 [
- *wechat-5x5-black-chess*
% t5 Q, Y+ @. C5 O2 H- w - *wechat-5x5-white-chess*))
- L7 Q& {- K+ `0 N - (delete-char 1)))
0 ]) T" W4 l& b1 }: X) a
; x, ]" ^6 v1 d! l: R E; S, k- (defun board-put (y x)9 G# G2 g$ A& i4 w `0 `
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
! U, X) T/ B2 y1 K# \% w+ R" Z. y - (board-toggle (+ y (first dir))
. C+ G/ C0 n7 r/ |0 L - (+ x (second dir)))))
1 H G( m8 ?' C
8 C7 U: G/ v2 S( [5 ?4 a$ U- (defun game-over-p ()
; l- u9 B; n7 x! ~ - (beginning-of-buffer)
: y$ I! `' V' ~# t+ L9 ~ - (not (search-forward (string *wechat-5x5-white-chess*) nil t))), n. X' F9 o- U$ O
+ z. Q! Y5 c# O; i; P9 w, ^# r- (defun board-show ()
. ?, E. e, N+ L- G; ]- o: E' r - (with-board2 h1 j+ c5 L4 Z5 q, ~* a
- (concat (buffer-string)
6 c; C9 K# r* Q - (if (game-over-p)' B8 Z+ P: c% \6 E3 @
- (format "共%d步,输入任意内容返回大厅" (session "step"))
% w6 g4 j ^' V5 J - (format "第%d步" (1+ (session "step")))))))& P9 f2 S* ~& a* c4 p. ]! X
- 5 W; E$ @9 I; ~0 f8 h+ ^) m% z
- (defun board-position-parse (cmd)8 }) s4 }9 N1 i' G- b- @8 V; G/ r
- (if (= (length cmd) 2)
h- O% M7 }9 G/ o - (list (string-to-int (substring cmd 0 1))1 I6 I% d4 R, l7 ~: k( q
- (string-to-int (substring cmd 1 2)))0 \; z; _* \- z
- '(0 0))), {' W2 I7 `! |4 Z% }. T6 { U
- 6 f$ U7 g+ Z5 u: h
- ;;; 游戏房间
# \# y. q. i# P - (defun game-room-init (cmd)8 _: ?, `3 N1 X5 q5 X5 \- s+ j- X
- (let* ((middle (string-to-int cmd)). _2 T4 ?" Z2 R% Q
- (size (1- (* 2 middle))))* `% ~) a! f" ]. q4 p( u
- (with-board1 J6 e" `5 x+ G/ l& g- B. d
- (board-init size)* q, c9 T# C8 j4 ]9 ?, v$ r
- (board-put middle middle)))
% x' u: H$ V& O - 'game-room)# G8 p# A) ~. F( G9 e
- ]( K2 W* S( _# R7 H- (def-room game-room+ q; f- L* _, m! \
- #'board-show
4 A. W( V2 U) h+ D6 N - (t (lambda (cmd)
d. {% w' b- w* W& g# a - (with-board2 U% E# h3 i( L o3 B+ y5 }
- (if (game-over-p)% W- m! q- d; x1 o9 N2 q8 G. q
- 'living-room( D% U9 n) p# ?* V" k9 ]
- (destructuring-bind (y x) (board-position-parse cmd), Z+ L9 R0 Q$ a( @
- (when (board-contains-p y x)1 i! c& n; o! M% }9 N
- (board-toggle y x)2 I4 M- F" } z- s; f0 G" I, C
- (session "step" (1+ (session "step"))))
5 g1 s7 U3 q- O5 o - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|