wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
: k# P0 }/ p/ ^借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
6 U5 U# F5 Y3 i, c- <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;">;; 定义新的游戏地图2 K T6 g6 X. G" T- r6 K
- (def-map "/game/5x5.el" ; 对外开放的URL9 U' t: z+ a# ?. x9 V9 V9 D
- 'tutorial-room-0) ; 默认的入口2 k' Q6 l) M6 A) X: e
7 v. A) B0 D8 a, t- ;;; 游戏大厅0 r# B* `+ n8 S$ a& T0 q: [$ O
- (def-room living-room( C- C% R; ~9 x3 C& d& \7 o7 A( y
- ;; 进入该房间后的提示语
- y/ ]" O6 O. U3 M1 O( H - "1. 教程% @) L8 I, P6 p6 a- H9 M$ o A& B- d$ i6 u
- 2. 入门(3x3)+ J2 U; ]$ Y+ c: U/ a4 g
- 3. 初级(5x5)
$ ?# @# |* {: \$ a% }# l - 4. 中级(7x7)
- z# D) Y" L* Q% a4 n& \7 I# C3 X - 5. 高级(9x9)
, ~1 N/ s: @+ q+ v: \ {* r - 0. 关于作者" T( V' D0 r$ q0 y, r$ r
- 请选择1-5开始新游戏:", P8 g& \/ q2 E( ^) J8 @
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名; v. F [0 J& M7 n6 q
- ("1" tutorial-room-0)4 b' f5 n6 z. b( @, y W8 u. q
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
: d8 R8 E& a3 @ - ; 相应的返回也可以为函数,动态返回房间名
( N1 c! c7 c; l( g+ v4 ?' t% o - (t living-room)) ; 如果条件为t,为永真* ^$ R8 w9 z1 e7 I4 ?8 l
* L# J8 v) [" ~. H- ;;; 作者信息
2 D9 |- D6 T6 v/ _ - (def-room about-room
) G; W% ~; F* J9 n8 \8 E6 X" T - "作者:redraiment
) b* V! h- _. y$ k4 S - 微博:http://weibo.com/redraiment$ T4 U5 W: r: x" c
- 有任何建议,欢迎在微博或微信上联系redraiment。
# a1 O1 r0 Y, u% N- L' \ - 请输入任意数字返回游戏大厅。") w: U4 K4 A9 b0 d- j
- (t living-room))
! S1 h1 p" e+ H- N$ S! n H' X - " W2 H' G7 B4 {- L
- ;;; 教程
7 V8 r, K" ?' { - (defvar *wechat-5x5-tutorial-rooms* 0
5 w. B! q4 a/ K) J7 ~8 Z - "The number of tutorial rooms")
1 [, T5 H" V _5 u8 x& n! `$ a; M - 9 {0 U9 `- | L- S3 ?1 Y2 v k- v
- ;;; 简化教程的定义
* X5 r' z% X* I. N - (defun string-last-line (content)
. j! H' l$ `/ {& x - "多行内容组成的字符串中的最后一行"
f0 u5 n: P6 ^ - (with-temp-buffer: l+ D% f: ?* ]
- (insert content)' x0 @; J ?: o$ f+ t7 u7 j
- (buffer-substring (line-beginning-position)
$ ^* Y3 K4 H) S. o, g) e - (point-max))))
5 E* a- M& n+ p2 t: H( V1 e$ p - 2 l' ]. ~" p- {( ?
- (defun def-tutorial-room (prompt)( A8 m/ p1 a+ D: X- K
- "根据提示语自动生成教程房间。
% p' T9 T) q2 d/ [ - 9 m0 G" I# s2 o& t( P, n* x+ ^/ ?
- 1. 提取最后一行作为问题;
- P: d% P& Z% \% d4 {2 P - 2. 分析问题,获取期望用户输入的内容;5 b B, t/ Y2 k# B% ^
- 3. 定义教程房间和重复提问房间。"+ a L& {; U' @4 r+ ~7 Y' f
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))4 h7 j; }* D7 i$ z
- (repeat-room (concat room-name "-repeat"))
) @0 o( y+ |. E! }* F3 V9 H - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))9 H" L% L8 w9 j$ [0 A0 ~3 `- i& h" c
- (question (string-last-line prompt))
3 Q2 |8 T; Y( ^5 A8 t - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question) P. P+ p' F2 M8 Q1 F; ]
- (match-string 1 question)))8 R+ Y: N) j$ A5 b
- (doors (if except
! L4 X9 a: {+ K1 L# h1 X - `((,except ,(intern next-room))/ M9 ^1 I- A% p; t3 {
- ("q" living-room)
% B1 W9 }) f. y! c& i% {% ]& V2 p - ("Q" living-room)! n" G6 }7 J# `! r: ^7 [
- (t ,(intern repeat-room)))3 h. o" h# u. s! C" ^( s: r# _
- '((t living-room)))))
" G9 e' Z8 w& q$ r( S0 J+ c - (def-room-raw (intern room-name) prompt doors)
5 O+ m2 U8 V0 V* _6 {2 `2 P$ y$ I - (def-room-raw (intern repeat-room) question doors)))
/ m) B V1 J' \% Z. K - 7 {5 B) Y6 I$ W g! o N
- (defun def-tutorial (&rest prompts)
! `; Z1 ~8 v# ~4 E/ I - "批量生成教程房间。"! K f+ E! y$ h) J) _( L& n
- (dolist (prompt prompts)
1 u) E$ D$ [, U- j6 @ - (def-tutorial-room prompt)))
8 D( W# H9 A* |7 v" k D
4 M1 t" u7 s: N+ x, ?1 m9 r/ p- (def-tutorial
# Q' V# R& O2 X; ` - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。6 z8 v D9 P9 ^: l6 \* r
- 1. 教程) x& k* G' t! M8 J* e1 e3 i
- 2. 入门(3x3)4 s# j M! A; r v ^9 H- {3 \
- 3. 初级(5x5)
, Z! X0 Z) W+ f& B$ @+ q* `) ` - 4. 中级(7x7)
" h+ _+ r) N! w. I6 L n# Z. E$ p - 5. 高级(9x9)- T4 O4 _2 A, I2 d* a
- 0. 关于作者& N+ k K. q' {+ s3 S
- 请选择1-5开始新游戏:
3 y3 m( D8 }& C: v2 ~- D) t - 您现在正在游戏大厅里。" v7 r' ?- i& R) T" n! }
- 请输入“2”进入入门级房间"2 B% K5 d3 |0 g& B
- " ①②③
' g ?0 r0 {/ D d - 1 ■
: O. o+ }3 ]& {% i) {) f - 2■■■
0 c' q: u1 I. ?# v - 3 ■
& i6 w0 Q! T- s: J5 q- ? - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!, K$ e9 |5 Z+ A- V0 r# q+ v
- 请输入“22”来关闭第2行第2列的窗户。"0 a1 v) I) g; w+ G W0 F# f4 U$ F" p
- " ①②③0 b2 p+ N9 M# o: k& D9 j
- 1
2 v) B- w! G) Z - 2 7 _+ ?6 K8 ? n
- 3 ' u8 R0 j7 h1 t2 T7 S' {/ {6 m
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。. i$ `$ h8 R- e" v8 K! U* G5 l
- 请输入“11”,试着开启左上角的窗户。"
* S: w% ]. F4 n; T) ?4 F( L - " ①②③
; y R# M$ {& w5 \. a3 N y - 1■■ ' T; J# @ v- M) B0 j0 e" l
- 2■
7 ?. Z5 Y* X, x2 G/ o' ~ - 3 * }% } O* S5 w1 v7 Q* s' N2 y
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
+ F' J5 _9 n& B# u3 M4 e* K - 请输入“13”开启右上角的窗户。") N% r+ q- V- H+ j* F0 D8 }% T
- " ①②③2 z+ S* C8 j: O' }( ~2 f. g6 g! h
- 1■ ■
( x) x r( [) w! E8 X! k" u - 2■ ■2 t5 e8 M1 Q5 w& Z* H8 o
- 3
) O2 V: w3 S+ P& U4 c$ L9 s! Q4 R5 | - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。. |( S& v7 d: L. k3 h: H/ N) l0 _
- 请输入“31”开启左下角的窗户。"
/ b. |# o$ w! r# U+ S - " ①②③5 F) k9 `) {: r" |
- 1■ ■
9 `7 G% Y" j p0 s5 N - 2 ■
$ T* m! ]+ ]( O& W- Y j - 3■■ + U8 o% a- ^: j% c! F
- 此时,总共有5扇窗户被开启了。
6 z% q3 ]' o7 o. g7 c - 请输入“33”开启右下角的窗户。"! k' j, ~5 g9 p' R( \
- " ①②③6 C. ^( e: \2 q5 G( Q; ?! H1 a" X$ C
- 1■ ■* A8 {+ e% f3 D: w/ v* B' P
- 2 + A; c m+ j. F+ E6 D
- 3■ ■
2 V6 T1 T" `3 [# ~% A# |9 a - 现在,只有四个角落的窗户被打开。1 N! ^% o( i& X- _
- 请输入“22”完成最后一击!"+ \, f/ S$ T6 B' n
- " ①②③
# z# n. o2 a' o$ R - 1■■■3 ?# H# V# B) u% F# w' ?! ~6 e# Z
- 2■■■
; @1 p( a- e$ R' W4 h0 } - 3■■■
3 W# T9 j; U" x( ?9 p+ x: H" O5 y - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
% O' M3 \# g5 g. [, i - 6 c) T8 P, ?; g5 U7 l( @
- ;;; 棋盘
5 Q" t) d4 m! g7 o g) E - (defconst *wechat-5x5-white-chess* 12288# G: g# t. w' E
- " ")
% j! ]5 N4 n/ {7 K: }5 ] - ) i8 p/ C- r/ J
- (defconst *wechat-5x5-black-chess* 96329 A6 I, v8 q7 u l( L4 o
- "■")& n* {+ J* A, u7 _; b I1 x
) Z' R) k2 a' s* Q8 @- (defmacro with-board (&rest body)6 _' |: x1 t. Y$ S2 G" ?! c+ O
- `(with-temp-buffer
/ V M( V5 A6 }7 O - (unwind-protect; D* n+ x } F. m' V% L2 B
- (progn
& ]3 W9 C' ]8 p& _5 [3 r - (if (session "board")
! z$ k+ Y$ n7 M$ d# ` _' w/ a - (insert (session "board")))
9 y; G9 {! \! M0 u5 _' |3 q - ,@body)) G ~; ^$ @8 d2 ~: O( P
- (session "board" (buffer-string)))))
9 k4 J0 I2 y" _ J+ k$ l) [: u
/ ~9 R9 J( |9 x8 s4 ~- (defun board-init (size)+ r& p3 \8 {/ G- w# b0 [' n
- (session "size" size). o$ A- B4 a1 t
- (session "step" 0)' c+ t/ x! o9 b% k, W- v# N7 C
- (erase-buffer)' r& `3 p. K/ D, @% R+ {
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
# f1 F& g# |8 y - (dotimes (row size)
1 B$ ?' G; B7 V. } - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
4 e4 H6 E- H# C0 x2 E - 8 L, I- q9 V# t' o
- (defun board-contains-p (y x)4 @- G$ j! }$ I; n0 N3 {& o5 D7 ^7 ]
- (let ((size (session "size")))
( o9 e8 U' d8 |4 x. u: v - (and (<= 1 y) (<= y size)5 f4 ~8 ^4 V: x6 F( V1 }1 C3 Y4 z
- (<= 1 x) (<= x size))))
& R9 S- D% }6 H
# X/ P& w' J% Z% G/ V& z3 j! [' W' o- (defun board-toggle (y x)
9 e6 D, P; b" O0 t) [ - (when (board-contains-p y x)& K h1 g3 s' s9 F2 } p4 u! X
- (goto-line (1+ y))' o/ t% l; R$ i( \4 G9 y& C2 R
- (beginning-of-line)4 |/ o0 e) `% O% l
- (forward-char x)
6 k1 o; J6 M4 z, `" E9 w9 G - (insert (if (= *wechat-5x5-white-chess* (following-char))
# E3 n- ?: D" p4 @) N# k6 _" O1 t' V - *wechat-5x5-black-chess*
" f. s* ?3 P" ]! F0 m2 _, i - *wechat-5x5-white-chess*))
. ]0 r* ]- f8 ~( s+ B T - (delete-char 1)))
; x+ x: C7 n/ P% X1 e4 X
6 T% x0 ~: l' R! x P/ @- (defun board-put (y x)
( w! l3 a- | |$ C1 H5 A2 d) i$ K - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))( d# T# V' c. ?& T
- (board-toggle (+ y (first dir)); H4 ]3 q' B. X% j% Q- Q
- (+ x (second dir)))))
' y: w% c( ]9 t- c1 Q, o _ - # K% R/ l$ l! p6 L0 Q# O6 h
- (defun game-over-p ()
/ s, o3 n B& b+ @6 ~# t6 W - (beginning-of-buffer)
c A, Q+ H0 G }+ D' h - (not (search-forward (string *wechat-5x5-white-chess*) nil t))); E5 _& v" y, _1 H2 E" g
. ^) z; L2 |. `" g0 R- (defun board-show ()( p' H5 H2 i5 L3 h2 p; r
- (with-board1 V0 y0 F; X% o# W0 r6 {* M
- (concat (buffer-string)3 j0 [% K, _; A. f
- (if (game-over-p)
n1 T& {' |3 ^5 P" h$ P7 p1 g - (format "共%d步,输入任意内容返回大厅" (session "step"))
, y% a0 D4 h' p0 H G6 c, O& b5 K - (format "第%d步" (1+ (session "step")))))))
, ^& e( S h+ {- y3 V: P3 M% X0 f9 _+ T
3 G& h% c# t+ [& K" `% T- (defun board-position-parse (cmd)1 i" c- O" U# R/ r
- (if (= (length cmd) 2)
5 K- f! l0 _$ ` b - (list (string-to-int (substring cmd 0 1))
+ s* ~# ^4 A8 @0 [1 `& J - (string-to-int (substring cmd 1 2)))
' N6 f; {: g. o1 m - '(0 0)))
" {. [) u# _' k) p+ e1 M
2 _* \& d# m* z- ;;; 游戏房间
9 \3 {5 I5 @$ Z. X - (defun game-room-init (cmd)
7 o2 @' m0 b8 t8 { - (let* ((middle (string-to-int cmd))1 f1 w$ ~* A4 _. v0 M2 C
- (size (1- (* 2 middle)))): Q. z9 r4 ~# C' G$ Q
- (with-board
- w. e1 C4 a3 P- h: m/ H7 C. | - (board-init size)
* _# |/ n$ z- t8 @) f0 A/ U+ G - (board-put middle middle)))2 {/ l* S B) m
- 'game-room)
+ T q) q$ [9 d+ Q3 ? - $ {% ]9 a+ C6 k P u- _+ J
- (def-room game-room1 l' s2 F1 b' t
- #'board-show' i( z0 z: j/ ~
- (t (lambda (cmd)
t2 P) F. t5 _, f1 \9 e - (with-board
' e$ t, \! j- Y9 ?. g5 M - (if (game-over-p)
: Y1 t2 u4 W0 M1 y. D7 z! Z/ ^1 K - 'living-room# {0 D& r: u+ K
- (destructuring-bind (y x) (board-position-parse cmd)
4 }3 S5 {! `! b6 }3 P m& Y+ s - (when (board-contains-p y x)
) O& {# t% t6 W& M; E - (board-toggle y x)! K, d6 W. R% t. g- k+ D
- (session "step" (1+ (session "step"))))
9 v: r2 s! S! e$ b6 D6 g - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|