wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
: a$ \ ~8 Q+ @0 L借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
( V _# ]6 Z- Z4 c3 O- <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;">;; 定义新的游戏地图+ Z6 `, h* b0 R( ]: ]2 X
- (def-map "/game/5x5.el" ; 对外开放的URL" E2 B6 h8 V/ s8 p# U- \1 M
- 'tutorial-room-0) ; 默认的入口
' r! ~! l9 F* J3 M - 2 f+ Y4 [1 \. w# U' ]- {
- ;;; 游戏大厅' R3 ]) X) e \# p |
- (def-room living-room
* E! a9 R' T% `% B( j' r - ;; 进入该房间后的提示语3 z7 M5 O4 J: q+ o* w( ?' s7 T
- "1. 教程5 B$ z5 n5 }0 |% N5 y1 U/ e
- 2. 入门(3x3)$ X7 `- x; A0 z9 `! E2 r) h/ D4 l
- 3. 初级(5x5)+ ~; Y' Z) f5 P+ a0 P+ o
- 4. 中级(7x7)7 i+ N1 w3 t* o& v
- 5. 高级(9x9)
' y" g# f$ r/ [- V" @2 a - 0. 关于作者
3 ]7 a) j8 d- W2 @7 R3 U - 请选择1-5开始新游戏:"
$ l3 v( Z% E' {5 d - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
' C! \* x+ _/ P! K - ("1" tutorial-room-0)& C3 e7 T* h8 h% p2 g& _- [+ {
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
4 w2 W. Q( a! w - ; 相应的返回也可以为函数,动态返回房间名
6 F7 V! A' g. Y2 W - (t living-room)) ; 如果条件为t,为永真& b! Y$ E4 W9 c; N( K+ K
- 2 m( T: e( y1 b( D% k
- ;;; 作者信息
; l: {8 q6 s* y9 S; r5 S0 z - (def-room about-room
. _/ ?( g, m" ?2 W8 v9 K - "作者:redraiment( n1 N. D! ]9 B, M6 X$ k* f7 h
- 微博:http://weibo.com/redraiment/ ^# A6 S' \7 y$ ]& u. X4 I
- 有任何建议,欢迎在微博或微信上联系redraiment。
. {. w" i( S! h+ i7 A) s& Q* _; e- @! \ - 请输入任意数字返回游戏大厅。"
& i, j3 r2 E% N x% H6 _4 m+ f- Q* b/ i - (t living-room))+ i/ A9 @1 \% a( T
- , g: L; u- V/ ?5 ~
- ;;; 教程' w* L% k% B; \. t+ _! j; l
- (defvar *wechat-5x5-tutorial-rooms* 0/ N# G5 g5 B1 E; P. Z$ @/ `5 Q- I* r* z
- "The number of tutorial rooms")
6 F" K! y3 z. y H8 }# ]! z I- U2 q - - O& t. V' e8 v, j5 j
- ;;; 简化教程的定义
( y9 B% T. c2 H7 @5 O) P - (defun string-last-line (content)
) Y# U; r1 n @ - "多行内容组成的字符串中的最后一行"
+ a1 y7 o, S% Y Y - (with-temp-buffer; o8 E+ Y* k" b& _$ x& I. D
- (insert content)
9 l; F$ ?7 }) Y1 y* Q* q8 e - (buffer-substring (line-beginning-position)# i) t/ s2 u5 h
- (point-max))))
$ W- Q2 F: P* t0 D
# ~* b2 Z4 t( M6 F+ z. [% Y- (defun def-tutorial-room (prompt)2 X& {; g+ h6 q% i" ~8 l; k
- "根据提示语自动生成教程房间。8 J6 h) w- k: h7 _' o0 Q
- 4 B9 Q9 D- M2 _4 E/ m9 A& i+ z+ R
- 1. 提取最后一行作为问题;
" b. T5 Y# x' ]+ R( ~* U - 2. 分析问题,获取期望用户输入的内容;
4 T& K. c: }3 e) Z6 g8 G2 y+ V - 3. 定义教程房间和重复提问房间。") ?; ^! W4 K/ a+ e5 j) S
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
# o/ |' F; l1 N3 |! s1 \2 y4 p - (repeat-room (concat room-name "-repeat"))
6 u8 l/ H, k" V0 W( N$ {1 q - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))4 \0 V8 s, w3 ]1 J
- (question (string-last-line prompt))
/ u) _0 s6 } @- Z! {5 \ - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
1 m3 o" k+ W+ E0 p) q* h - (match-string 1 question)))
6 U5 P! l8 Z- r+ o# h# \ - (doors (if except7 V' ~$ Q o$ j( ?0 u( C
- `((,except ,(intern next-room))
" _$ O! }4 l: R( {8 a& }4 S - ("q" living-room)
2 X ~ {+ T2 X. g' T - ("Q" living-room)
) b( h' X9 Y3 E5 C! b2 C - (t ,(intern repeat-room)))
: p6 a7 H, C3 \ - '((t living-room)))))
( Q! L2 o: B, Y N1 a - (def-room-raw (intern room-name) prompt doors)
" l& m9 Q: C& R( L5 J - (def-room-raw (intern repeat-room) question doors)))
9 |' Z/ J1 I; E' c
+ }7 e/ W" F0 Q) d) o8 V* I3 N3 S- (defun def-tutorial (&rest prompts)5 i' S$ L0 g7 ~( N
- "批量生成教程房间。"
9 P3 u0 m2 Q$ l% l6 s0 y2 P! v$ W - (dolist (prompt prompts)) t7 P9 H' y! Q4 ^1 o) U* z) ]
- (def-tutorial-room prompt)))
* U9 o! E( B1 T - 9 ^' f" n1 m2 o8 r' x
- (def-tutorial( {, W6 S4 P; c0 ~8 I* h4 I" C9 |
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
N) V0 D6 g+ ? S - 1. 教程
; }2 [% g s( [2 H U8 J - 2. 入门(3x3)+ ?0 Q5 T* C' F9 h
- 3. 初级(5x5)- t# k$ a5 m f" q" P) B
- 4. 中级(7x7)$ j1 ]/ C+ S. `7 |2 k7 i
- 5. 高级(9x9)) c: |8 u9 d, d0 g0 j
- 0. 关于作者; A3 t9 Q) y( P: `* n* ?2 m0 Y
- 请选择1-5开始新游戏:1 i/ n9 [7 I. ]. ?1 }0 N
- 您现在正在游戏大厅里。
9 [! v, e# `& X- U - 请输入“2”进入入门级房间"
9 y+ v. a6 A1 I, ? - " ①②③
9 H1 w: [' i* V% x+ [% j" \* C' t2 | - 1 ■
1 i6 b" @$ I; e1 R! H8 W: W0 q - 2■■■
! u, o- h B$ C/ I( L - 3 ■ * N# |& c& {& v5 Z" g p/ {
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!3 ^3 g: ^. ~# K. o4 u ]' D
- 请输入“22”来关闭第2行第2列的窗户。"
5 I2 e2 V- b) S* g - " ①②③
. A N; i7 s- ? - 1 : g( y w6 A: R
- 2 8 j& ]5 P) s u3 g" j
- 3 ! @% m9 E1 {9 r/ H; ^+ T- x
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
$ W/ z) y! n6 B5 Q: T - 请输入“11”,试着开启左上角的窗户。"
7 J ?/ G$ s4 T4 N g" b- G - " ①②③
7 F! w5 `2 w' E: u4 P% T+ O - 1■■ ! ?" t, N- q" a! S' y# W2 }
- 2■ 9 }+ U% V0 \7 t' h ]" B: E
- 3
& ~ p6 o; ^7 \' N - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
- Q; [8 j5 E! w) h# e - 请输入“13”开启右上角的窗户。": J5 `/ a# a+ b- `, O- a) N+ U( b
- " ①②③0 J9 G5 b0 g! Q5 g
- 1■ ■
+ u8 _! x9 [& X& m* a - 2■ ■
Z8 c$ q3 O$ F8 R' v4 f - 3
3 @ U$ G' @! k( s - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。4 c3 y2 ?! y0 l
- 请输入“31”开启左下角的窗户。"1 [: O! c. c+ d @0 W& g+ b/ I/ ~
- " ①②③# X$ `8 C; w( T6 ]
- 1■ ■( [3 z3 O" H) \. g' o L3 W
- 2 ■
6 ?/ o' H+ P- h# J6 Z) i - 3■■ ' _/ [; d8 \7 L& i% y! {
- 此时,总共有5扇窗户被开启了。5 O: z4 Q* I" u4 w( a
- 请输入“33”开启右下角的窗户。") t' s# G2 U0 \) g' Q# x+ |% W
- " ①②③
! M, H* z0 A6 r - 1■ ■
3 ~% w8 k: P6 ] - 2
0 m& U6 o$ S. a4 U& W3 ?& h6 c - 3■ ■
2 B. _7 x: Q) f" Q" G8 V/ U4 R - 现在,只有四个角落的窗户被打开。$ {9 l; T8 X& p
- 请输入“22”完成最后一击!"' r) U( W& R% W. z
- " ①②③
3 Z! O5 V- m* M: _+ I1 [0 c - 1■■■
% z9 W% A1 Z# u; U - 2■■■
5 d3 N+ `# V$ R! E7 R! D( G - 3■■■
5 n% G6 f7 u; O - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
' e2 f/ t& R* q( E - 6 V# ^" U% x2 v2 g* w4 s4 r) L' X5 e
- ;;; 棋盘4 } w' F8 P1 ^0 M% c
- (defconst *wechat-5x5-white-chess* 12288
* e4 U: t2 t+ Z3 @/ { - " ")" U2 p( M0 U2 X6 g
- ! v7 {5 [0 g: `# ~9 ^
- (defconst *wechat-5x5-black-chess* 9632+ a; Q) g' ]# E6 A" n5 b, e% T
- "■") ]$ u& T. [7 T: g; s7 R
- ! W K5 @5 F$ G, Q* h( E
- (defmacro with-board (&rest body); F& |, r; R0 h$ t% Y5 d( h8 U
- `(with-temp-buffer# o% f5 g: w \- A3 Z; C W
- (unwind-protect
! G+ W* h7 M" ^0 p; S$ l* T$ N - (progn5 G% e, }1 J9 m3 f
- (if (session "board")! x: F: }" n- r" I
- (insert (session "board")))2 B9 ?" V! p' W( z/ ]
- ,@body)
4 t& X' f2 H! E: Y ` - (session "board" (buffer-string)))))
) X: m( y- X+ n* Q, i
0 {4 K" n5 W7 l# |4 a- (defun board-init (size)
4 \- _; r2 D4 B, j2 d% g - (session "size" size)
- H% u- K5 T+ h0 p$ V; k5 X/ L+ n' j - (session "step" 0)
$ R, A" v9 u" ~' R - (erase-buffer)
/ |% W0 @( v5 o- ~* v - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
. P( v `5 @1 K3 R- a/ p - (dotimes (row size)
* Y2 I; }3 P) V3 z* H7 R' U - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))! R8 f3 o0 x7 e n7 h3 F7 Y$ b3 Z
! C0 ^6 r+ l9 t) _& t) e- (defun board-contains-p (y x)0 N5 `3 v$ Q" x. c, p
- (let ((size (session "size")))& m- t b# X8 I& s C8 n
- (and (<= 1 y) (<= y size)
7 y) J0 E q# I9 p7 e1 l4 K; N6 D - (<= 1 x) (<= x size))))' X2 R/ {& O2 h. I1 S3 u6 b
- + k. A6 ]+ I3 `% s8 y$ X% V
- (defun board-toggle (y x)
) \' [4 [# b B1 }% A: U: B; Q1 o - (when (board-contains-p y x)
! D3 Z* ]) p0 o6 v9 q3 P - (goto-line (1+ y))0 y1 X* Q$ U& B. ?
- (beginning-of-line)3 Y# M$ y4 \" v
- (forward-char x)( \& R ~# Z' ^4 Z7 ?+ Q/ K/ g# I; z
- (insert (if (= *wechat-5x5-white-chess* (following-char)): B! U2 G' b- q8 Q! B
- *wechat-5x5-black-chess*6 c+ }6 C8 C/ c a/ M* m" `" Z
- *wechat-5x5-white-chess*))- l8 e# Y2 g8 f, _+ ^3 e
- (delete-char 1)))
- V! }2 {* w, t1 h! H - / | J4 j/ ^- N& W* `
- (defun board-put (y x)
, k5 q& W+ b2 j1 \& i& ?$ ?2 E$ | - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0))). t. z4 i2 E& R5 Z' V8 m
- (board-toggle (+ y (first dir))% \. M3 c" b0 P; H
- (+ x (second dir)))))
- M* H1 J2 m* f; m - , S" y( O) C2 E
- (defun game-over-p ()# D" |4 g' r) K% `7 e( n
- (beginning-of-buffer) B3 g0 H+ I. _2 n
- (not (search-forward (string *wechat-5x5-white-chess*) nil t))): f% P/ F0 w" V) }! L5 L
! C' R$ F5 W! L- (defun board-show ()
1 u! J& F+ a9 I! U/ l - (with-board8 X4 e. Z ?. ?# C3 [' j
- (concat (buffer-string)5 O, |9 t$ r, T. h; }) m9 S# E
- (if (game-over-p)% Y# ~1 ?6 X! O( k/ {- @
- (format "共%d步,输入任意内容返回大厅" (session "step"))
$ s/ }+ g# c! j2 ]3 H; B - (format "第%d步" (1+ (session "step")))))))& }. P% n: }/ ^7 s1 d% s
- & B! s* `3 b/ g
- (defun board-position-parse (cmd)) ]0 z0 l3 R/ o3 O- F0 q# R
- (if (= (length cmd) 2)
" {2 ]( P- p, c9 T' v- M; S$ z- s - (list (string-to-int (substring cmd 0 1))
7 B% Q& M2 g) _2 _ - (string-to-int (substring cmd 1 2)))
$ c$ Z5 _3 m+ o8 U9 |) s% ^4 z - '(0 0)))
8 T( ?! Z* O9 \) a
! N, }& W/ y/ A# j7 S5 V1 q- ;;; 游戏房间" N5 q u5 r0 W* F4 e5 t
- (defun game-room-init (cmd)
8 Y& {% P- z9 W8 C% @2 o9 ] - (let* ((middle (string-to-int cmd))
7 Z' A! D9 B- }4 } - (size (1- (* 2 middle))))2 ]0 A6 i+ ]# n( h: L) p i; C
- (with-board$ Y( w4 T) H* z0 j/ Y0 |9 k
- (board-init size)$ V! j8 b- A/ K
- (board-put middle middle)))1 B& s! C; F0 U2 K, s
- 'game-room)* k2 r5 O9 G6 V. T- h$ O, z9 \' Z! y
, s2 x! Y1 `7 @/ j- (def-room game-room* @$ D' p5 q/ a; ^6 N: h1 q; S
- #'board-show
]% q$ X: i( e - (t (lambda (cmd)5 D2 ]$ V/ X1 J$ N! C' R
- (with-board
6 h% m6 U# I% C - (if (game-over-p)
- f& }9 M8 \8 U( ?9 g - 'living-room
9 V& B+ U4 c3 ?1 I5 g: E" Z2 Q8 a - (destructuring-bind (y x) (board-position-parse cmd)3 V+ Y" q7 r# i4 W( h# Q# c
- (when (board-contains-p y x)) }6 T( p1 V5 s* d- Y8 O% n
- (board-toggle y x)9 P0 a+ n/ w v9 b
- (session "step" (1+ (session "step"))))9 [& s" k5 a8 B" G$ {
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|