wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。: @, e8 o; ?( s5 ~3 q9 h9 N
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- ' ?; I7 U3 A9 Y% y" W
- <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;">;; 定义新的游戏地图6 `0 Z" O) {2 q( ^3 b ?" n
- (def-map "/game/5x5.el" ; 对外开放的URL8 q# S/ q/ D) _4 [
- 'tutorial-room-0) ; 默认的入口
7 |# C5 _1 q! A3 ^+ x/ F - ; I. d6 Y5 V! @1 X0 ^- ~
- ;;; 游戏大厅$ Y+ y9 J }" p& N5 Y
- (def-room living-room% U, Q* Y" W: A A" ~
- ;; 进入该房间后的提示语
% [$ D! b* M1 D1 n - "1. 教程; \: D; X4 u& t6 |6 R8 j
- 2. 入门(3x3)
) X0 Z" H& Z- s7 M - 3. 初级(5x5), {" [* ]; Z) x* _9 g
- 4. 中级(7x7)5 h+ z' Y7 @! E S3 i
- 5. 高级(9x9)
: s4 X: w- {+ ~. }' k! t3 ? - 0. 关于作者" i% O) Y' }. {' B+ ]+ v! {6 z
- 请选择1-5开始新游戏:"! X$ S1 b2 n7 I+ T' R, V
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
! Z& v; L- t4 d+ d1 X2 X* [+ E - ("1" tutorial-room-0)4 x& L# }* C9 s" h' @; F" L5 I7 j; e
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配. a2 K- q* {$ \+ S; P2 E/ f& m& k0 d
- ; 相应的返回也可以为函数,动态返回房间名
) S1 x0 ~* K' n* `9 q) L - (t living-room)) ; 如果条件为t,为永真
, E+ Y# \* l8 k& I$ P) X0 r- q - ( T) g+ z! y' Z+ R' y. H9 M% ]
- ;;; 作者信息7 C( O- n/ q8 E V# W; z) m* g& x
- (def-room about-room5 z4 L" v4 o) B
- "作者:redraiment
* a6 D" {4 d! ? - 微博:http://weibo.com/redraiment0 v' ^( T' V) M0 x5 b0 l2 e) L: `& c
- 有任何建议,欢迎在微博或微信上联系redraiment。
8 p/ B3 w, X- ~" M F% m - 请输入任意数字返回游戏大厅。"
0 s3 j8 z y( C9 Y& p, x9 J - (t living-room))
* o3 a# I7 c" R2 {/ e/ B, i
* D8 O8 J+ O0 O$ H% U/ [2 b- ;;; 教程
4 I, ~3 ?% P7 M2 o7 y - (defvar *wechat-5x5-tutorial-rooms* 0! J* ]/ F, r: _. s7 K9 d n' p
- "The number of tutorial rooms")
+ W' o* x+ e& G9 } - % {" ^5 [* }( o
- ;;; 简化教程的定义
; b. B. i( ~; \! A6 i: Y - (defun string-last-line (content): Q Z* S! G: j/ K
- "多行内容组成的字符串中的最后一行"' d- x1 o* d' u4 V- f8 `2 y
- (with-temp-buffer
0 U3 b# E' ]8 J; q8 N5 P* @ - (insert content)
# j+ f1 o0 [/ }. i& l2 m - (buffer-substring (line-beginning-position)2 t$ [! D- ]4 {" z" A2 E. b
- (point-max))))" G; ~# r+ T. R3 \( e9 N$ L
0 g2 {, S J9 u7 C0 a; L- (defun def-tutorial-room (prompt)4 C0 Z; z# o& F
- "根据提示语自动生成教程房间。0 M$ E! a3 Q) r/ {6 }7 m' m3 z
- 1 r' r& k+ y% k/ h L2 U
- 1. 提取最后一行作为问题;
1 q' ]& r6 o& t( D - 2. 分析问题,获取期望用户输入的内容;$ _- f5 V. k' @6 ^3 X
- 3. 定义教程房间和重复提问房间。"
8 f! l$ M" i0 X. Z. } - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
. }0 g5 n9 ~1 G K8 p" g7 X$ K - (repeat-room (concat room-name "-repeat"))
) o, }8 c" J, j1 @/ E - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))6 A6 \5 z. B8 m* W2 X; v
- (question (string-last-line prompt))) C2 p- \- ]2 p8 R- S+ O# c6 |
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
* \4 F2 U/ _1 S) z* d0 y1 d - (match-string 1 question)))4 D# t5 M! ^9 d# B7 w& R8 v. u
- (doors (if except# d1 P- D4 ~/ `& T0 h
- `((,except ,(intern next-room))
+ V. v6 M3 c4 V# i( t( N7 {; [ - ("q" living-room)
' x: m; R; H/ n+ `+ y5 R$ l - ("Q" living-room)2 V( j" A, f6 z7 n+ a9 ^4 h
- (t ,(intern repeat-room)))
D7 f+ d# |6 F7 G/ } - '((t living-room)))))
, t$ [; k; K+ }% X5 y+ ? - (def-room-raw (intern room-name) prompt doors)3 i3 j9 t$ _2 r3 M
- (def-room-raw (intern repeat-room) question doors)))) r# V9 N" D3 a, m5 j
" N7 m* }; A7 J7 |4 [1 j- (defun def-tutorial (&rest prompts)+ K& P" x% @3 ^2 Z
- "批量生成教程房间。"
2 s% Q/ S, g) F& r) ^9 {3 k% H A - (dolist (prompt prompts)- d; d% V0 @- I/ w. ^
- (def-tutorial-room prompt)))5 m% U1 u R! ^, F
. E# W d' T+ h: G- (def-tutorial
5 b6 K% n1 G/ F5 y( x7 e - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。/ O/ Y( d7 C3 z: k4 c1 b3 q
- 1. 教程$ I; ~- N/ L3 p0 K$ a. Y
- 2. 入门(3x3)
2 c" l+ q8 V, s9 I8 D - 3. 初级(5x5)7 E ^7 c4 A, \5 {/ G9 p6 x
- 4. 中级(7x7)" Q3 ] j2 N8 M, m5 ^2 C
- 5. 高级(9x9)% Q" m6 s2 u1 E5 ]/ Q W
- 0. 关于作者
* b3 C L; E. d$ Y. | - 请选择1-5开始新游戏:
0 B0 ?8 g* z/ L4 ? - 您现在正在游戏大厅里。) X8 E7 O( ]! e+ u7 {3 k4 A, Q* |
- 请输入“2”进入入门级房间"7 U ~4 j5 I& F6 u7 V8 ?9 G) d
- " ①②③
$ R* E: Q; u2 ]9 h8 m6 y - 1 ■
1 C# l t* ]4 m4 R - 2■■■
5 j, Q% T9 q+ Z2 l) u - 3 ■ . Q4 ?+ V" _1 z% P& Q
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
. n6 U1 t1 B2 T- j# c - 请输入“22”来关闭第2行第2列的窗户。"$ P" _0 F& g. Z' r2 h
- " ①②③# Y8 I; ^9 F) m1 W& x
- 1
* N0 X0 c9 ^6 L: J$ P - 2 7 q; m' W$ ^+ l* ?
- 3
% {. Y2 M. D" V4 p, c* n0 n9 ` - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。4 ?) E L) r$ X5 ^% |+ z
- 请输入“11”,试着开启左上角的窗户。"
- Y9 l, Q- m4 Q0 t - " ①②③
; \4 H: F0 w; k* r7 E: [ - 1■■ % n) G- w$ O/ V( i3 D' j
- 2■ 8 [4 i, Z% ]+ O4 ~' `+ G4 i8 Z
- 3
~# G% I4 n1 k3 s+ T& j. J - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
) @9 _' f0 ?8 O - 请输入“13”开启右上角的窗户。"# M. \0 q4 Y" m7 A
- " ①②③
( w& G+ @7 Y9 |. u( k/ k4 X9 F P# W - 1■ ■
. z8 ~! m6 u3 ]* Y) Z - 2■ ■- ~) r$ h+ d3 q8 e' d" Z
- 3 4 i" e4 `+ n8 Z6 u
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。8 r1 L9 p6 D0 V* G+ R$ c7 N
- 请输入“31”开启左下角的窗户。"
8 D8 }/ U1 I" Z) b) j/ U - " ①②③& A$ B6 w2 f" O- j* z% p4 S
- 1■ ■7 X. l! I* ^+ u9 e" Q6 Z( j
- 2 ■1 Z6 E0 H/ B4 T0 o$ Z' e
- 3■■ ( p- Y$ V2 Q' b1 x
- 此时,总共有5扇窗户被开启了。
5 @) r4 ^% F, u t3 w - 请输入“33”开启右下角的窗户。"" X; f" h2 J' [5 [6 ]& I( |9 v
- " ①②③9 Q% o5 W& \" |- h# \+ s
- 1■ ■+ Z) A! W% ]5 b
- 2
0 m. Z6 s% ~! a* } - 3■ ■
! N5 f" m) i! a2 G5 m - 现在,只有四个角落的窗户被打开。
/ U9 M" k# g( l) u. Y, U( l - 请输入“22”完成最后一击!"& D. A3 M$ }6 f! T0 c
- " ①②③
& R3 [8 X; \9 Q" R - 1■■■& E( i8 ~- h. K) s3 W2 f e
- 2■■■
: [' z. ^' }+ H+ }' R; t - 3■■■9 S- |; b% y: |
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")( U- z& A0 v# U0 y6 G- Z0 ^" T5 z
" n8 {7 f: M3 |4 j# p7 q- ;;; 棋盘4 W' q! D, D r5 j& Y) R3 v9 F
- (defconst *wechat-5x5-white-chess* 12288
3 k* t" A0 U& i" F - " ")
9 c& q( r) i: t5 J! G& K7 V* A
`1 a" w! C& T, W6 k! L- (defconst *wechat-5x5-black-chess* 9632
0 C8 I. k- V5 ?$ E, ~6 w" \ - "■")9 \: `/ S1 o2 \
/ ]8 n7 l9 Y- K/ P; Z+ L) s+ ]% Y5 B- (defmacro with-board (&rest body)' C, _" h5 A7 v
- `(with-temp-buffer
0 N5 _: ~- n/ f+ Y- m - (unwind-protect
! g5 f5 S' Z3 ~ - (progn k2 u7 D! ^; s* C9 G6 p) \
- (if (session "board")7 t. \' p: h/ s1 h4 q7 v1 w6 r
- (insert (session "board")))
X& J) B/ o4 p' w - ,@body)/ X8 n* K0 |7 X+ j" H9 y; ]# B
- (session "board" (buffer-string)))))- B- ]' j; C/ ~9 a' K6 r8 P
- - D) g! v+ g6 p5 O7 K9 w
- (defun board-init (size)
$ ^6 M/ T9 c% A" M$ I - (session "size" size)) {6 }+ U1 t! |* X* o7 s
- (session "step" 0)& W+ [- t# [2 G
- (erase-buffer)' o2 _9 n2 H3 ^) x7 F: C$ X0 r
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
: [8 L, u- W& Y! J2 Y" l - (dotimes (row size)
- R' M+ _( h, l6 T - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
! A) |0 E0 g5 `1 u! X, }' o D8 v
3 H( ^" f0 W/ ^1 F9 x- B% P- (defun board-contains-p (y x)2 U4 k1 D) Z, J. a$ W9 ^6 }; ^- [8 V9 r" V
- (let ((size (session "size")))
+ r# D% } G, s/ K - (and (<= 1 y) (<= y size)+ c9 f: r* m. C# _# B' y3 R/ A
- (<= 1 x) (<= x size))))
6 W' H' R6 _- e" l! o - $ h" _' b8 _1 w4 k- }2 k8 ^* p
- (defun board-toggle (y x)( p2 p" h: o) {
- (when (board-contains-p y x)
8 n- K. m5 D5 e, I, r- J - (goto-line (1+ y))
6 r2 e! N; O( G* r! |3 x4 e7 { - (beginning-of-line)0 y$ D5 R- v/ U7 P' |) M, U
- (forward-char x)7 M% A8 I! c5 |3 T; l
- (insert (if (= *wechat-5x5-white-chess* (following-char))* q" p0 ~8 r8 j- ?$ T5 W- q3 ]
- *wechat-5x5-black-chess*. L i7 i, J: P: L" P
- *wechat-5x5-white-chess*)); ^# t0 W4 j( k2 I" q( z+ s
- (delete-char 1))). n8 @) i; i/ B2 i. O
- 8 E5 l+ m! O8 H3 y; P& m; t
- (defun board-put (y x)/ n0 v& n' w% |3 v6 ?8 t
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
" W1 x% c4 K8 _, H! S - (board-toggle (+ y (first dir))
! g$ [; H+ j* A- u - (+ x (second dir)))))# ~5 ]4 C, ~3 z* U
- ) o' R+ } x5 S/ L @
- (defun game-over-p ()
' d4 x1 e' |7 J0 o- @! F% b - (beginning-of-buffer); [5 T3 y" D! m- F
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
& M; J) w& R( B$ f. s - 5 q* T4 H; H4 U; I* ?
- (defun board-show ()
% [0 R- i6 x& \$ ] _6 c4 ?$ I - (with-board
8 z; e9 z( |/ O7 ]: C- X - (concat (buffer-string)
' i+ @7 E+ r: ~; J - (if (game-over-p)5 B/ K. {/ l. B0 _
- (format "共%d步,输入任意内容返回大厅" (session "step"))
z) W, s& h% Z# ]' \# n4 [: } - (format "第%d步" (1+ (session "step")))))))$ R4 ^% B9 {, B: _% a
9 H" k5 a; `8 g# ~ q2 w# b- (defun board-position-parse (cmd)
. @% k. X* J+ f2 L" V' N: U7 l - (if (= (length cmd) 2). n6 |* c3 Q! z& K: M6 }* y$ Y
- (list (string-to-int (substring cmd 0 1))9 x; F- [2 E- n! p2 t
- (string-to-int (substring cmd 1 2)))" v9 Z) w2 [& l7 k" G7 t
- '(0 0)))
. f6 k) K! U$ c5 G4 o- q
0 C' r0 h& U5 R- ;;; 游戏房间5 L/ @+ m9 L. g9 k( \4 g
- (defun game-room-init (cmd)
; k1 e& {" U7 c; L4 h - (let* ((middle (string-to-int cmd))
: X$ O O1 e# h - (size (1- (* 2 middle))))2 O5 f0 U* t$ n+ T+ |* Q% w
- (with-board, M# q0 M3 b' j3 D, s, X
- (board-init size)1 G: O& N/ w' k
- (board-put middle middle)))
& G) n4 x7 b0 W$ x2 n+ j* R' a( i7 U - 'game-room)
- F! s# r% B+ m/ N$ e0 Z: v# I - + X3 N! F; |1 ]
- (def-room game-room
! f: r+ m9 O! |7 ^ - #'board-show
/ o* D6 C1 Q+ |0 u - (t (lambda (cmd): T" n& G! i( x0 \7 A
- (with-board, b7 L5 F4 [5 z' S+ S5 \
- (if (game-over-p) n9 t4 ]6 T3 b' f2 _+ t9 P
- 'living-room
3 B8 K8 Y& p3 ~0 j# I/ D - (destructuring-bind (y x) (board-position-parse cmd)
% \ ]8 N3 ^+ U N6 @% q. f - (when (board-contains-p y x)& ]) ~3 ]' A, d- z# X0 c) m) Q
- (board-toggle y x)
$ ~. v$ ]% g: e- h8 t - (session "step" (1+ (session "step"))))
) H1 a" O& [7 W5 m5 @ - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|