wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
6 a/ [2 x/ f: j& A* D% O; T+ Z借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
( }- P6 `( M9 S: {$ ? S- <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;">;; 定义新的游戏地图7 p$ R1 }1 c+ ]8 {. v5 i
- (def-map "/game/5x5.el" ; 对外开放的URL
! O5 u- q3 z) ~ - 'tutorial-room-0) ; 默认的入口+ A3 m+ L/ J& Z7 m1 B
6 I0 R7 Y: b! s- ;;; 游戏大厅
9 P3 S" X" f0 Q5 T, g - (def-room living-room
W& L) {2 c( z* ~7 T. t1 z" T% J/ z - ;; 进入该房间后的提示语
/ I; I* H9 }' [: ~; c7 h7 R/ U - "1. 教程/ G2 g1 s5 t9 M4 [8 L
- 2. 入门(3x3)0 C" E) f. o% b9 i9 L( Q
- 3. 初级(5x5)3 R8 X. \( D7 @! M
- 4. 中级(7x7)2 W; B7 C" \0 o; `
- 5. 高级(9x9)
, b+ W4 \$ S0 z7 ?9 X - 0. 关于作者
3 m0 Q0 P0 g" q# F, G+ w; { - 请选择1-5开始新游戏:"0 ]1 [1 n8 h) X! u& ?) Q
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
* Y, O/ n5 W7 v9 k8 q0 M; ? - ("1" tutorial-room-0)/ d5 j+ x+ x2 ]' U) [! d
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
9 u! B! f" c1 w - ; 相应的返回也可以为函数,动态返回房间名& _1 Q& ?. s8 z: p
- (t living-room)) ; 如果条件为t,为永真- U' M: X+ p+ b1 O" Y3 p. N \- y
( i/ E' {4 A# F$ M- ;;; 作者信息
3 y7 i4 D- a+ v& F; ?4 ~ - (def-room about-room
W/ \/ T* j# H0 a$ N5 h6 y" ^ - "作者:redraiment. P2 T# U) T" N' w4 A6 z
- 微博:http://weibo.com/redraiment
+ O ^) ]$ x" c. I/ G/ N - 有任何建议,欢迎在微博或微信上联系redraiment。
7 }/ h& B5 {1 h& M - 请输入任意数字返回游戏大厅。"" S4 u6 m; p3 j! `% r- k ?2 l
- (t living-room))
~4 l% F8 s0 A2 s6 S% G
9 e" c1 ~8 ?4 M8 z' ]& n" K4 ^- ;;; 教程' ~$ s |) z3 V) x3 O+ W
- (defvar *wechat-5x5-tutorial-rooms* 0
3 Q/ O5 [$ J' h9 v) o" M! m1 o. \" R& S - "The number of tutorial rooms")1 a) j/ O5 \% R
- - E0 D3 p) R9 \
- ;;; 简化教程的定义
+ [2 q% t/ O. Z- }# } - (defun string-last-line (content)
* k# c- ]+ {$ V% v - "多行内容组成的字符串中的最后一行"
6 @3 d! p# [8 O9 C- w6 l - (with-temp-buffer
7 c% B! c# `: P - (insert content)
( v0 f7 U/ o7 K# e - (buffer-substring (line-beginning-position)
7 N9 B8 r$ l; x$ [( N8 ` - (point-max))))
' h6 K. k) [, h% O - , h: B6 y) O; n* j" [
- (defun def-tutorial-room (prompt)
# i4 Q3 X: R; ?7 p" U H3 a - "根据提示语自动生成教程房间。' i* z+ I$ s( z( ?8 R! l f" o
8 t0 u8 `0 \2 Q$ T# M$ j8 n- 1. 提取最后一行作为问题;
3 u# {9 {. p0 @. H - 2. 分析问题,获取期望用户输入的内容;
- F& ^2 {# h2 I# ^* C - 3. 定义教程房间和重复提问房间。"/ j& w, }9 c. ~$ [
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))9 L$ Z+ G' |0 x( S. s" Q
- (repeat-room (concat room-name "-repeat"))6 k+ z$ N. E* E+ T6 N' t* Q
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))+ y6 y2 v7 [. A6 E
- (question (string-last-line prompt))' J/ P* K, I: r0 c
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
' n! }; E, ]" |: C. \ - (match-string 1 question)))
! \& [0 E4 E; ?9 p- E - (doors (if except
2 V" J& [% ]0 ~2 @9 J6 p9 C - `((,except ,(intern next-room))9 D7 z* @6 F( U5 F1 H, ^' |* a
- ("q" living-room)0 S7 T/ U* |3 `) B) d- @# Q# Z
- ("Q" living-room)* i/ A+ x% y# l
- (t ,(intern repeat-room)))6 K; K: P( `! U/ ^4 j
- '((t living-room)))))
; ^1 J4 S$ P5 k+ R6 ^ - (def-room-raw (intern room-name) prompt doors)- K% X: @5 {) ]( a
- (def-room-raw (intern repeat-room) question doors)))
# ?4 g2 q4 ?6 R0 K2 h - : k! b1 m" g6 K" Y) M
- (defun def-tutorial (&rest prompts)
) F1 \% Z+ x3 c/ F - "批量生成教程房间。"5 ~! U( w) E4 U' s$ S0 m3 M/ |0 ~
- (dolist (prompt prompts)
7 ~) |. z x3 E8 g; E - (def-tutorial-room prompt))); n2 H4 Z6 e! F
- # U' I6 u7 ^) z
- (def-tutorial3 l3 a9 c0 [. w0 ? w! R- h# R
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
/ R7 j% l1 H5 B$ r4 U |. | - 1. 教程
1 @( G/ ^5 z6 Q: Q - 2. 入门(3x3)4 q& e: p, Q. O) ~( N
- 3. 初级(5x5)
, ~" `* Y9 V! _" \3 v - 4. 中级(7x7)8 A$ v, Y; i4 v' N
- 5. 高级(9x9)
- Q) [6 B5 P& M& V% B; @+ G - 0. 关于作者- h5 s- E/ y" X/ o# y6 M% r6 i1 h
- 请选择1-5开始新游戏:' Y8 A5 N* D! g' T
- 您现在正在游戏大厅里。$ Q, G) A0 y) u4 e
- 请输入“2”进入入门级房间"
. M7 v* @0 v5 W$ R - " ①②③
0 M7 S1 {' u- v5 X0 j [' h9 _- S - 1 ■ 8 V9 f6 E/ i+ w* z; I1 u
- 2■■■7 K* W* W1 H* l' Q& m, G& R+ f
- 3 ■ ( Q% E* B% h' Z0 Z
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!( u$ t5 @0 A* K% m$ g7 p8 A
- 请输入“22”来关闭第2行第2列的窗户。"
/ x, ^8 e! G- [* d - " ①②③% K x, ]' C, M: \% b7 U0 P8 e
- 1
$ o' N% Y1 v+ ~ - 2
; [' V0 j* J7 _& [1 ` W& g& m$ d - 3 8 j2 @' N( z$ z+ z* u* y- S
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
5 [5 d$ I- H4 P ?, c& a2 e7 [ - 请输入“11”,试着开启左上角的窗户。"
* b& Y/ c2 U" I, E& p" G2 ? p - " ①②③; S. d$ M! r, }' b% d3 ]
- 1■■
4 b. Q K2 c+ P0 f4 y6 j$ H - 2■
0 n! x" D/ G8 p [- T5 F. q - 3
3 h; U" j; O7 V4 i7 a* L9 L - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
% |& a) m& d' K" _ - 请输入“13”开启右上角的窗户。"
$ i1 L/ V# k* d' e3 P; Z - " ①②③ y. N0 o" H3 T- b2 P' @
- 1■ ■
' |, n5 X% G) I) B4 O- V - 2■ ■) f% P1 J1 f7 D0 ~
- 3 " \3 E7 W8 W, l
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。; Z2 [; y. \$ E; E, N1 y2 U8 X
- 请输入“31”开启左下角的窗户。"
: f F3 t g" V5 P1 z z1 c: M - " ①②③( Z7 G+ `% ^2 i$ C' @
- 1■ ■
( P- C; Z" j" _- ^* w$ C( ^# G - 2 ■
* Q3 J2 E" ^( H" B' r8 p2 A - 3■■
/ I0 H0 y& i0 E0 L - 此时,总共有5扇窗户被开启了。
4 N$ @4 ?& i W* x9 N - 请输入“33”开启右下角的窗户。"/ }8 r. [! [: Y& A+ D( l% {9 d
- " ①②③
, n- l: g1 L0 M+ u - 1■ ■
+ i7 r2 u; b/ @1 Z6 q2 h6 v - 2
1 t# E2 R/ l0 ]3 ^2 u9 a, |2 p - 3■ ■
+ m. a7 b* ], m2 R - 现在,只有四个角落的窗户被打开。+ ~8 d5 s, {( t& y
- 请输入“22”完成最后一击!"
+ L( ^# Q o' J& P - " ①②③
N% Y# U4 @' T& u& ^ - 1■■■
, k' Z$ P2 n- H. o& o: ? - 2■■■. Q1 s! S9 h6 q- w! K! w3 j
- 3■■■! a; s9 j9 E( m7 _% J+ ^" ^
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
W/ i/ b. A( K* I# C. L( X0 ]4 R - $ w. |! `# N# R" Z7 {
- ;;; 棋盘, E ?# w* I1 V3 x! N
- (defconst *wechat-5x5-white-chess* 12288
* k" J& Q# v1 W - " ")
5 Q5 f2 G3 K/ |! n
# q9 o8 x- U X9 T% Y5 D- (defconst *wechat-5x5-black-chess* 9632
* A" L5 o5 i* S6 R/ { - "■")
" d c' g- z8 s$ d! X) m' s4 G- Z
/ N; G8 A4 I4 ]. J- (defmacro with-board (&rest body)- m" h( I Q T0 s& p9 o) U3 Q2 p& s$ J
- `(with-temp-buffer% t8 Z4 ^: X7 G( l3 @
- (unwind-protect% ^* s7 ~0 O& {& S m/ E
- (progn
/ D# _( X8 V, ~, v) w - (if (session "board")
- u0 p( W4 V( o2 F( ~ - (insert (session "board")))
. }. ?1 y6 p' p# e; u - ,@body) X8 G1 M6 J- O! H- i
- (session "board" (buffer-string)))))7 e9 V9 q. n& f
, I& o; N+ o8 k- (defun board-init (size)
8 q" t2 G5 U Y. c" H. w, \9 [6 R - (session "size" size)! }( {5 x9 G( t! I
- (session "step" 0)
4 c# a @/ g8 h- _. J - (erase-buffer)
3 y/ i# h; O: b* s; _9 o - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))' _& W4 v: h5 d+ h0 R2 c2 J
- (dotimes (row size)3 C1 a( d V: V
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))* j! A y6 T( U5 z7 M, N. g. V. X( u
# F" {3 g% G2 q8 D7 A: Q/ H- (defun board-contains-p (y x) Q. g, l( Y0 J( k+ E7 ]9 j
- (let ((size (session "size")))
" q+ L3 h; \, c& @4 [1 f1 B7 d& | - (and (<= 1 y) (<= y size)" s$ b/ Q& t4 p4 T F. h( v
- (<= 1 x) (<= x size))))
. n j; i: e2 |) _# n" u - 5 f) u) b/ ~$ n( Y! t
- (defun board-toggle (y x)& _, H c. }0 w E1 q4 a4 Z! q2 @
- (when (board-contains-p y x)6 z- Z6 ^) J% P/ C
- (goto-line (1+ y))
& ?8 Q% j8 R# M4 E5 A2 X+ ?6 _ - (beginning-of-line) w" d& B! ?( ]( E; }
- (forward-char x)
g3 t, A# R, W) A: u! f) N. {$ C3 ^ - (insert (if (= *wechat-5x5-white-chess* (following-char))
7 P6 e2 }7 }4 H9 [ F- ] - *wechat-5x5-black-chess*) W# d" W- Q& |& ?: n/ R x, t1 j
- *wechat-5x5-white-chess*))
6 j# E3 b( E! ~9 b( T( M - (delete-char 1)))+ ^6 e) H& F! ?: J7 m
- , ^) ^9 ?; k O. Z$ [; T9 l
- (defun board-put (y x)
, S" e; U1 e v! ^# \ - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
6 {; |% k8 y# j) P$ \ - (board-toggle (+ y (first dir))
6 c; F5 A. C1 L8 `5 |* i& l7 k+ c6 _ - (+ x (second dir)))))
& a' x* x' y7 K - # L0 P8 j$ M2 z: p2 K3 `6 e5 b6 O1 Z
- (defun game-over-p ()* p0 l; s( @4 \. `& N4 s
- (beginning-of-buffer)0 g) m/ I' @# N/ v$ H+ }
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
1 B z) c2 ]- Y0 t; ^
: v# U4 u4 j7 w) |( c- (defun board-show ()
" Q( E5 t: D9 }' i1 D# D4 g9 V! p - (with-board' h6 ?, t: ?8 V9 s/ {8 j! \
- (concat (buffer-string)! m: W2 s& R9 ]. }7 C) I
- (if (game-over-p)' b+ ?" C. f, c7 y) u. b5 J
- (format "共%d步,输入任意内容返回大厅" (session "step"))
6 i% i5 Y: \& N - (format "第%d步" (1+ (session "step")))))))
9 \) O5 s! M. c! |1 H
* m6 n5 h# U4 h) L! r1 g- (defun board-position-parse (cmd)
, p8 X3 k0 G- v6 y k h - (if (= (length cmd) 2)" T' u- i" J) Q0 T+ L0 N
- (list (string-to-int (substring cmd 0 1))2 N' K; b0 w" _; J5 o" X
- (string-to-int (substring cmd 1 2)))
8 g- g; h, }" T) i' g - '(0 0)))- O* l5 e. p: q7 [
& }1 H7 z# ~, q% r' q- ;;; 游戏房间
/ a- |5 A- B! l4 { - (defun game-room-init (cmd)9 |/ a/ Z& Q) `" M& ]2 {
- (let* ((middle (string-to-int cmd))
) J1 ], z- X, `0 ^8 ` - (size (1- (* 2 middle))))
$ _, ~5 d4 [" N$ D1 m - (with-board3 K% C( H1 b8 I. u1 [4 d
- (board-init size)' {/ e0 N/ p0 E) B# K7 |- C w
- (board-put middle middle)))
5 s+ t( Q; G- p0 \, a! f - 'game-room)
# [% w$ W( R' W
3 U( \' n1 `. h6 C- (def-room game-room8 q& p5 _% q! }0 f6 r
- #'board-show
& Z5 F/ S# z2 N3 s# a - (t (lambda (cmd)
6 G1 q/ ^9 a! H- A, v* z - (with-board
( o+ C* D0 t7 s9 ] - (if (game-over-p)
- a1 ]* U4 @8 t: ~: Z6 B# }- _ - 'living-room- u# C1 B6 B- o3 O% j2 F
- (destructuring-bind (y x) (board-position-parse cmd)
0 m- M% D7 L; ]- L: D1 k' l - (when (board-contains-p y x)
+ W% d: @* U [5 O2 C - (board-toggle y x)8 Z" H8 t' w& l+ G3 ~( x- l# a
- (session "step" (1+ (session "step"))))( t+ l$ o+ P7 w. i& x
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|