wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。/ C7 m# l% @9 c( e/ z9 N
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
1 I1 {( N$ s0 a( T- <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;">;; 定义新的游戏地图
1 ^+ u7 y2 P6 I' n - (def-map "/game/5x5.el" ; 对外开放的URL
. ?' x* L" f2 }: a7 y4 o" o2 a% { - 'tutorial-room-0) ; 默认的入口
& O9 K5 j6 A0 b% E+ _
+ O$ Q8 K& D2 |' _2 w& ?* x$ O1 ~, M- ;;; 游戏大厅: O7 W" f) v6 q. o
- (def-room living-room
3 m# N! C9 z( x3 \ - ;; 进入该房间后的提示语
8 }! v8 H( h. }' ]: G - "1. 教程
9 H- f6 o- h0 t: o - 2. 入门(3x3)) T8 Q0 V: T: Z! o
- 3. 初级(5x5)
5 r. C( u f1 }+ V - 4. 中级(7x7) P8 |( g. O0 m1 y/ [& |8 S$ G
- 5. 高级(9x9)4 j, }0 ^9 i* \) M& U# @
- 0. 关于作者
6 W; H2 T5 y$ Y# J" S% G - 请选择1-5开始新游戏:"# T8 k# c- E& a3 n
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
! |& I2 {" j* Y& H/ r: v5 z - ("1" tutorial-room-0), E. K* X* o/ T5 ~) Z: _- G+ H( p0 Z
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配* E* ]& D. [# X/ I
- ; 相应的返回也可以为函数,动态返回房间名# a( v g; P4 s/ l6 x/ Q
- (t living-room)) ; 如果条件为t,为永真
2 h4 n4 C1 |7 U. u d2 D6 ]& \
5 \& f2 ~. x7 ~7 L8 ~- ;;; 作者信息1 L& O- _: h0 {3 A$ h( }
- (def-room about-room J4 C+ f n6 k7 Y
- "作者:redraiment
6 b2 K0 Z8 h& d( f( l. S; ~ - 微博:http://weibo.com/redraiment6 V/ u! @' N0 Q/ [' x! H% F
- 有任何建议,欢迎在微博或微信上联系redraiment。! H: v& O" K. R
- 请输入任意数字返回游戏大厅。"
3 }: h8 @- D- \- N9 t- Y - (t living-room))
5 ^9 R* G& L- l6 g
8 e5 |# R& e( T: _- ;;; 教程
4 J- f' a W+ P3 S8 z - (defvar *wechat-5x5-tutorial-rooms* 0- t. |. O* `, X% C. W8 p- t4 M# C' O
- "The number of tutorial rooms")# h. _1 Y; G+ y) |- q
- % L% X4 ~- a- c9 `4 e& Q
- ;;; 简化教程的定义& l* u( x/ x2 {; {3 y
- (defun string-last-line (content)4 m' k! c6 u% s4 n" R- n
- "多行内容组成的字符串中的最后一行"% ~) a" l5 Y4 E0 x
- (with-temp-buffer; e- H0 f% {/ [! C
- (insert content)/ \: g' b' s4 F$ _
- (buffer-substring (line-beginning-position)" t9 A2 K5 {. q
- (point-max))))
& L$ O5 r# Q2 {0 l) I" h
; W0 V" e4 ]6 c- (defun def-tutorial-room (prompt)
: n" n4 |: d6 l - "根据提示语自动生成教程房间。 n" S- l% w7 T7 T# J
* t# c& [- V' m# g a! p7 [0 h- Q- 1. 提取最后一行作为问题;# n) U4 A. \1 ]0 m; K
- 2. 分析问题,获取期望用户输入的内容;
t/ k6 e0 U6 @$ w - 3. 定义教程房间和重复提问房间。"
9 H' |, f* r* y1 ^. ? - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
# r% B# Q$ T" W& p! [ - (repeat-room (concat room-name "-repeat"))+ r5 j3 ]' V7 B- ~5 o+ \
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*)))) {! H8 k8 F, C& n. P5 R( P- p* I
- (question (string-last-line prompt))
. Y! D* `- ^/ f( P - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)0 J7 E* l. O. `" f% `, k) r+ P6 M
- (match-string 1 question)))% u W; p$ l" k# W, `1 H* R
- (doors (if except* G9 a: p. H- a/ i
- `((,except ,(intern next-room))
( s& i9 G/ J; u - ("q" living-room)
* a+ L6 M0 n4 |5 h+ D) F8 V' M ? - ("Q" living-room)
; `% _) R1 Q$ r2 G* w* f. t - (t ,(intern repeat-room)))
1 M( o; K: H2 D) Y' X7 ] - '((t living-room))))), \: A9 o! ] w+ p: |1 |
- (def-room-raw (intern room-name) prompt doors)
/ I0 [6 r% R: k- c3 N - (def-room-raw (intern repeat-room) question doors)))
2 A( v% d9 F3 S0 f - * R0 B, i. z6 {: m5 R0 M* J- Q
- (defun def-tutorial (&rest prompts)5 Z2 |9 k9 h8 ?$ A" J* g, }; f
- "批量生成教程房间。"
/ D) a9 P$ M0 u w7 k5 }1 B - (dolist (prompt prompts)
Z% {2 ?( u$ f }' m - (def-tutorial-room prompt)))" ^9 N' W; }) {1 L3 a
5 H7 l; b! |/ x, o- (def-tutorial) z$ Z M8 p. r, @0 g
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。1 @4 [' W0 J* t. T. m: S
- 1. 教程
, H% p% G5 m% u& y - 2. 入门(3x3)6 T/ z) H% M9 M
- 3. 初级(5x5)6 k' F6 D5 i( V- ~5 |- }
- 4. 中级(7x7)7 J# D1 Y/ c; _ f3 R& g2 y
- 5. 高级(9x9)8 Q) g( r* ^1 v+ ?; F# O
- 0. 关于作者0 d" @0 Z/ s4 G; G# z# r4 X2 N2 \
- 请选择1-5开始新游戏:* A5 b+ O) ?- t+ ?2 A+ ?
- 您现在正在游戏大厅里。
, N* j0 E6 R# N( C6 k - 请输入“2”进入入门级房间"
C; M" V7 ]3 P% f& t* l - " ①②③
]- h& m( `: V# Q- x- \3 y - 1 ■ + e( N0 y9 G2 C: e0 z
- 2■■■8 i9 u3 h+ B- U
- 3 ■ % l& D: Z# a. L* l! i9 H
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
+ J" J1 ?# E& O G! Q7 Y# K4 ~ - 请输入“22”来关闭第2行第2列的窗户。"- |9 I$ p+ v9 }! |) {2 E
- " ①②③
- J+ Q/ u5 j; I7 b+ j' ` - 1 ! N9 Z/ w: b0 v/ y. |3 f) {' k1 l
- 2
7 Z- H' |: c1 ~9 W* E8 _6 V- j - 3
% G! ]7 L: u" T5 [/ z - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
/ E3 X y0 g6 R. D - 请输入“11”,试着开启左上角的窗户。"& J& F' ?# q/ v) `0 q) p5 x1 J1 A4 H$ j
- " ①②③
/ K2 [# ?) J9 ] - 1■■ + `% ^* I7 y9 O. G2 k
- 2■
2 Q6 q( H- f( @* {1 H7 S: u% R" g/ n - 3 6 E: |& g8 ^* O7 ~. n4 T" l7 S
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。) A/ ]- X- @3 c3 B8 |, C
- 请输入“13”开启右上角的窗户。"7 Q7 ?, ]2 l& v+ E
- " ①②③) u- {3 U+ k X2 `* G2 l) N! }) f
- 1■ ■
- x3 u. f- k) j1 Q- Z, s) l - 2■ ■
, X6 n4 y* z# q - 3 & V% y: c: C: q( A
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。) j& ~& ~6 J0 N- _' D' |
- 请输入“31”开启左下角的窗户。"( \( t; d5 T* f8 `$ U' T
- " ①②③
. O3 ]2 b3 f2 b& D - 1■ ■" x5 r" F, N* X" u- Y
- 2 ■
5 w- q/ a: C/ E+ ?+ H - 3■■ ( G O3 g6 x" t4 u4 s0 F/ r
- 此时,总共有5扇窗户被开启了。% q2 X/ n9 A! A3 l
- 请输入“33”开启右下角的窗户。"1 E2 q7 T" I5 |+ \! @5 y$ {4 u
- " ①②③/ b5 b% p* Q6 _# Q6 W
- 1■ ■
2 [7 q" Q/ B! w2 @) P- f, | - 2
0 W4 j2 w' n* r1 J/ N - 3■ ■
7 P) S' j; N& Z9 {& e# p - 现在,只有四个角落的窗户被打开。
2 n5 W1 g7 H5 \8 b - 请输入“22”完成最后一击!"8 M8 ^5 K) ]! `% ^7 ]+ a0 |+ T: X
- " ①②③
1 |+ [4 F2 B. J0 ~/ T' g - 1■■■% @* Y: x: D" {) P
- 2■■■
$ w, Q( D* ]: L: l1 B% a0 F7 _" a4 s - 3■■■: ~, T# k! |; @) g4 V4 @
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")! r' K0 Y5 X- ]( H1 K3 @0 Y% \9 F3 H8 K R
2 H# u- c) K0 b4 L$ C q- ;;; 棋盘
: R* c! F3 ]/ [0 x0 @; i - (defconst *wechat-5x5-white-chess* 12288
* P! w# w) G* I% U0 J+ M ] - " ")
3 `- A' |! i; Z. J3 O
8 n8 p; @; O1 k- (defconst *wechat-5x5-black-chess* 9632
0 w; T% a1 P/ ~1 y - "■"); ^/ \5 P/ m% e
+ M% Q3 i# P, y$ Q; F2 C7 l- (defmacro with-board (&rest body)+ {& p! b. E, z
- `(with-temp-buffer6 Y9 w/ O0 ^( R7 W/ X0 o
- (unwind-protect
2 S; M5 p5 U$ J0 R - (progn
5 `* I2 @2 q# m2 r: `3 Z9 | - (if (session "board")
% e7 q1 b4 B# _- _: J' e - (insert (session "board")))
7 |( R2 I) ~9 a- j, `* z8 a - ,@body)
, J' n7 i9 w I; C7 i4 V - (session "board" (buffer-string)))))
* ^3 q) V/ ^5 Z
" t0 _* G* Q3 U0 y$ b- (defun board-init (size)9 X1 m. {3 q0 ?# Z8 Z
- (session "size" size)
. |, o5 A, W5 F6 K& o - (session "step" 0)' a, D; m- D* s* v4 V0 I
- (erase-buffer)! }9 `" ?& g% N) Y! { ~+ ?* L
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
9 g& |2 s& _- B# p# M - (dotimes (row size)- ]* W. t/ T- L/ m+ v1 {: H \/ O
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))8 _& z9 W3 [' R
" r; v$ s& n" ]1 K* W4 w- (defun board-contains-p (y x)
7 [0 h D$ a5 f. y6 ]5 m - (let ((size (session "size")))
3 s6 D1 A2 ]- f - (and (<= 1 y) (<= y size)
( F/ O! k, c# X3 a' j, k - (<= 1 x) (<= x size)))): c: R5 g6 P% Z# y
+ W2 k. f4 [( s- X. E P; F- (defun board-toggle (y x), _# M! a3 _. V/ Y6 T% x
- (when (board-contains-p y x)
- B3 }1 V* X$ T- G% r! p - (goto-line (1+ y))4 ^+ x. R; O/ |, d! Q: N
- (beginning-of-line)2 [, N6 u& y. {# ^
- (forward-char x)- L y1 ]9 p: u2 h
- (insert (if (= *wechat-5x5-white-chess* (following-char))
, Y) L: A$ u0 H, t) ? - *wechat-5x5-black-chess*4 O+ j- U) u: Y8 v/ W
- *wechat-5x5-white-chess*))% u, L W6 v) V) `2 D
- (delete-char 1)))
) ^, X6 s( O3 g; N2 _* B - " r; x& m1 I1 ?8 s9 n, f
- (defun board-put (y x)
& A4 E! T9 l6 T3 I - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
* F9 \ m7 E" k0 F - (board-toggle (+ y (first dir))' z f1 d$ K! U
- (+ x (second dir)))))
7 G. r6 a1 C5 s2 E7 X- {) G8 L
# J0 S+ a4 [0 A! X# F* q- (defun game-over-p ()* W e6 j- t' p; G( @7 n
- (beginning-of-buffer)
|, |1 l% n' _/ r - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
$ u* n7 N! C4 F
. E) X& m' U q$ r0 o$ R+ B- (defun board-show ()
8 ?! ?9 @; o% Z$ W. [4 B9 O+ o - (with-board
3 ?0 R5 {* H) e" F i - (concat (buffer-string)
6 v/ x. W! G7 l: D, M. t3 A# D - (if (game-over-p)
1 L% |1 l+ }: [ - (format "共%d步,输入任意内容返回大厅" (session "step"))- U$ _, Y1 \- R+ R4 R9 }- {
- (format "第%d步" (1+ (session "step")))))))
6 S' ^6 j+ W( t
: w7 B" b+ G$ M' A- (defun board-position-parse (cmd)6 g+ {/ {7 d: I! D) v- k
- (if (= (length cmd) 2)8 Q0 K( [$ R) G8 D8 q
- (list (string-to-int (substring cmd 0 1))
1 W7 d# U% G+ B2 j, @- h$ m* e0 x - (string-to-int (substring cmd 1 2)))
) S% h7 d8 ?9 [( @ - '(0 0)))
) O/ W# X( H/ `* X1 \" T2 v
* t4 t! J+ X" |( v' ?# @- ;;; 游戏房间& U0 t( _; ?+ X/ w& J6 h
- (defun game-room-init (cmd)+ G* p4 n/ l7 N2 B. i: J7 B: Y1 f
- (let* ((middle (string-to-int cmd))! ^8 p( O" u. F8 o
- (size (1- (* 2 middle))))
7 X: N; O% o% p0 f - (with-board) e ` `6 r# U3 n8 g0 c9 z
- (board-init size)6 X. Y: u6 j+ \ g- O6 R
- (board-put middle middle))): M0 G0 W( o8 ]$ U E! C6 E$ L0 l7 c
- 'game-room)# c' [" A" c6 M$ m6 n; {
- * C$ y, l6 {+ p7 E! q
- (def-room game-room- [- ^" p% T' w2 G$ E( F
- #'board-show
! ?% u( ~$ k4 _& L! L0 N) U - (t (lambda (cmd)+ h1 b/ X' N5 F/ i8 {: n
- (with-board
& x4 Y- d3 l. U0 K+ }% Q' o - (if (game-over-p)
% C( v+ ]& F" \2 t - 'living-room
7 D0 {/ S, @3 t" T" D - (destructuring-bind (y x) (board-position-parse cmd)
1 O$ a# l. s D8 K. H6 u4 l - (when (board-contains-p y x); r. j9 ], u% V- F( w
- (board-toggle y x)5 e6 l. i2 `/ Q4 \/ ~7 O
- (session "step" (1+ (session "step"))))
. o# w! b6 V! ^% Q% u0 P - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|