wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。1 R: ^5 O' p3 @
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
- X& b4 N' Z+ e- e' F- <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;">;; 定义新的游戏地图
* m- U# V, e; p - (def-map "/game/5x5.el" ; 对外开放的URL
' J4 M" |7 m$ `; x1 b) @# A5 h" N# a - 'tutorial-room-0) ; 默认的入口2 \% Z6 v8 T, O$ N
- % U8 J6 F; G t
- ;;; 游戏大厅
: f6 R) h/ T3 w, ?* C/ c4 h - (def-room living-room
- e2 H) W+ r8 j, P2 y) Z - ;; 进入该房间后的提示语
% X. e$ n6 K# H - "1. 教程" P5 R k( V2 n% r# r/ M
- 2. 入门(3x3)
6 \% {6 S. W* n0 Y! V6 j3 a1 M - 3. 初级(5x5)
: f: I$ S% c5 u - 4. 中级(7x7)
( S8 f& g9 y+ i+ I9 W - 5. 高级(9x9)
! M0 t. y5 ~6 R. t - 0. 关于作者
& _8 f1 X/ Z4 t - 请选择1-5开始新游戏:"
: P/ l) R, U5 k/ Y, b! { - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
" C: k, \1 a w8 [/ x9 N - ("1" tutorial-room-0)4 f) H( D- f: C
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
% @- t' \ b3 T* w. r - ; 相应的返回也可以为函数,动态返回房间名7 O7 X8 ~# i1 F/ m+ N9 c2 b
- (t living-room)) ; 如果条件为t,为永真
! E. y) T, S' M; i- A: t
) C) y) w7 F& g' N- ;;; 作者信息
1 [" e* E( p1 K$ Q9 { - (def-room about-room- b! B3 A) y2 k9 `8 O( Q j4 D+ l# z
- "作者:redraiment* R1 h7 w+ ~3 E. ?' A6 ^( H
- 微博:http://weibo.com/redraiment4 M* c' s7 y; J* Y" [8 U6 K. i4 ?% W
- 有任何建议,欢迎在微博或微信上联系redraiment。
. E+ t: @3 l* R - 请输入任意数字返回游戏大厅。"+ v; G0 }& N" f7 s0 z o! ~
- (t living-room))
/ J6 C. Y) o8 V
2 B! l9 u0 z: b' t- ;;; 教程' B- f( f3 M* C4 C0 s7 Q
- (defvar *wechat-5x5-tutorial-rooms* 06 u; j! m; {) \' x; P7 n( F" F
- "The number of tutorial rooms")
! p' {3 ?, _, E0 r
4 [3 }3 ~4 e7 K; }- ;;; 简化教程的定义
; j O1 A9 x; Z - (defun string-last-line (content)
5 R! I+ P* i' j" [ A7 S" m# t - "多行内容组成的字符串中的最后一行"
: @/ Q0 y) c+ A1 B6 U# ^ - (with-temp-buffer
, Y* N& a2 G/ w: c- r e% V - (insert content)$ c0 H& Z! K4 l
- (buffer-substring (line-beginning-position)
" {' r( T/ s' l) g' A0 r- v$ v E - (point-max)))). M! [, b0 D/ y) K3 j
- : [) R$ x H* P1 ~+ C5 w. X& A
- (defun def-tutorial-room (prompt)
% ?6 z6 E9 {5 U5 a - "根据提示语自动生成教程房间。5 r0 d# V% t, s# f# o
- ^- e$ X4 Z2 d; e4 W2 I# Q% F- 1. 提取最后一行作为问题;& r7 D; N, _) O! {& H
- 2. 分析问题,获取期望用户输入的内容;# N5 d+ j: ~$ N; }9 h
- 3. 定义教程房间和重复提问房间。"
( o; [, j3 A" I - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
( j- O' I8 F" Y% U; x; @ - (repeat-room (concat room-name "-repeat"))
- S( l9 a) b8 Q5 [* r - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))" b o/ {8 ?6 A
- (question (string-last-line prompt))
2 j/ h, V) p( W - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
0 s Y, b6 T$ ~& N5 v4 E( X - (match-string 1 question)))
l% s2 B. @8 T - (doors (if except
: D& Y/ l4 D" R. |) m* x - `((,except ,(intern next-room))
$ Y0 o& |# P# C* J/ m' {" H - ("q" living-room)
0 R2 h2 u0 E7 ?9 P - ("Q" living-room)3 w6 k7 ?" j! @& H% D6 C( l' V3 w
- (t ,(intern repeat-room)))
# [2 [( o7 K; h B' Y8 d, g" K& b - '((t living-room)))))/ ~1 g$ u- i0 N" l; |' p- K4 O) |5 b
- (def-room-raw (intern room-name) prompt doors) R# B/ q6 q- } p3 a( c
- (def-room-raw (intern repeat-room) question doors)))
% Z" [) V! E/ }! c, L, g! D) D/ @
+ S& s$ ^/ _& b4 ]7 @: Z- (defun def-tutorial (&rest prompts) f5 ^2 \3 h& e5 E. R! X
- "批量生成教程房间。". C8 r5 l/ y+ C3 k
- (dolist (prompt prompts)
% S+ ]3 [5 A0 e* _ - (def-tutorial-room prompt)))! L; h; ]6 m7 M8 Q
- , P& L0 }* c) r g, Y6 H2 J
- (def-tutorial
. A( s- X0 a" a- N' X1 s - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
5 E8 K. X" k" w, G8 a6 [ - 1. 教程3 {( @1 ?3 F* Q; s
- 2. 入门(3x3)
; e9 F# o! ?$ F( s- p5 L - 3. 初级(5x5)2 \! [8 [# ?! z+ j; f* ?4 ~; W
- 4. 中级(7x7)
1 e% d% ~: U) @9 y# u+ a - 5. 高级(9x9)" V7 _9 h/ P$ ]
- 0. 关于作者( S8 l! B! z D2 h
- 请选择1-5开始新游戏:
8 s8 n! a/ c& [$ z - 您现在正在游戏大厅里。
/ q8 J# u8 V8 O9 p& M1 s - 请输入“2”进入入门级房间"6 R( a. w2 ?' A0 D9 f
- " ①②③
3 ?, u6 P6 }% y8 d# X - 1 ■ 3 P: F: _ S. A5 Y2 H
- 2■■■
2 u5 Z2 }- A8 I9 @( f7 V0 S8 N - 3 ■
# W- r7 N5 b: O% U' s1 s - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
: @; s" N8 N# @; \- I4 a - 请输入“22”来关闭第2行第2列的窗户。"
" x! m/ B9 d; W2 L! j9 W - " ①②③
6 ~4 g1 O7 N m - 1 9 C8 B2 N# g" ]0 [1 P6 i
- 2 * o6 d! u5 B: C% e0 v S6 l) R! P8 Q
- 3
1 a4 D2 V9 {6 y- i4 F1 v ^: G - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。% |3 M3 |& s2 B8 F W6 o
- 请输入“11”,试着开启左上角的窗户。"
* [3 d! f: r' t$ f* e: B - " ①②③7 v6 F P8 R6 ?* a, q6 E
- 1■■ 1 M+ ]# s9 F' \( i6 z
- 2■
5 T; C7 [; w$ z0 C4 ]* A n - 3
& Z" ^3 U$ X( f" U4 O4 k5 Y# _ - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
# x! H/ R+ a% f - 请输入“13”开启右上角的窗户。"
/ ?& V6 a3 ]! o- ~ - " ①②③" s" z" E& K4 {/ l" J s6 V
- 1■ ■
: S5 b2 K" O1 y) @" u7 A! [ - 2■ ■
4 t5 ?* a& w% {0 @8 j: J/ y0 Z - 3
! ^) u' d" ?4 Z2 Y, q0 u! K - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。( A* F( }3 S! \& N* z' S' u
- 请输入“31”开启左下角的窗户。"2 C( D7 P% ~1 s0 L) i% Z1 B
- " ①②③
: [9 H; E, b* ~4 u9 d0 o. U' L - 1■ ■
1 }0 R% t/ z4 G( p- Q. z - 2 ■6 ^% [9 G4 ~: x
- 3■■
7 g% I* {) _" R9 E3 b* Z: ^ - 此时,总共有5扇窗户被开启了。8 |. M; @1 V/ R% B$ a+ u$ Y
- 请输入“33”开启右下角的窗户。"" w4 R c" t! S# ?1 j
- " ①②③
& s7 C6 B& K/ d - 1■ ■
+ I C% r" \) j. R - 2
3 u. l' K9 U% m - 3■ ■0 _1 ~0 s y& N m% P2 ^/ f1 F; L0 B
- 现在,只有四个角落的窗户被打开。* H0 U4 H2 ^1 I: c; }; I
- 请输入“22”完成最后一击!", Z) @- o4 k' x- Z
- " ①②③
' `1 s! E3 d; M! V, l: C" y2 J - 1■■■
; e7 u7 q& y: z. {. G - 2■■■
5 m7 a0 A4 `$ K3 w& \. j - 3■■■+ N* Z" P8 h$ H/ g- t0 P) y
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
5 t' E* f* U0 U/ G! n# ^+ \
- p+ t: B& E& Q5 N- x. r, G- O- ;;; 棋盘
5 G7 ?5 e+ L, Z6 f - (defconst *wechat-5x5-white-chess* 122882 a. ]( e% `; B4 ~1 I/ g
- " ")$ c6 j8 _/ ^8 z u7 b
, E0 ?! _+ a8 U7 b8 B( p* F- (defconst *wechat-5x5-black-chess* 9632
( t- o: R8 r* m; A% E - "■")
5 ~ S$ Q4 Y e. ?, F - & Y; l, l/ e- S
- (defmacro with-board (&rest body)
6 r6 u% N( h$ p9 V: y& J - `(with-temp-buffer5 g' u+ Z' l# R
- (unwind-protect, F$ U! E8 i3 s5 u, K- b
- (progn; K O. {3 h' K2 S9 t8 f8 V
- (if (session "board")
1 r" S) \! g# Q* d3 F - (insert (session "board")))
% Z9 z( {# l6 K6 o - ,@body) m, p4 n" A; z% e- ?
- (session "board" (buffer-string)))))3 h- c& ~( O* D0 ?
- , M" K) D: I- o
- (defun board-init (size)
( Y( r8 s' [) R) O3 J - (session "size" size)9 D, }. ]; [# C5 Y# T4 k
- (session "step" 0)# H6 V3 S/ A+ q. i9 f3 _# \" K# A1 A
- (erase-buffer)
7 M& }. R; `( O1 [ - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
7 \8 c# H% o( _1 H \ - (dotimes (row size)
1 ~/ u6 K; X/ j- {# Y - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))! T( s' W; R5 k% X3 k
9 ]8 _1 |$ {) E _2 b- (defun board-contains-p (y x)0 ]/ v' k# W. ^% w% Q( C
- (let ((size (session "size")))# m) c3 t7 M& L# ?' _
- (and (<= 1 y) (<= y size)3 ?5 ?3 j, a* q
- (<= 1 x) (<= x size))))
/ ~/ G- f/ W& M8 d
( Q5 q S) _, v) _- (defun board-toggle (y x)9 |; b" q! H# b+ c# |/ e
- (when (board-contains-p y x)
5 k. ^% z3 I% N/ U+ S - (goto-line (1+ y))
2 O$ v' v+ i+ `( ~ - (beginning-of-line)& Y) q' f+ y9 C2 C9 e+ ^
- (forward-char x) S( d0 v5 R/ K* U' a, o8 n
- (insert (if (= *wechat-5x5-white-chess* (following-char))6 F( D& c3 H9 P. w) H
- *wechat-5x5-black-chess*
" l' J8 B {2 x1 f' J - *wechat-5x5-white-chess*))! B1 x* b0 f, q$ U$ ]6 s$ m
- (delete-char 1)))
; I6 O6 A( i8 M - 9 ^4 K( S( w0 l5 Y; W# |4 n
- (defun board-put (y x)
8 Q* D0 v. m0 M4 I+ V - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))% {0 p6 m! [2 F
- (board-toggle (+ y (first dir))) [& B$ V+ F2 U. j7 O0 ?0 X
- (+ x (second dir)))))
! O- {* S5 \8 l - * s, }. {! O; z b+ G/ H( \
- (defun game-over-p ()
" H% @' k, f2 @, t1 w, e - (beginning-of-buffer)
* Y" Y! J" l, L1 i - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
. O3 h% j w3 ^ - ! s# O, f1 e: V* @% q
- (defun board-show ()5 S6 L% k- ^& h* D
- (with-board' f( x: R3 I6 ]4 l9 H% B8 k3 D1 u# g
- (concat (buffer-string): V+ P8 q* [8 ^% m0 Q
- (if (game-over-p)
( B# c& i" B3 V' i3 G - (format "共%d步,输入任意内容返回大厅" (session "step")) \- T/ c" ?/ }) m. X" |+ i
- (format "第%d步" (1+ (session "step")))))))0 e+ q. R- @% q8 g+ j
- ; S8 g) C& M J7 `/ U5 J' ?9 w
- (defun board-position-parse (cmd)
. u5 V( ~: i V$ L4 Z: @$ { - (if (= (length cmd) 2)
: Z, u8 ?) l0 t0 ]+ y! L m - (list (string-to-int (substring cmd 0 1))7 Q: K; x# c" b$ [8 o0 u9 q ]
- (string-to-int (substring cmd 1 2)))
0 h. y$ Y7 J& E3 o - '(0 0)))
- n8 J0 V+ \& B- |6 | - 3 K, j) [& T; l/ F5 t0 B3 D$ H
- ;;; 游戏房间6 B. v' L5 Z0 Q0 ] ~: a
- (defun game-room-init (cmd)
# V; e+ m3 i' { P/ m - (let* ((middle (string-to-int cmd))
1 e+ q4 a. n; U# E* p; h - (size (1- (* 2 middle))))
8 N: |; {6 p0 O8 x* @3 `' t4 k - (with-board! ~, O% L2 [9 z: O
- (board-init size)( N3 C3 d1 G0 k8 X1 P9 e
- (board-put middle middle)))
' a( K/ R5 I; ~7 C/ q9 s! K" ~8 M( t - 'game-room)
t! g) `; K/ L& t7 P4 X
4 O l5 g, z2 w' I; K- (def-room game-room# R( l% g, c; N& f2 D, h0 l
- #'board-show( H q9 D: a& S e4 W! A4 X
- (t (lambda (cmd)
3 k- L$ [9 U% ]- ?3 a4 Y4 o - (with-board
4 h/ ] B- P8 C - (if (game-over-p); @6 ^3 X5 u2 J/ N
- 'living-room
3 G+ e# o1 F+ j( {( l |( T. i0 Y! U( _ - (destructuring-bind (y x) (board-position-parse cmd)
( J0 I( ^( {* F* t - (when (board-contains-p y x)
. J/ s- Q: A. T% d8 F M - (board-toggle y x)3 d) Y0 t+ `# e1 w1 t1 h* G
- (session "step" (1+ (session "step"))))
8 t' Z9 W: i, [! n - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|