wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。3 _; c: C) y8 }1 T. J
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
) u. h; @0 u; i8 h0 [- <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;">;; 定义新的游戏地图
* `3 k1 \' i9 l# U+ R( L% E% y - (def-map "/game/5x5.el" ; 对外开放的URL
: F# _. C) Q* ` - 'tutorial-room-0) ; 默认的入口* ^( n9 l+ i" }/ F
) F/ G8 B* \, I5 C- ;;; 游戏大厅- A: u! f9 x- G2 }! o7 n
- (def-room living-room" p% L! m. ]8 A: ~; h3 N1 T$ Z
- ;; 进入该房间后的提示语8 X& l3 {8 F- w5 s$ P; u# R
- "1. 教程' |6 f% s/ [! h* u7 q0 m! ~
- 2. 入门(3x3)$ x6 S& n- r0 a M# j/ j
- 3. 初级(5x5)$ {/ t. c5 L9 i1 ] D: i
- 4. 中级(7x7)
4 b) ~% r5 C b" h: r# B" v0 Z - 5. 高级(9x9)
' K4 b4 O2 ?0 J+ R! G# k - 0. 关于作者
[# S# B# T1 \, \0 W - 请选择1-5开始新游戏:"
$ D8 o9 L& Z3 N6 V1 @ - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名7 H, w$ R3 W9 y
- ("1" tutorial-room-0)
' a4 E0 H. r# d: E - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
& o& C, X% j1 T" J% J - ; 相应的返回也可以为函数,动态返回房间名
5 u3 Q( ^ V* F; _# R1 t& ]7 Z6 M - (t living-room)) ; 如果条件为t,为永真/ a& {; [+ J$ P6 V
- 7 w: i1 ?2 M" |: V+ o/ r6 s
- ;;; 作者信息
# V/ K; [) Z: g% v. M - (def-room about-room
1 M4 S4 |$ e0 N& u% k& e: e) q - "作者:redraiment
9 ? O& S. d/ ~! m y) M ` - 微博:http://weibo.com/redraiment
. a- w( y+ w% w( F4 `: m/ V8 K4 O$ i - 有任何建议,欢迎在微博或微信上联系redraiment。+ {) A, }* y2 [, L/ e) O3 {
- 请输入任意数字返回游戏大厅。"9 N. w% `/ ?( `! Z% p
- (t living-room)); N- D' ?4 X& I
: F# r8 Y0 N' D* g/ R- ;;; 教程9 e! g0 e2 s: Y" j' k. c
- (defvar *wechat-5x5-tutorial-rooms* 0
0 [' ]2 ?1 `4 ?+ L+ s5 l+ ? - "The number of tutorial rooms")
: I# o+ U2 p4 h% }# T& y - 1 ?- ~! V& @/ _0 B" L4 Y% V
- ;;; 简化教程的定义2 Z. K# {8 |0 }* P& D9 Q
- (defun string-last-line (content)% R9 Y5 D+ m. n% s, R# \
- "多行内容组成的字符串中的最后一行"( h, A8 D8 Q1 H" l
- (with-temp-buffer4 Y- u3 ~- N4 q+ e3 k! f7 U
- (insert content)
& D! \& W' Y- u8 _* K! i - (buffer-substring (line-beginning-position)
4 z2 G! Q* o) {2 t) o - (point-max))))( u# ` m3 [& k9 y
- 8 O& v4 U. I8 M, f |0 l% q
- (defun def-tutorial-room (prompt)3 [9 `- Y% g8 G( ?! a% w
- "根据提示语自动生成教程房间。
7 p3 |" U5 x' f7 P# \3 J1 `
. \- H% a9 J/ A7 {6 A- 1. 提取最后一行作为问题;7 x; n! I* G2 m! o
- 2. 分析问题,获取期望用户输入的内容;
4 ?3 b: p5 n j2 I0 j - 3. 定义教程房间和重复提问房间。"
# H% |" l3 x: l4 R/ }2 c - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))+ I. `. n) j2 q2 C9 z0 x/ t4 a
- (repeat-room (concat room-name "-repeat"))
* z W d/ z% n0 e7 I3 k5 c/ U" z - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
) x2 I% L1 ?& [2 T2 W9 t! q - (question (string-last-line prompt))
% v6 g- O1 T( {, j" Z - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
; K$ x7 x" S. o, P/ j" W" l! Y - (match-string 1 question)))2 c) k5 f$ q& H; ^: f
- (doors (if except. L3 X+ q b/ |8 D
- `((,except ,(intern next-room))
7 _- o" e* a; V! R' H7 v; J( I! M - ("q" living-room) Z$ R* \0 N8 k( a1 ?
- ("Q" living-room)
, _% c9 C- w c+ e C) s, s$ K - (t ,(intern repeat-room))). o+ f6 C0 s. i+ }; {% {( H3 s2 E4 W
- '((t living-room)))))
8 E4 C) k% j" @; b0 a3 K - (def-room-raw (intern room-name) prompt doors)( J2 X) T" C$ C5 Q: F6 C
- (def-room-raw (intern repeat-room) question doors)))% _6 K: x# E- @4 [) T6 X
- 9 l2 \+ y% f5 N" T- l/ A
- (defun def-tutorial (&rest prompts)2 n/ }0 T) F2 H
- "批量生成教程房间。"/ D. n! X! D5 L! `& z
- (dolist (prompt prompts)
" \1 M/ E! ~ M1 [; ~8 S! D( x - (def-tutorial-room prompt)))
' T# |. S. N% T
' t" i& d: L7 L- (def-tutorial( }5 _, |# t3 a: `# J& f; g2 ]- \) g% m
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。9 g2 D4 g% K; d; H
- 1. 教程1 B. l) R# }3 g0 B/ L$ J& l5 u
- 2. 入门(3x3)
& S( F7 e& o( _/ J - 3. 初级(5x5)& m* k7 u$ c0 L; T* a( R8 D% e
- 4. 中级(7x7)
! r! A; z% u* P' G - 5. 高级(9x9)# m7 j& _$ `( W/ ~
- 0. 关于作者6 `5 S% }5 G) I: P: m6 q, u
- 请选择1-5开始新游戏:
9 t) G1 ~3 h9 G8 c/ @- L - 您现在正在游戏大厅里。
) `4 ]' S, v4 |) d( J0 |/ b' `7 O - 请输入“2”进入入门级房间": _+ v& h8 @7 @3 {+ y% d% ? S
- " ①②③8 y1 [. ~5 u! m
- 1 ■ - v8 o9 V: a/ H. A
- 2■■■& h+ M I9 w7 D3 n/ g0 N V( i
- 3 ■
5 K! D' ]% {& a" m: G# K0 k i - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
; {# s) {9 [* S+ p3 r6 e - 请输入“22”来关闭第2行第2列的窗户。"& ^0 \, a3 U4 ]% j6 A( k
- " ①②③
' \+ \1 G7 {2 Y) {/ S* } - 1 $ d! |% D, x2 g" v$ }' S- H
- 2
, Z9 t; `( e+ N+ f5 }. N - 3 7 W) c9 D! p. d# Q; a! x
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。3 M+ G. P0 I. a
- 请输入“11”,试着开启左上角的窗户。"8 a* c5 a& ~* d5 p) [) {3 q
- " ①②③
0 ?+ B; B2 F; {, N% Q! n, Y - 1■■ 0 ?; I( C' g8 ?! z" o% A
- 2■
/ s! ^% U) i% Z& _3 i - 3
6 U% U% `9 E3 v/ o; Q5 r3 M' v - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
! H2 i+ O0 @3 Y3 l% J9 d - 请输入“13”开启右上角的窗户。"
9 U. w! |8 r% C& k& x) S - " ①②③6 o+ d. b9 Y; T3 }. v8 w7 ]0 D) v
- 1■ ■6 U! F/ x: q! _' d B2 w% ]1 j
- 2■ ■7 |( H" P% N: @
- 3
# [! G3 t' p7 [ - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。2 _6 p" ]/ h3 v9 o, @' R. b3 X
- 请输入“31”开启左下角的窗户。"
# {- u" r8 j4 Y - " ①②③
8 w1 O& F, n. ~( @, M" Q8 a - 1■ ■; M$ B6 A& ]! U2 q6 K
- 2 ■
3 |4 P+ S: F$ H% v4 h* \" I% b - 3■■
6 N( U) e) Q8 v* Y3 i - 此时,总共有5扇窗户被开启了。
2 ~! T0 j% [. Q: A - 请输入“33”开启右下角的窗户。": \% |) ]( L1 g5 z$ i0 s7 n
- " ①②③
, [; }8 n& k6 [ - 1■ ■2 g5 h$ c7 T4 O4 j
- 2
7 |4 ?. @9 V [. V# A9 U% W - 3■ ■
4 v" q, L4 Y7 b) y - 现在,只有四个角落的窗户被打开。& ~. x0 S R- o
- 请输入“22”完成最后一击!": ]/ }/ i" H' V+ ~' G H
- " ①②③: `- F3 i2 x9 U, b' ~6 ]# T
- 1■■■
: x5 |* X% M0 \# J/ {7 b- Z - 2■■■
$ J: E. D4 e7 Z* G3 C# P$ Q4 C8 q! w3 \ - 3■■■
* D: g6 ^. m% q8 g - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
0 o" \9 y8 b( q- n - 6 K3 y# p& x7 B8 s5 J* |
- ;;; 棋盘! Y3 m; I+ d$ Z( J
- (defconst *wechat-5x5-white-chess* 12288
2 `. z1 V$ h3 t! G, r7 m. Y - " ")+ H$ Y- P) ]4 J! ~, [
- 8 J5 C" `/ y. ]3 h
- (defconst *wechat-5x5-black-chess* 9632' ?5 N) r; ?& o$ @& [, i: v6 \1 M
- "■")
8 f( K6 `3 i% M
; Q# Q/ [% C2 S& }0 l- (defmacro with-board (&rest body)+ d# l# j. \; z# E# C
- `(with-temp-buffer
- }) k" e8 U! a0 \. d( V - (unwind-protect
: O" c: h+ a4 `9 r - (progn: f5 e. N! e9 D. o% x
- (if (session "board"), z- l/ B% l" t2 O; o
- (insert (session "board")))
" A0 \7 V1 q) e$ R - ,@body)
: d' Q# r0 C L. Z7 Z" e! m4 s+ d4 u. i - (session "board" (buffer-string)))))
" ~, O* B1 e0 m" m - ! @* c1 z3 S- Z2 s: K7 }' F
- (defun board-init (size)" ~% k! A5 j7 q
- (session "size" size)
/ B% s6 o7 r# j4 y9 B: ~5 q - (session "step" 0)- i: M1 l! D3 n/ \- z" i* o
- (erase-buffer)$ X4 v. L/ F9 d: ]5 a+ l
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
4 U% T+ T3 B- q" w' `5 [ g4 f6 {) z - (dotimes (row size)" o- M1 b; w" t8 P8 T* }0 Y
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))" X8 W/ w" m& D& _! X1 A! G
, v5 Z3 {: e% t Z/ _6 w- (defun board-contains-p (y x)2 o( R, ]( s& X( ^- L
- (let ((size (session "size")))+ n d( C% e& s- }; U4 i9 h
- (and (<= 1 y) (<= y size)
' e1 }9 X% }/ j$ F; O$ L1 K e - (<= 1 x) (<= x size)))); J) V7 Q3 I* _# d$ I
: O" z* b- R! `, N# M- (defun board-toggle (y x)- U1 L% d( P3 |" s5 d) L2 j* \
- (when (board-contains-p y x)
1 z9 e- I0 |9 u) k- z - (goto-line (1+ y))# n* d2 S2 H7 q9 V2 l0 D
- (beginning-of-line); f2 b1 G- i- v/ ^, Y0 f; v
- (forward-char x)
" f0 i k" o# N2 P1 W - (insert (if (= *wechat-5x5-white-chess* (following-char))
6 H0 Y, D: G% V* F, |* Z. e* P - *wechat-5x5-black-chess*
) u# j8 F8 u( D) c! d/ ?# d1 ~ - *wechat-5x5-white-chess*))
& J5 B9 R: m6 e' i* I9 r- |8 ^! [ - (delete-char 1)))
- Z) S" w) }4 d6 w
. u- q: s I1 q" ?2 o' E9 v- (defun board-put (y x)0 O% U7 x7 ~4 i
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
6 I, F% }' ?$ V% l - (board-toggle (+ y (first dir))
1 X2 T/ Z! [- ?3 X - (+ x (second dir)))))
+ Z9 a# O/ U( `2 O+ e - 0 M! [( r- L0 X
- (defun game-over-p ()2 v* Q' n; b3 B8 Y
- (beginning-of-buffer)
5 ^: m4 |: w, [# `; h - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))1 b7 O0 e& N, |0 t6 P
- 0 b& x" q$ g8 G: X2 @$ \
- (defun board-show ()
: B4 u8 d- X7 r( F9 a* T; t - (with-board
6 |9 M, Q) W1 E/ t# s9 F# v - (concat (buffer-string)
$ I1 A/ S4 o( d: N& [ - (if (game-over-p)
: ~1 @, E9 g5 X" F - (format "共%d步,输入任意内容返回大厅" (session "step")), s7 P _3 m, E, L! w( M5 e! {
- (format "第%d步" (1+ (session "step")))))))
* N' \: L$ G: h/ b% Y- j1 d% r
2 Q$ H8 ]5 `8 L& |+ |1 K) ?- (defun board-position-parse (cmd)
7 W/ I6 P3 U4 a( g. D" h - (if (= (length cmd) 2)' L; ~8 }7 a8 j; v8 ~$ D# j/ G' q
- (list (string-to-int (substring cmd 0 1)), G1 i* e, ~, B/ O* W0 N
- (string-to-int (substring cmd 1 2)))2 S! n: `9 Q1 z0 \$ v
- '(0 0)))2 g4 `" f+ Y/ r# [+ @
0 p) V# k' R4 t- ;;; 游戏房间
6 ^1 z" r5 }& x. O* L5 u4 k - (defun game-room-init (cmd)
" ]4 S' G/ g$ H1 [ - (let* ((middle (string-to-int cmd)): @' c& U& C9 x8 @: _7 | T M) e8 c
- (size (1- (* 2 middle))))
5 F. C1 q% Q8 Q - (with-board/ S0 F1 N- C. G# x, S( K- m- N5 E, |
- (board-init size)
( ], `$ B( P" ]: I+ J) V - (board-put middle middle)))
6 L4 Z" I5 ]- \4 G8 N - 'game-room)' L0 @6 v0 \$ u6 b
- # E |- E$ n+ `2 y4 i% r
- (def-room game-room
/ @% D0 S+ B$ z5 I( h7 ] - #'board-show
' U7 ]; s# I5 r# n0 L3 Z - (t (lambda (cmd)
7 f0 v( j w, A% w1 {1 q - (with-board7 @, d) m* ^! ?9 k
- (if (game-over-p)
+ G$ B. c' R1 P* m0 g - 'living-room
% I# x# {) S1 \' U/ ^1 j' m2 @' J - (destructuring-bind (y x) (board-position-parse cmd)% q4 p4 z1 O- ^6 ^" T8 K8 r
- (when (board-contains-p y x)
; K1 Q: a# K2 v: \1 ~ - (board-toggle y x)
, p2 t, c( P" q/ m* f9 F. V/ x - (session "step" (1+ (session "step")))); m% @8 t5 P$ o* y+ i- |0 {
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|