wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。# h/ [: u4 u& W* I* ^$ C. N1 i
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- Y; _- _; o+ i1 K
- <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;">;; 定义新的游戏地图$ W# i/ Z7 Y# H2 I+ C
- (def-map "/game/5x5.el" ; 对外开放的URL" k4 {7 W/ k8 T% h/ x
- 'tutorial-room-0) ; 默认的入口
$ x" }# h% [2 j$ X, Q {9 U - ( a' l1 c+ G( |
- ;;; 游戏大厅- z& J$ x0 t. r! m; J, k
- (def-room living-room- ^% A H; K! ^0 Z1 R
- ;; 进入该房间后的提示语" A" a9 C, s2 O5 V
- "1. 教程
$ h6 Q6 v1 p1 | - 2. 入门(3x3)6 f. K* l& C: e3 I& i; @ C5 g
- 3. 初级(5x5)
! `3 v: F7 w K- P) ? - 4. 中级(7x7)
/ B A# W2 }* H+ F) o6 o8 C; A - 5. 高级(9x9)
" C3 M$ r" {6 N" g! a - 0. 关于作者$ y$ C5 g% O1 }1 s4 G
- 请选择1-5开始新游戏:"' ?5 |3 ~% c$ r4 n9 k& b0 _
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名! D! @5 [, ]& z" _- H
- ("1" tutorial-room-0)4 V% m0 \+ x0 i
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
0 V0 _2 Z- i8 H7 h3 r+ H - ; 相应的返回也可以为函数,动态返回房间名
3 W& y8 }1 M6 {, E - (t living-room)) ; 如果条件为t,为永真
7 Y n q/ i9 x% C A6 a
2 D- a4 f% |: r, O- ;;; 作者信息
" t3 H% M8 x. N4 X# Y - (def-room about-room
0 g- n; T3 E; o O. j: z4 a - "作者:redraiment5 [# v3 f4 H/ i! ^5 b7 |
- 微博:http://weibo.com/redraiment
6 R3 [% w0 V& O! x - 有任何建议,欢迎在微博或微信上联系redraiment。; t' T" C$ A! {. D) y$ m9 |0 z
- 请输入任意数字返回游戏大厅。", D( f" P6 g' f/ K
- (t living-room))
' q/ ^' C4 O+ B& _; b - 5 R" ?; S# J# @6 o: T' ^+ U
- ;;; 教程
, C# I0 l: ~' W1 d7 m - (defvar *wechat-5x5-tutorial-rooms* 02 X8 _! v t8 P; a, t I
- "The number of tutorial rooms")5 M/ n# [! f; ^$ c9 c
- / [2 t+ h. }5 G5 | M5 W/ B) q
- ;;; 简化教程的定义
. g0 i4 n# j% U) {3 B6 N2 | - (defun string-last-line (content)( ?& F) r5 ]& w2 ] z2 W6 a
- "多行内容组成的字符串中的最后一行"
, Z6 \5 \/ V5 b, ]* j- Y% l1 E! f - (with-temp-buffer
3 R+ t5 v4 Y$ T* |! r - (insert content), F( D m* }3 J9 y
- (buffer-substring (line-beginning-position)% ]' _2 G: ^$ L9 C! t
- (point-max))))
8 o5 d) b- G& W( m0 ?" l - ; f2 x( ]/ t' N f
- (defun def-tutorial-room (prompt)& V. ~" \7 \3 z, C
- "根据提示语自动生成教程房间。6 V! P3 T# f/ B# [2 r& K4 b. m* o
- F* B2 L" A$ g3 C: i
- 1. 提取最后一行作为问题;
6 U: M7 M# T" I v - 2. 分析问题,获取期望用户输入的内容;: l3 d3 U) l+ g) P$ ?1 ^& { |
- 3. 定义教程房间和重复提问房间。"4 V3 Z! a4 K' y w, P. V
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
1 C1 e1 l- w1 V/ t - (repeat-room (concat room-name "-repeat"))% R8 x: {5 O7 g" `( v* @7 d
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
" [; w3 H- L8 y: s - (question (string-last-line prompt)): i+ b% u, B9 S2 i* X/ R; u9 {
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
3 y6 c1 `3 ^9 J$ [2 G1 z - (match-string 1 question)))2 X+ l/ f' U, o* o5 N/ U% D
- (doors (if except
0 s$ k+ p' x k2 A" ~# S$ ] - `((,except ,(intern next-room)) f3 R8 n) n; b% k) _- R
- ("q" living-room)
3 G2 o: p4 [2 m9 M) ?' Q6 L4 O5 R/ h2 i - ("Q" living-room)
, \7 I- N" k; s; c' y' g - (t ,(intern repeat-room)))
' A5 Q# \. [4 I; A3 s+ N - '((t living-room)))))
/ t* x9 Z6 ]/ ~3 u2 e( C - (def-room-raw (intern room-name) prompt doors)
/ {, C: i( t+ l9 f& G - (def-room-raw (intern repeat-room) question doors)))
+ Q+ m8 i/ n% K. j- g
7 \# i" T E$ f2 c' G- (defun def-tutorial (&rest prompts): A* E9 ^ K) l' v- R
- "批量生成教程房间。"' o3 p q8 d. Z) P4 M
- (dolist (prompt prompts)6 ~0 S: q! Q- n3 f9 V' n; V
- (def-tutorial-room prompt)))# l8 S4 c; i r" V8 j+ C7 \
- 9 ?! n, j! q, Z1 J8 E4 O0 M
- (def-tutorial3 \; m+ ~+ z2 y) c+ }
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。1 E T& q t7 e; [5 l
- 1. 教程+ Z) t0 B3 Y6 ~( f/ g
- 2. 入门(3x3). E1 ?: T5 [7 \
- 3. 初级(5x5)
* t# ?1 |$ G( |5 A- r( U- P - 4. 中级(7x7)
' V% U5 a; r, D! l* d5 B - 5. 高级(9x9)5 n1 [* o% ]7 ?; C2 U9 ~8 J
- 0. 关于作者( A) i6 X9 J& O: g2 s, p
- 请选择1-5开始新游戏:
$ Y0 T4 `( ?$ ]$ b - 您现在正在游戏大厅里。) k7 o; e B; s8 E3 G
- 请输入“2”进入入门级房间"
/ d8 x6 `# d8 k8 B. D; I, l - " ①②③9 T8 t) K/ N: b* L& B, Y
- 1 ■
3 O1 L, R$ |* y8 S, ~1 Q - 2■■■; T9 e7 w7 Y, g8 X3 P) U8 ~
- 3 ■ & \* K( |- y$ W( G/ H( @8 R
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!) x4 p+ H) D7 K/ `9 O
- 请输入“22”来关闭第2行第2列的窗户。"5 Q' @; z: n/ P0 I- j, l. l
- " ①②③
. }2 t8 E5 D# H( m% Y2 v - 1 $ J$ m3 S/ D+ s2 L) o/ V9 Q
- 2 ; t% D' Z! A, f! C! c# v
- 3
# y. n0 f5 u- [& e2 q. O% ^ - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。- `: c/ l; F# V( f
- 请输入“11”,试着开启左上角的窗户。"
! o+ z. ?8 N' v+ {' s& T% \ - " ①②③2 P9 R) B2 b0 O/ e" d1 \, p
- 1■■ 0 m w& @% {0 Z. ?7 A( [) T+ g
- 2■
! F, A" O. t/ n% I% ^- |; z4 u - 3 / s6 j4 j1 C7 n# r
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
( e& Y% l) i( a( |, V' H - 请输入“13”开启右上角的窗户。"
n! w% |7 x9 _* Q - " ①②③
( w. S6 m2 \1 h - 1■ ■
' q/ C; X) R2 [8 z* }5 n P - 2■ ■
4 V) m# ~' m2 k0 j6 F - 3
. [6 ~. a2 X+ w, ~ - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。* s' ~9 ]1 e/ S- x5 e
- 请输入“31”开启左下角的窗户。"( y4 U( J- R! T/ t. I
- " ①②③
' L& h- o8 I7 V - 1■ ■) ~( D; c4 V! G9 e
- 2 ■
- c; v( T* q# c) y9 o5 R - 3■■
8 U8 Y" } |* J6 J& H; W+ Y' E - 此时,总共有5扇窗户被开启了。
7 V( S- Z/ f" ~5 x - 请输入“33”开启右下角的窗户。") v0 c9 `$ }0 |1 q0 j- q- C6 ~
- " ①②③
6 ^% b5 i* B% ^& J - 1■ ■
/ q n+ k& D# }! @- ?6 P - 2
. g: K0 K s: j6 K - 3■ ■
8 x$ Y5 k1 P" B/ R& K5 r - 现在,只有四个角落的窗户被打开。
( d8 A0 M6 |& G6 c# Y7 q/ D0 r - 请输入“22”完成最后一击!"
1 S5 X3 v3 `9 I - " ①②③
. G2 H5 [* Z/ @3 [ - 1■■■
0 q* d% c! N! V0 }8 a7 x2 B3 N - 2■■■% D `) v2 S+ d7 I* H
- 3■■■& n# g6 o* B/ {: I+ |, J1 R
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
. H1 ]- m/ |# D9 ^ - 4 N* B; k' B& {% a# ^
- ;;; 棋盘3 }- I8 K+ q) | M2 x
- (defconst *wechat-5x5-white-chess* 12288
4 s6 b$ k( V ~ i3 d8 B - " ")
( K5 `0 x' k. e( r8 R
' I1 ] ]1 x- A. W- (defconst *wechat-5x5-black-chess* 9632
3 n7 [0 `# x6 h( \7 j - "■")' ?/ O- D2 S: @
4 F+ `0 Z" G0 y' i% `) X- (defmacro with-board (&rest body)
! I& ?( A8 s8 F9 ~6 y& B, [' w - `(with-temp-buffer" L! m4 {8 w4 ~9 r
- (unwind-protect
* A; {( v& w9 p Q8 d5 \2 | - (progn
0 I6 X3 y3 u; W! c- W - (if (session "board")6 i4 C- e) o9 U' k7 Y
- (insert (session "board"))) |7 E' z9 p* |+ ]8 t' q
- ,@body)
2 h( D4 ]- i% Q. H( k- X2 M - (session "board" (buffer-string)))))
9 Z) o# a" m7 z - ! r3 K# A u& J# ?* _- @: L9 l
- (defun board-init (size)
# _! d" [, G v/ h6 | - (session "size" size)
$ T+ v+ ]. [( X; \# E% x - (session "step" 0)
% R ~ O( j* b2 Q R8 z+ Z - (erase-buffer)
C) W& Y8 H+ Y1 b4 c9 T9 w' h - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
- R+ z |- Z# H* T - (dotimes (row size)4 C8 ~- z+ m* v$ |" D V
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
6 x% x* _0 X" W( n8 R3 k) b& y8 i
2 _ M* C _) L% w* [+ Z- (defun board-contains-p (y x)) G) e5 a5 z5 o6 n
- (let ((size (session "size")))1 O% H1 S$ _$ b5 d6 ?5 z. F1 N, {
- (and (<= 1 y) (<= y size)
$ u& G8 v' M; `" ]4 b - (<= 1 x) (<= x size))))
) O9 C+ m( s/ O$ d - 6 n* X1 s/ a6 g. S1 m* R9 A
- (defun board-toggle (y x)" o% o) S3 ]2 g( \% b
- (when (board-contains-p y x)% [ d! a, @8 f
- (goto-line (1+ y))
) a% Q! ]; {3 t+ ]2 L8 i - (beginning-of-line)
: l+ v1 q5 w1 P6 o- U+ e - (forward-char x); F0 g, }/ g, r" r, L/ ?7 J8 Y
- (insert (if (= *wechat-5x5-white-chess* (following-char))
" M) F+ a& y4 W& s - *wechat-5x5-black-chess*( [0 g. k! d* B F! R$ z& W1 x9 H- Z
- *wechat-5x5-white-chess*))/ ~$ } p3 P. ]% c4 [
- (delete-char 1)))% P) b7 |$ {/ v. h
- $ _ m& U# W8 j. @; ? b
- (defun board-put (y x)
^# o4 k( ?: p' Q. g' A/ E% M5 I - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0))). X& m w0 f3 l, o6 s ~. A: n$ n- ~
- (board-toggle (+ y (first dir))
- Q8 L5 E# x6 W( \8 N& V - (+ x (second dir))))), m0 n. L6 |; q$ Q. x" t+ a# F: ?
- & y1 l; m7 l6 |- G+ _5 L
- (defun game-over-p ()
, h; J7 K8 O: W9 f - (beginning-of-buffer)
3 M1 Z' c4 K5 J) z1 R7 l* q/ |% p - (not (search-forward (string *wechat-5x5-white-chess*) nil t))): T' |! T0 c- p6 k# Z2 {
- 3 e+ J* j1 x$ h: S# A; f* u/ {9 `& Z
- (defun board-show ()9 ~7 Y( C/ ]" V1 s4 r4 \
- (with-board& w3 O' y4 r& G" U7 W( j; C. L" C
- (concat (buffer-string)5 Y; d: t* W2 g& A& c, P1 z
- (if (game-over-p)' J( X5 d. C5 B5 X1 ~ B8 u
- (format "共%d步,输入任意内容返回大厅" (session "step"))$ G* `/ G5 e, N( A' i3 r- T
- (format "第%d步" (1+ (session "step")))))))
$ L3 L; b/ }& J0 e( @5 W( C( \ - y8 N2 v! U: f# y
- (defun board-position-parse (cmd). a% g1 x9 w( f1 M" h/ x
- (if (= (length cmd) 2) d* I3 W$ X. ~ D" z2 E# D
- (list (string-to-int (substring cmd 0 1))
: W& ]* X7 n, z: L9 v. T6 L5 t3 M: ] - (string-to-int (substring cmd 1 2)))
0 J, @7 G( c! V6 A! N/ o - '(0 0)))
7 s" X' k' n- N$ W; | - 2 U9 a* ^& K' s
- ;;; 游戏房间/ ~+ d4 \* h& M' U
- (defun game-room-init (cmd)
) B8 k4 t( I) y7 N4 U, h - (let* ((middle (string-to-int cmd))2 ?! ~! P: F- p5 g7 \# T0 k2 f- T! S
- (size (1- (* 2 middle))))2 W& x* y1 ]" w* F. w
- (with-board: m7 E9 J7 V9 Q* a+ `- y" W
- (board-init size)
: I8 i& }+ J+ z, S7 f7 m; ? - (board-put middle middle)))
( ^6 u5 |( i+ V S" a5 P* ? - 'game-room)
; O# y4 k u$ g/ @& c* q
* V6 `2 b1 z+ i% W- (def-room game-room! x$ Y, v: f+ L( k( N
- #'board-show
, u$ L* R+ b4 A2 F - (t (lambda (cmd)! |& D- ~: R2 ^0 ?( c
- (with-board
( s- }' v; _& h$ j. r# L; |7 h - (if (game-over-p)
7 l. g3 D8 h g T% } - 'living-room
% m& O1 k( }1 G. f3 Y- X - (destructuring-bind (y x) (board-position-parse cmd)
& N6 m# H5 i# o - (when (board-contains-p y x)& ^' o. b" o1 ~* ]) }0 {
- (board-toggle y x)
& W2 q5 X0 Q( f9 b. g - (session "step" (1+ (session "step")))) u7 t' P+ w/ H$ ]
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|