wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。% S* G$ V% h9 Y3 s
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
1 z5 T' H7 G9 ]4 T$ j- C- <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;">;; 定义新的游戏地图
$ s# q* Y) S2 m+ n* K3 f$ | - (def-map "/game/5x5.el" ; 对外开放的URL& n) a V0 {( m- b9 K3 H
- 'tutorial-room-0) ; 默认的入口
: Q+ g$ v! F: p
# d7 r1 M" b" A7 g- ;;; 游戏大厅( }2 J) Y9 H. ?
- (def-room living-room& b) h. Q& L$ {2 v0 X* W
- ;; 进入该房间后的提示语2 X6 ~. h' p) V
- "1. 教程
1 X; W& _4 T! D. J2 ^ - 2. 入门(3x3)
. \: a) _, Y4 w% ?- d. x - 3. 初级(5x5)
% q# C$ ?5 k) G }$ O - 4. 中级(7x7)
6 D3 A% M0 v: L( w |. J1 h+ \ - 5. 高级(9x9)
% q- m* a+ h+ P j1 z5 H$ Q - 0. 关于作者
" @2 N5 d: {# A& m7 v) z - 请选择1-5开始新游戏:"( ~0 \. m# y4 D) y5 y7 `
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名& T* m- g, |' Z0 [( r
- ("1" tutorial-room-0)/ p# F. n0 [* Y0 p8 s8 p, z
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配) J% d h5 b9 `7 [
- ; 相应的返回也可以为函数,动态返回房间名
! f; ?8 w! v+ m( b2 c, B9 J - (t living-room)) ; 如果条件为t,为永真$ j, X$ d* C( e- C5 B
- * d. K8 Q" W9 a/ y! V* Y
- ;;; 作者信息
: f5 B# q! Z# H/ I$ Q0 u - (def-room about-room4 g7 g' }% O* F, Q. g- I
- "作者:redraiment9 l' ~3 k! {1 Z
- 微博:http://weibo.com/redraiment9 t) ] v: l2 w+ I7 s
- 有任何建议,欢迎在微博或微信上联系redraiment。* U* C0 f. F* j+ B8 {3 ?- S
- 请输入任意数字返回游戏大厅。"3 G$ B* k3 d! Q7 Y/ I
- (t living-room))# u% r$ V* W1 W! I7 a
- 2 n% Y0 n7 v) w* k! a) x, i
- ;;; 教程
# g6 w2 u& r3 B - (defvar *wechat-5x5-tutorial-rooms* 0
1 |# n4 {! ^9 p' g - "The number of tutorial rooms")
/ j v, i+ r3 C6 ~6 R3 ~/ V, h - : u! W: j% ]2 R$ K
- ;;; 简化教程的定义
5 ~: [* @; X" u% K' y) V( j) ~ - (defun string-last-line (content)
[2 h3 @, M9 S& e: ? - "多行内容组成的字符串中的最后一行"' `% M4 B% M2 Z
- (with-temp-buffer6 v+ k- |2 y3 [) \& q, B
- (insert content)( `) q0 }6 l1 A
- (buffer-substring (line-beginning-position)
. S8 |5 h7 u5 O( O - (point-max))))
8 e- E$ N4 i& [3 Y- | - # g1 d2 D+ n0 @) Z2 u0 f1 R
- (defun def-tutorial-room (prompt)
' j+ s5 F8 X9 F5 u2 F - "根据提示语自动生成教程房间。# s1 u) @3 T# {0 q+ n* r
/ F; G" h) g/ f. N, f) l; P- 1. 提取最后一行作为问题;. K& ^8 C- j/ i% p
- 2. 分析问题,获取期望用户输入的内容;
6 n4 {6 d7 P2 `( q/ D4 ^2 W( M7 ` - 3. 定义教程房间和重复提问房间。"
9 x& X# e6 w$ `- m3 u - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))5 A+ J! Y V* `& K+ ?
- (repeat-room (concat room-name "-repeat"))
' D9 x7 d: @' ~( y - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))" W2 \5 M0 `; j2 Y' k K3 S
- (question (string-last-line prompt))
; R# `3 C6 M- A* w! i1 T+ T3 Q, u - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
- w( ]2 |. I* ~% N4 P8 T - (match-string 1 question)))% V$ q: t) [ n6 _+ `4 D
- (doors (if except
* Z# J2 P+ G+ P# N8 ] H - `((,except ,(intern next-room))/ ~, L' O$ ^0 |! J3 E! N
- ("q" living-room); N( r$ E2 q' S: B+ p [& x
- ("Q" living-room)* @/ G' r. g. h/ X
- (t ,(intern repeat-room)))
5 u& |: B6 ^0 \2 n c( |5 d - '((t living-room)))))# p# S5 o7 f5 y# e
- (def-room-raw (intern room-name) prompt doors)
# z/ }. J7 r+ ?& B$ P z - (def-room-raw (intern repeat-room) question doors)))7 i4 {2 e# j z- U2 M3 |0 q5 {
/ m5 G K V! x2 U2 ~4 m- (defun def-tutorial (&rest prompts)
4 {* |" D1 k E' d - "批量生成教程房间。"
) J0 O* ^- Z( i& q7 R - (dolist (prompt prompts)
/ O6 F7 ~# l) F9 @9 Q& L - (def-tutorial-room prompt)))- }' Z9 s. D: a4 T( q/ @' \( z H6 M
6 `+ ~( {( ~1 ^9 n- (def-tutorial" [+ H( L* @9 U: O3 K
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
0 D8 h0 B6 J, w4 T/ Z2 r1 C - 1. 教程
/ |+ E2 n) ~' p6 k" Y* a9 c- S - 2. 入门(3x3)
6 s3 L: S. t x8 y+ A6 x7 O - 3. 初级(5x5)
! H& d$ T. ^+ L1 U8 E3 d( H' z - 4. 中级(7x7)
/ M6 f* N* U4 U4 t* L9 c - 5. 高级(9x9)
]% x/ O+ l J! t! q/ P - 0. 关于作者) @& I/ W' B. ^, M. v
- 请选择1-5开始新游戏:' q; G& H* B6 ~& Z
- 您现在正在游戏大厅里。
' Q# l! q5 ~! e/ }' S% a$ u - 请输入“2”进入入门级房间"
( ~6 \; o, P/ a: u9 ~/ g9 L+ l" d - " ①②③
4 I( P2 H4 `- v/ J4 n, F0 b - 1 ■ 9 R' H# M5 [; l$ X: |" g
- 2■■■
Z8 z- n5 g# B& I8 ^+ r - 3 ■ ) Y$ k8 ] }: C& ~- N5 W9 x6 ?
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!" L6 x- l/ I6 t3 V
- 请输入“22”来关闭第2行第2列的窗户。", K0 ]8 k/ s [5 m9 g
- " ①②③7 t5 |; }9 M+ {1 |$ U2 O
- 1
0 j3 b$ H+ K8 W! O: x- ] - 2
8 x! T% u, P0 m; V$ w; F0 I+ I - 3 ) o+ K a3 ~; }8 a
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
7 j+ R) \* a' L& o- `& Y# U - 请输入“11”,试着开启左上角的窗户。"
, j6 Y I+ X- m - " ①②③6 I8 p6 Z1 _, e: [! c; E
- 1■■ - {' ^& q! J# M' ^8 Y
- 2■
4 V0 ^8 @) n; P, J3 C - 3
1 ~/ N6 I; v# w. E - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。: I6 C7 e3 i4 B- h' }- o
- 请输入“13”开启右上角的窗户。"
* _. Y' @: I- D, f/ D - " ①②③4 S! m9 T" k8 Q' w
- 1■ ■6 T7 B8 K+ R6 Y+ w% b& G
- 2■ ■5 L" p, f# k2 N0 }* z' j$ Y
- 3
! m% |7 c7 G9 x- z6 }+ O3 X - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
1 v0 l* G' V5 V, v - 请输入“31”开启左下角的窗户。"
% N9 `* @" S$ D6 O/ ~3 n7 r- m - " ①②③
1 s( w& v/ X* d0 z9 X - 1■ ■% l9 @( T+ y$ U( F8 \. G. H) X3 ]
- 2 ■& @: h5 p* O( d; e' |* p6 p, N5 k8 {8 p
- 3■■
( R r1 E& U/ T! `; W2 B( C - 此时,总共有5扇窗户被开启了。
8 y) v) u6 g& h2 F0 Q. H - 请输入“33”开启右下角的窗户。"
/ Q) {" |* L, X) n% K/ C" d! Q2 s - " ①②③
x8 I* B, T+ U+ ]8 m% k. G - 1■ ■: ]% B2 n# f3 @. ~8 F/ g. S/ M! t
- 2
+ s5 V; V" Q8 J& s - 3■ ■& v8 F0 O; F0 U6 x
- 现在,只有四个角落的窗户被打开。: D4 ^/ L. X0 c
- 请输入“22”完成最后一击!". g2 w: f! Q' x6 F& w1 t; d6 ?
- " ①②③ ^/ L \& U/ l$ |, A
- 1■■■& I* i$ I4 I) l9 `% S7 `
- 2■■■
& V# P; P" L( f9 y4 Z/ E - 3■■■3 O" ]$ i! w) y: D. z
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
8 Y6 P4 X/ D- b. M0 s; ^ - : _$ _8 w' E& M4 P1 e
- ;;; 棋盘# T4 _4 G' ?& R, N6 r5 S
- (defconst *wechat-5x5-white-chess* 12288& Z+ Y, U5 J. |& {7 X2 y
- " ")
' {8 B4 k4 n4 o& c( X2 u8 @ - + I7 F$ `* ^; H w' u& R
- (defconst *wechat-5x5-black-chess* 9632
0 W& k+ @' P2 i6 @ - "■")# y4 X% p. y: `, G) J6 a
2 S8 ]/ |. t$ K0 W) ^/ L- (defmacro with-board (&rest body)2 M6 v: _' b; I& B, w
- `(with-temp-buffer* U- d7 Z) |( E1 a5 f
- (unwind-protect
) v$ ^7 P5 Y3 R4 a - (progn
1 ^3 ` Y8 J/ y p9 \2 T - (if (session "board")
) C) s" V: |1 {1 H - (insert (session "board")))# F/ }5 Q4 I+ u9 o' ?3 d
- ,@body)* ~/ i6 ?. T1 ~* H: ~
- (session "board" (buffer-string)))))9 Z6 x% x3 O- B. ]
& p2 o6 |( l7 E- f( E3 x: [9 z0 M- (defun board-init (size)
8 r! K8 A9 m- y% P+ N2 U - (session "size" size)
( `! K7 d# R; }1 F9 q, U! g - (session "step" 0)5 n/ I; O3 K8 _( W7 A: u4 a
- (erase-buffer)( l! u- l2 E. _9 B' B( Q/ X* h
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
3 i R5 B3 E6 U/ U9 @ J) K - (dotimes (row size)5 ~6 L9 r8 j9 d N Z* |
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
# _4 U3 w: o- @" B/ j" {1 B - . S j# d2 r! B% P$ s+ Q7 d
- (defun board-contains-p (y x): V: S, t: z* W3 I! U( T @5 j$ |
- (let ((size (session "size")))
* j0 \5 M- n H2 L, C3 B3 k - (and (<= 1 y) (<= y size)
$ }' s. s: e% }1 T9 D - (<= 1 x) (<= x size))))
, t" |+ F1 b4 I/ f/ z5 d - 5 l O; E* R) u9 U% ?
- (defun board-toggle (y x)
3 }5 S3 O1 ^0 C - (when (board-contains-p y x)1 R& H& M$ _7 p2 M& G" a" j
- (goto-line (1+ y))& |! }4 a$ d# g9 h
- (beginning-of-line)1 F w4 r) p" |! R
- (forward-char x)- {! M/ G. D- x& P5 F5 `/ H$ ?
- (insert (if (= *wechat-5x5-white-chess* (following-char))
. i& ]% G1 R$ Q% d' ~6 K) U - *wechat-5x5-black-chess*8 W2 s& A: i4 X2 b0 r) y7 H% E9 S* i
- *wechat-5x5-white-chess*))
0 k; W0 ]( J/ R; u+ ^1 t - (delete-char 1)))# b. [; ^; Y) G' s3 @, u- \& p
6 Q& U( L7 W h- (defun board-put (y x)
v! c4 U5 Q4 V/ n N6 f* e - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
4 d9 I# t0 m9 k' a - (board-toggle (+ y (first dir))
$ d2 T n5 K, y+ j7 y6 c' p! L - (+ x (second dir)))))
* B$ Q c0 e) U! P: m# S( A. o - : e- W* d2 U' _* z
- (defun game-over-p ()" { D, L, d+ i- c
- (beginning-of-buffer)
+ m: `& j4 l4 o( k& z8 M - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
0 G4 c/ u9 Y- J+ b4 x - - u4 a$ j! F) j2 A; @0 s
- (defun board-show ()5 f. |8 G3 | @, E' J
- (with-board
6 ]# @5 N( O( R) z - (concat (buffer-string). [% @8 E8 n+ p& N
- (if (game-over-p)# K- q9 e* w K% S- C: l
- (format "共%d步,输入任意内容返回大厅" (session "step"))- H* \7 t {" j" H) [! S
- (format "第%d步" (1+ (session "step")))))))7 z( ^, j/ g, a, P- A7 u8 v+ ^* }
- . @- S( k7 D% u5 m$ x( W0 w* b
- (defun board-position-parse (cmd)+ T2 x, i9 K; ]7 u1 k! U; \0 k2 v
- (if (= (length cmd) 2)
1 h6 h+ U- o& q8 h( x* W3 h9 G - (list (string-to-int (substring cmd 0 1))2 [- D7 ]% R, a ]
- (string-to-int (substring cmd 1 2)))7 {. B& S/ J' Y) L# l$ x2 m7 c0 |
- '(0 0)))
/ Y8 C8 U) ^) G. w; }; S# c
: ?9 t7 z; z2 I5 d$ x! \- ;;; 游戏房间 J H! G/ u- @& C8 n& ]
- (defun game-room-init (cmd)4 ^; ~, E; v0 V* k. c9 e
- (let* ((middle (string-to-int cmd))5 z# k s9 k: t5 V
- (size (1- (* 2 middle))))
; d$ f! Z3 O- B8 p - (with-board4 E; g- }6 d$ b' @# M8 L
- (board-init size)
. E6 m6 r1 G- E* a3 A - (board-put middle middle)))9 r2 U8 I! p# I" v! \( Z! N3 t
- 'game-room)9 f4 t0 Y2 ^0 K0 J% C' S$ P
6 D8 Y' @- B6 L3 [! r: C. K- (def-room game-room
: ]. w! m0 J8 {; q) m) ? - #'board-show
$ v0 R. t; a/ |. \2 a5 u - (t (lambda (cmd)
2 `. } T7 b% q0 X" y - (with-board) D- J* q, P. x. Z0 T$ i* b) p: c- L
- (if (game-over-p)
2 |- c! A+ ?; ]7 \" R2 T! v - 'living-room" x* X* N9 S; K
- (destructuring-bind (y x) (board-position-parse cmd)
+ `8 W0 w: ~$ k2 p8 k - (when (board-contains-p y x)! o9 u# X0 n' V; i1 U
- (board-toggle y x)
! }5 T- c& w- U* f* P9 V+ y - (session "step" (1+ (session "step"))))
( v! m* D: p T* R. p5 s - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|