wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。) s% z5 H4 q; h, x- T
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
+ F9 H4 v2 o/ H- Z2 }- <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 Q% `, Q+ B G p5 n - (def-map "/game/5x5.el" ; 对外开放的URL
8 v! ]' P" C( p1 `& m( s7 c - 'tutorial-room-0) ; 默认的入口
% L: d; i1 P2 g0 S
! Q6 J) Y- K" |( i) l- ;;; 游戏大厅
( X, e( o% z: F/ Z" Y# p - (def-room living-room1 I' B. O; u$ S. E* V+ l' a! e
- ;; 进入该房间后的提示语. H7 y4 y( N6 B& m
- "1. 教程+ J- I* D" J) P7 y# u1 \
- 2. 入门(3x3)' g' H- j1 n5 L, z6 l
- 3. 初级(5x5), Z2 s; N7 n- |% d& {, J# u, `2 W
- 4. 中级(7x7)5 I9 J6 k; z5 m9 ^& R* _
- 5. 高级(9x9)5 t: H9 O3 Y/ |
- 0. 关于作者0 o3 \7 [( t; o r9 b) z" {& s
- 请选择1-5开始新游戏:": s2 X9 W! {, I0 c
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名, A) q1 n+ z( H% X1 v
- ("1" tutorial-room-0)0 j! A8 U/ O6 D# Q' i+ h( U h
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配/ ~9 H' U2 {5 Q
- ; 相应的返回也可以为函数,动态返回房间名7 |: w, A( D9 n5 b D# e
- (t living-room)) ; 如果条件为t,为永真
6 g% G; V* p' i4 k4 [
& L9 M. J$ f# k1 v# t7 a- ;;; 作者信息$ a' Q) C; K' c2 H# ?- t6 T' V
- (def-room about-room* Q* |9 v: ~5 P1 G* k1 l% [+ C' K
- "作者:redraiment
7 o9 y1 b- J3 I: ?& m u' x - 微博:http://weibo.com/redraiment
: U, ]# ?+ j1 F* H+ l3 C - 有任何建议,欢迎在微博或微信上联系redraiment。2 d5 ?7 ]7 T* Z9 [
- 请输入任意数字返回游戏大厅。"
% c1 H" B8 [; {: s$ b6 ]- i - (t living-room)): _; N; ]* [+ Y! u
) Y6 P! v" H& C% l* r; |- ;;; 教程
/ i% C+ q2 N1 p) Z$ B0 a2 r6 c - (defvar *wechat-5x5-tutorial-rooms* 01 ~8 K/ L# \, G/ h* |& i1 @+ a
- "The number of tutorial rooms") ^* M; \# W1 n' C& z
- 7 t8 F! g# T) w( t! B) D
- ;;; 简化教程的定义
; ?% J8 A) }4 I - (defun string-last-line (content)9 S3 J U' G3 D# a* _1 Y
- "多行内容组成的字符串中的最后一行"9 q [( [* f% g
- (with-temp-buffer
9 z1 o" T7 L9 o" [1 \$ U - (insert content)
' y2 `5 \' X; B+ ^% m9 U; R" T - (buffer-substring (line-beginning-position)
/ i* W9 R- L6 @# }) P- e1 z8 t - (point-max)))), d* `: f, T( W$ P* L9 |- w
% c( l7 K8 Y# m1 ]! y1 g5 \# m& A- (defun def-tutorial-room (prompt)8 j: Q: H4 o1 w) o1 b
- "根据提示语自动生成教程房间。
6 j1 E1 K1 F! N3 O* A - 1 o: s! O+ s3 B% ~1 q0 _2 p
- 1. 提取最后一行作为问题;
, c l! g3 `" v7 D - 2. 分析问题,获取期望用户输入的内容;4 m7 ^0 I* H% I3 D$ }) z
- 3. 定义教程房间和重复提问房间。"
' @3 d* r" o3 t) L! h' y. b - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))1 I ?8 {& u. C2 g. S
- (repeat-room (concat room-name "-repeat"))+ O, i4 u" { N% l j' U
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))& [0 \5 e: {, M* r4 g
- (question (string-last-line prompt))
" l% j' U) a6 L; d9 K - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question) y8 D1 p" m% f* Q1 g6 M; V
- (match-string 1 question)))' d: Y0 S6 w# N
- (doors (if except x9 Z8 T+ r; s+ d
- `((,except ,(intern next-room))6 y$ @( v1 p* d5 \; x6 \0 ?. Y
- ("q" living-room)# ~/ {4 a5 j G, R+ a
- ("Q" living-room)
$ B; f, J" v* {. F: K3 @& ^ - (t ,(intern repeat-room)))
: w! A9 e0 n# G1 S% ?' l' k - '((t living-room)))))( H$ ^6 O+ \$ v# o! f. }: ~9 }
- (def-room-raw (intern room-name) prompt doors)
) R( } D, y9 C - (def-room-raw (intern repeat-room) question doors)))
& _3 P4 V' I3 O/ t3 `7 B
% D! W) i6 C, {2 m0 t) R, D8 @- (defun def-tutorial (&rest prompts)" v* a/ }; T2 N- c* U
- "批量生成教程房间。"
6 y% e+ c' n2 A/ K - (dolist (prompt prompts)* m. T3 G6 N; E7 \) J& X* ?! J
- (def-tutorial-room prompt)))1 ]3 ^; W& l0 a k p& F3 u1 {( x
7 U# z- o9 ?$ @! y1 E7 ] O- (def-tutorial
& M k/ p. }+ c/ V - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。& a& [4 i9 \ n4 ~( f9 @
- 1. 教程
+ b% ~& f V6 t+ W" e& ` - 2. 入门(3x3); s* o# N+ g; F9 V. d2 C$ {; ?1 I2 X
- 3. 初级(5x5)
2 F9 O" E( A/ h! I - 4. 中级(7x7)
. ]1 o7 \2 q8 ], b+ x+ Z, t" Q - 5. 高级(9x9)) w5 Z' u' U& y+ V; }* Z
- 0. 关于作者! A' [& ~% J" d
- 请选择1-5开始新游戏:) B8 H0 I, M1 |/ u" \. h b
- 您现在正在游戏大厅里。' V, W, h8 J( u5 O4 c% t
- 请输入“2”进入入门级房间"* i2 {' b5 m- _/ w0 _: ?
- " ①②③
+ [1 l1 I0 D/ i3 D - 1 ■ % }" f0 X& A7 p: p5 f1 m) D
- 2■■■$ x+ J* g& J8 R
- 3 ■ + m8 b. Z9 ~/ T
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
" \2 m. K! k' y! E - 请输入“22”来关闭第2行第2列的窗户。"
( s% d: D# v; I( c' q! v. [ - " ①②③( ]0 \6 l" r: |3 L, F N7 d
- 1 & c' g( R5 ]* d
- 2 ) E, Q3 S% y( z, v
- 3
8 E2 _/ ^; f" m; g4 ?! X0 i - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
& x0 \! I5 G& O+ p - 请输入“11”,试着开启左上角的窗户。"; k* V$ }+ S& d {4 T
- " ①②③( F& ~. d1 `$ R& P7 u r
- 1■■ / t% r# t% @- F% u6 ~! t* R/ O) b
- 2■ : ^5 V5 ^, X# Q' }
- 3
9 a$ g( t. t( n! x* a5 z& y - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。 w: {. T/ q; ^
- 请输入“13”开启右上角的窗户。"
6 b0 d9 Q$ v, f' N- y - " ①②③
1 `! U' u5 w( n! _( d$ f* U" d - 1■ ■" Y9 y6 k# D8 f1 C5 U
- 2■ ■
2 e% j7 y5 y# a( B - 3 5 |. X0 k9 q. N
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
. z1 D! E# F" H5 D* X$ a; m' l - 请输入“31”开启左下角的窗户。"
( J2 K' L1 [$ v1 A9 } - " ①②③. j& J0 W/ s( S1 n* ^" c7 r5 H
- 1■ ■' \0 G- j. N [* k1 t* N
- 2 ■4 b! M) R2 t& O {" I) Z
- 3■■
4 f% h" q9 E* T% h# g4 T - 此时,总共有5扇窗户被开启了。- x6 \; P' y4 U: d' b" i
- 请输入“33”开启右下角的窗户。"
, x2 [. s, O5 F: l) ?- e - " ①②③8 W7 \. E. d6 j4 w
- 1■ ■2 K' M% s& L" k* c3 ~2 W8 F" U
- 2
0 f8 P; s0 x1 [. A6 b - 3■ ■
/ E: p* |3 ?8 q0 O; F - 现在,只有四个角落的窗户被打开。" Q& D+ R& ~# n
- 请输入“22”完成最后一击!"
0 t5 [! @9 A" d; e - " ①②③
8 h9 w ?# B. E- \! z+ K! Q# A - 1■■■
- r$ Z5 T! \' v' x1 C" t* m - 2■■■
- v1 C" ^' Y; R3 B/ \ - 3■■■+ a/ e5 h4 P/ q3 J- x
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")9 H* {- S3 b4 _2 f
- ) c- o. g7 }% w) c! t- x1 P
- ;;; 棋盘' J+ _8 R% X. f) r8 w) I+ J4 r% u
- (defconst *wechat-5x5-white-chess* 12288
$ i# o& ^/ O) |: a - " ")9 q: n) M6 s+ {5 u: C2 C4 \" q9 V
- 9 [: b- D. W6 v" Z- s; ~+ [6 r
- (defconst *wechat-5x5-black-chess* 96320 X+ R. H3 W0 m
- "■")
1 Q2 A; k) |0 s' @+ t
2 s: t: g/ P2 S w8 {- (defmacro with-board (&rest body)
. t& Q0 p) w0 F1 P9 P% C( H - `(with-temp-buffer
4 v* Q' G1 a* }: s* F2 Z6 m2 O - (unwind-protect
7 K+ h$ @ N' { - (progn
3 h; @. U+ Q" F- M: M4 P0 Z4 S - (if (session "board")
7 G+ _! A" i* D - (insert (session "board")))
% ~! G) O! Y6 b7 P& C. e - ,@body)
0 w& s% V9 t2 f3 R( Q7 h9 [ - (session "board" (buffer-string)))))
2 K) i S: e* p( ?" F4 C# i) B
+ ^! N2 Q5 l Y% j- (defun board-init (size)+ w }1 d2 r7 \& h
- (session "size" size)
# D( O7 R; `5 m2 ~; H - (session "step" 0)
2 y" y7 W* s) C, g. Z7 Q; H' A - (erase-buffer)
1 b3 h0 w# \& F# i9 } - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
/ X Y) m( v+ P9 N - (dotimes (row size)
, M2 E9 s! \6 f: X8 e - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))( d4 Y# x6 X# C- I" i& f
3 u1 I# W- F6 ^- E- v# i- (defun board-contains-p (y x)
) M6 S9 T0 Y( Y4 U( g$ F% W5 i% d6 d/ r - (let ((size (session "size")))
( m3 y x% E- ?4 M( y - (and (<= 1 y) (<= y size)% g2 r8 u0 Y1 }' }! ~4 F6 ~
- (<= 1 x) (<= x size)))). {, N" J; G. y, A" D% D
- - i4 N1 W" g# U# t5 P
- (defun board-toggle (y x)% h% _# H# I2 ?( D
- (when (board-contains-p y x)% t# d; G' C: _* o; d
- (goto-line (1+ y))1 Z( ^- w0 X, X; @$ ^. W
- (beginning-of-line)
5 y" p# I, y) T! ~ - (forward-char x)
) {4 ]0 Q) W' Z5 [8 y - (insert (if (= *wechat-5x5-white-chess* (following-char))' F5 t; t5 O8 p4 x$ q3 x8 Y
- *wechat-5x5-black-chess*! |3 ~3 ^+ x* j, n
- *wechat-5x5-white-chess*))
6 M% j' x0 m* i9 f+ a/ J - (delete-char 1)))
" o5 m( \% V7 P8 V
; t( b- s8 p) }5 z9 q- (defun board-put (y x)
- k' v' C$ y5 h7 z - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
$ I3 i: M C' Y! A% S; Q4 D( v, [% x9 s - (board-toggle (+ y (first dir))1 U: S+ A. o+ o0 C0 S
- (+ x (second dir)))))( A! w4 h: G/ b1 J6 Y( i
6 |3 g$ { M& J3 \3 V- (defun game-over-p ()
: N; a) }6 U! s& m - (beginning-of-buffer)
S D; R( ?6 N5 x6 Q - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
9 K, @, Q# f) }' ]6 r
& V+ l6 ]8 x* C; E: H3 E! }: k6 G7 ~- (defun board-show ()
2 K6 B) ^' W! A. t; V6 s: U/ { - (with-board
' E: _9 T$ E0 | - (concat (buffer-string)
5 }1 Q& S+ H! O, l; j# T% A - (if (game-over-p)
0 l9 q5 T6 X& W - (format "共%d步,输入任意内容返回大厅" (session "step"))! ^+ c, s4 p" D2 `- r+ p
- (format "第%d步" (1+ (session "step"))))))): D- z" V8 b! N' V
- 3 M7 ^+ d% q1 `8 H7 y) T- g
- (defun board-position-parse (cmd)' P- w# U/ g$ E5 _
- (if (= (length cmd) 2)
% |8 v+ Y* `+ ~# o. X2 B- V - (list (string-to-int (substring cmd 0 1))
1 T0 `4 ~9 S4 P, j - (string-to-int (substring cmd 1 2)))/ k' C+ ]7 Q8 p1 }+ P9 x+ [
- '(0 0)))7 Z# x# D+ {5 m5 u, R, B
- 7 f5 y! A+ N; |+ c" |
- ;;; 游戏房间
4 t; X3 A0 q3 ]9 G2 G - (defun game-room-init (cmd)2 r( A I0 {- L/ p
- (let* ((middle (string-to-int cmd))1 l8 Q( |7 ^9 g: { e& J+ O
- (size (1- (* 2 middle))))1 ~5 k Y0 ]" }. [% c+ T5 i
- (with-board0 O1 a& S; r! V/ T8 M3 g$ B9 y2 t
- (board-init size)
- n& N7 i1 V# N4 o. z# k V - (board-put middle middle)))# h* H' {! m# |0 X
- 'game-room), b; O8 H/ a! P& W( N# p/ d* k
; X/ R I; t. L: S- (def-room game-room
* u& L) e# b1 g! D - #'board-show3 R1 i4 S* ?* S% K. C, D8 {
- (t (lambda (cmd)
+ A- d @* N" T, q% v; A - (with-board
9 E; f9 J* A/ ~* ] N. e - (if (game-over-p) A' _! l& G$ E1 J
- 'living-room B" n. `9 G" C# L9 p
- (destructuring-bind (y x) (board-position-parse cmd)
% ^; A! _7 c: o9 [. d1 C) o( }$ O - (when (board-contains-p y x)" ]0 p+ o1 {! G3 t
- (board-toggle y x)
, p8 O" D' F9 Q$ Y$ z' T. Q - (session "step" (1+ (session "step"))))
" i0 j L9 u3 p9 y: o* _ - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|