wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
4 v. a9 J, g0 P, A借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
9 U: r/ o( ?' R' {' ~* D- <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;">;; 定义新的游戏地图
4 _% _7 o8 t: j. O9 w) T! O' p - (def-map "/game/5x5.el" ; 对外开放的URL
( }) e6 L5 r! T1 y - 'tutorial-room-0) ; 默认的入口. U; H4 A0 ?0 n R* R g; t+ j( \
- # k. f, v0 p, @0 X# m" J2 `
- ;;; 游戏大厅
& h* G# q% o' B - (def-room living-room
/ ?/ C/ {' a6 S6 q2 { - ;; 进入该房间后的提示语
6 b3 O( G9 t/ D9 N7 h - "1. 教程
/ R% u! M9 ?- \0 H) y3 i - 2. 入门(3x3). B) s1 p0 x9 u+ g
- 3. 初级(5x5)
' H. A" I8 M: c& d - 4. 中级(7x7)
* S8 D: C8 t% r3 C! J$ _# X$ q7 Y$ @: } - 5. 高级(9x9)
y) x& F; K8 ]! P - 0. 关于作者
7 H' {% [8 K+ r0 s$ `) ~ N - 请选择1-5开始新游戏:"5 b1 o/ G8 f/ {# @$ j( v0 e" P
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名" g4 @7 W& c! X1 _% D+ s
- ("1" tutorial-room-0)2 K( Q, e m( v; @* u) @: x4 S. b
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
, U8 C+ q @+ L6 `0 J$ ~* d3 D - ; 相应的返回也可以为函数,动态返回房间名
* d6 n+ {; g3 y' V1 m' H* y* \/ K, ^ - (t living-room)) ; 如果条件为t,为永真8 A+ |. A9 C' [
- 3 m5 S6 u* f$ M. `/ U) {+ W; t2 q
- ;;; 作者信息
' ?2 `& k% z1 Y - (def-room about-room( o- B3 {$ n; A, g: i# I
- "作者:redraiment- g9 I, `, ?/ p
- 微博:http://weibo.com/redraiment
7 ~; _7 p* `+ [3 a" d; ~8 [' y; t - 有任何建议,欢迎在微博或微信上联系redraiment。$ I) x& d7 s7 ~# j/ f/ |" r
- 请输入任意数字返回游戏大厅。"
9 o& k; s4 s) l. }4 A - (t living-room))
, l8 P" Y& m1 r i, s0 M5 U
5 C' z) ^5 N) k# R7 t5 ?- ;;; 教程4 n7 U( Z9 ]* B0 i
- (defvar *wechat-5x5-tutorial-rooms* 0( F, O! N" L& U0 P
- "The number of tutorial rooms")3 Q3 @! f8 F9 o- I6 Z
- 4 R- q: e( V3 r# J
- ;;; 简化教程的定义
# ~8 k7 `4 z6 v+ u: i w - (defun string-last-line (content)
1 z: S. i3 z0 W& Q! x6 }. Z - "多行内容组成的字符串中的最后一行"
- b- m0 `8 }2 @; w4 ` - (with-temp-buffer+ b- s% g7 f r( J
- (insert content)
9 x4 m4 G) p* ?" v6 Z - (buffer-substring (line-beginning-position)
" d8 o3 `2 r3 ^5 z# o$ [$ A& c1 b - (point-max))))
! U* L4 J. @) C6 N$ R6 z( U
4 W' B4 I% D+ W7 g7 n% d b! O- (defun def-tutorial-room (prompt)
! h/ V0 \. f5 b+ K - "根据提示语自动生成教程房间。
+ U) k7 T4 I4 S* F" P" F7 a
" {& @# N5 H9 {. w2 _* |( q- 1. 提取最后一行作为问题;1 w/ t) J3 |' e1 X9 K. B
- 2. 分析问题,获取期望用户输入的内容;5 O, n+ [ V6 @; r2 M8 d
- 3. 定义教程房间和重复提问房间。"; K; y; n( s1 e
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
/ d6 d+ ~& O z2 p9 j' v - (repeat-room (concat room-name "-repeat"))) l+ d- O* D& @3 }
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
; i9 L- R) v8 h% a0 E8 ~ - (question (string-last-line prompt))
% i( ?4 `+ }. b. H+ ?: g4 `1 s& e - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
2 x; y5 s3 o* u0 Z' i. q. M - (match-string 1 question)))
" h3 ~) J1 ^- X0 B# D8 [ - (doors (if except1 b9 F5 D) B, G8 K: x4 d" ` Q) B
- `((,except ,(intern next-room))4 K% s, V, G! e: E- B/ k, y+ M, m
- ("q" living-room)
! F1 ~$ N) S/ j, c T# q - ("Q" living-room)
$ }! A) ^ e) y) T) m - (t ,(intern repeat-room)))
+ }% x6 u3 q5 B) h - '((t living-room)))))
3 t/ f$ d, [7 G- j' N - (def-room-raw (intern room-name) prompt doors)$ i! S3 N2 b' h5 c9 h# n1 O a! Z
- (def-room-raw (intern repeat-room) question doors))), {& R( e% A. d6 B" Y
% C {' ^( B2 }) O+ c8 d5 z7 m9 |- (defun def-tutorial (&rest prompts)
3 ?1 V9 L, {, {. C, ?# D - "批量生成教程房间。"1 J; C/ B6 f G% |: A
- (dolist (prompt prompts)
! v2 P) g* m, N - (def-tutorial-room prompt)))+ j. m. b+ F1 S9 I( E
9 U$ ?) y2 Z r$ q' S- (def-tutorial; X. O! R( n' |+ |4 L! z# k& z; ~6 s
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。) }5 R) i. h9 y. r. T
- 1. 教程
& r7 W s$ Q# w+ M; ]8 H - 2. 入门(3x3)1 q* _2 N1 Z: k) j
- 3. 初级(5x5)5 m7 R5 o2 q8 I
- 4. 中级(7x7)
1 q/ M7 D W" @* W6 s$ V# p - 5. 高级(9x9)4 y' ^1 B+ T, b
- 0. 关于作者' m1 U7 \+ j: Q% W& m! o
- 请选择1-5开始新游戏:& F3 d7 y" _! D) d. w
- 您现在正在游戏大厅里。
% v/ i) s3 z- A4 e - 请输入“2”进入入门级房间"
" Z2 X# a0 u* g - " ①②③
/ T( ? J7 g8 Q1 G) w" g+ B/ o - 1 ■
v- z, u5 b8 X' ~ - 2■■■
2 Q. l2 L/ G* Q( \. `) p' c5 C - 3 ■ : F: [* {# _0 P( E7 K- i; k. [: @7 T
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
8 U* i& s: P- Q. k: {) u- O# L, s - 请输入“22”来关闭第2行第2列的窗户。"
t7 M' j0 U. j - " ①②③. C7 R1 X' R3 I0 g, ], E* T6 ~8 \
- 1
; p8 g7 y0 T. ~5 r g - 2 _. K" q* I. X7 G# q; V
- 3 6 X! ^" m( i3 n6 u9 I) c" ?# |
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。) I0 m+ @7 l: s: E4 s. @
- 请输入“11”,试着开启左上角的窗户。"
5 g2 u0 x$ Q; q" V7 c- p% |+ k U - " ①②③
3 V+ q# |& k+ @: U7 Q8 j+ S - 1■■
, |/ o+ Y# x* F% i8 I. | - 2■
/ I" V, b: i8 [( l- Z - 3
+ s7 j" k* ?, \* [1 S - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
2 V3 z2 [2 ]# ?( n- ^ - 请输入“13”开启右上角的窗户。"; p( ^/ I% B5 z+ L4 v
- " ①②③
V ]0 N! E; `* z8 q+ d - 1■ ■
0 x( I& x* `; T. S6 Z - 2■ ■* m" X5 R* \' N/ e& W
- 3 9 }( b7 ~* g! m6 F8 ~
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
% `* `8 `+ P S- A7 t; \ - 请输入“31”开启左下角的窗户。"
$ w4 ^6 P! F1 n+ \ - " ①②③: X' K+ P% B" W0 i, v0 a
- 1■ ■6 U! q0 O) Z9 n. p+ D
- 2 ■& V4 O/ F+ P5 e! ` }: U' v& c
- 3■■ / O& C* T4 H8 G. {0 C/ i* e
- 此时,总共有5扇窗户被开启了。9 k) @+ y' U7 t' K
- 请输入“33”开启右下角的窗户。"
, _- S7 U, X2 n& g/ f7 f4 m2 N - " ①②③& S' O* F8 l9 ?- j! c$ w' [- ^
- 1■ ■; \# i" C4 `) q" G- T
- 2 * i+ d' a( u p8 ?- J/ q
- 3■ ■
. v3 F# u$ S& H; V* ], i - 现在,只有四个角落的窗户被打开。
2 L5 U4 j! v3 o$ z. s5 R - 请输入“22”完成最后一击!"$ s. h, k" v) l$ h) q" W% S, O5 o+ p
- " ①②③# h- ?2 u7 Q% b
- 1■■■
1 I _5 u. \' t! X1 \; j/ g, y - 2■■■" R5 e S; a! w" e! a x1 Z8 W
- 3■■■
4 z+ K; o- C* f: q5 a/ _8 z, c/ t# F - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
5 J/ ? {1 t. f$ C+ G" `' m7 h
# W1 d! r Q$ @- ;;; 棋盘
7 V6 F) c# e* n6 ^3 H; C0 z - (defconst *wechat-5x5-white-chess* 12288
; F8 \+ `" g6 V% Z5 r' P! n3 t - " ")* K7 u* A) L/ k, H2 D
- # K# x# Z: A Y* O( g
- (defconst *wechat-5x5-black-chess* 9632
$ o }+ R% Q- f" s - "■") a. J4 B |9 Z( R" c- u' z
- 3 z) S3 l- ^ _
- (defmacro with-board (&rest body)
6 z9 e+ t" U& n: r2 u {% v - `(with-temp-buffer
* g% Q2 O2 `- ~* S7 p& [$ L - (unwind-protect
. W* R' K( ?$ I t! M, u3 u8 c - (progn) L: o& _' c7 p, \: t
- (if (session "board")7 s4 H" E t! B( `2 e! t$ t
- (insert (session "board")))9 e g4 C. i2 b0 i
- ,@body), l4 n9 f& l# B, P, q) \2 p+ \
- (session "board" (buffer-string))))): W( y9 }% P2 b) |
- & C9 j" E9 ^; K+ D0 q6 U2 S
- (defun board-init (size)
. Y) n: s/ j9 \( _ - (session "size" size)
, R8 H f4 q2 |+ f; R' U9 b - (session "step" 0)9 T2 c. B! j; _5 w, ~1 q0 ^# l
- (erase-buffer)2 ~6 |3 I+ b+ W& ~
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))2 { E& N5 |! ^* W
- (dotimes (row size)
b/ M' j% l3 a% O3 } - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))9 H- j& o# {! S0 e
* ]# h8 c. b0 w( z- S: F- (defun board-contains-p (y x). H: G7 _. @8 `$ V9 V, ^
- (let ((size (session "size")))
4 o& {3 Z9 R1 t5 d* u8 w- k2 a - (and (<= 1 y) (<= y size)
# [# n$ x9 I; F* L9 M - (<= 1 x) (<= x size))))
2 Q& C; z" y$ U6 M: t& }) R - 4 ?. Y+ w- I$ C x5 M
- (defun board-toggle (y x)
8 n; O% j7 C: y7 o" o$ S# R - (when (board-contains-p y x); Y) f/ \6 q6 P# a% W( s/ _
- (goto-line (1+ y))
6 ?# s: O) L: b; [. ~2 D* G - (beginning-of-line)' R8 R2 L. o' S! [" b
- (forward-char x)- m6 F7 S: Q& P) `$ _$ V
- (insert (if (= *wechat-5x5-white-chess* (following-char))7 a- J1 c) S% s( E2 [; j
- *wechat-5x5-black-chess*: X, @2 E: i- i& i' Q0 I8 c
- *wechat-5x5-white-chess*))
$ {; |6 A& T$ W9 u, I, Q1 b - (delete-char 1)))5 d# g3 s" W$ V5 w3 V% [1 {
- 0 W. S; k$ k# b9 e* A. g
- (defun board-put (y x)4 k$ C/ L4 c$ Q% {* d% f
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0))), U, V* P) a9 o9 i1 H
- (board-toggle (+ y (first dir))+ q. r9 }: q8 V6 Y
- (+ x (second dir)))))
+ j* C: W" F% T. q! c( j/ w- K - 3 @/ i. @6 T6 \6 z1 C. N
- (defun game-over-p ()
% h/ `+ U; {) O - (beginning-of-buffer)" x+ z% G" o: t9 t8 q. ^* {6 }
- (not (search-forward (string *wechat-5x5-white-chess*) nil t))). h: r5 H* k. O5 J" J
. U! @. z4 f, ~- (defun board-show ()
: N( x: c2 p* o- l8 Q3 l - (with-board
7 [% C* Y& C/ V5 i; ^8 I& W: s - (concat (buffer-string)! Y5 O' }5 R; v
- (if (game-over-p)% D2 l- S" ]& q! B5 z
- (format "共%d步,输入任意内容返回大厅" (session "step"))0 i* d. w$ V% r; s; f
- (format "第%d步" (1+ (session "step")))))))
+ Q: S& p1 f( J0 C- l
3 x; N3 w2 ?1 W7 w, f- (defun board-position-parse (cmd)( c0 C: o" W9 ~0 ?3 v) k- d# G7 F
- (if (= (length cmd) 2)
. y& D1 E7 D) L/ [) ] - (list (string-to-int (substring cmd 0 1))
9 X# F W5 f$ j0 U4 u+ i5 w - (string-to-int (substring cmd 1 2))); C8 D+ @7 v# }% C- w
- '(0 0)))
6 j/ w0 f- P6 _7 R2 h8 T* _( b
& B4 r$ [$ Q* O5 B- ;;; 游戏房间
4 e* Y- h3 K) d1 {2 _ N - (defun game-room-init (cmd)
" f# X5 Y) j: k& I3 T1 g& r - (let* ((middle (string-to-int cmd))
8 M1 N* Y: l3 T/ R" `& l/ ^- S9 C - (size (1- (* 2 middle))))
# V5 r6 { K% {$ ] - (with-board
- u A6 [4 l& Q9 Y- V+ p- r - (board-init size)
- I5 ]0 Q S3 v) {% ? - (board-put middle middle)))7 k" |1 r$ _* P; U, x0 L
- 'game-room)5 Z- k4 P$ }/ E6 v- O
' F& L0 d; t: t# F9 E6 W- (def-room game-room8 j0 K2 P6 T2 f) ?- [4 w3 Y; N/ z
- #'board-show
. m5 T% l% s; Q4 @) Y - (t (lambda (cmd)/ w8 U! l" g1 _* k4 U' ?
- (with-board
. q' ~& U1 p* h - (if (game-over-p)
: J$ u5 I. F5 N3 F - 'living-room
8 L0 o0 C9 C3 [% J- h, W - (destructuring-bind (y x) (board-position-parse cmd)
$ P0 k7 _$ U. e# d' }6 [+ q. B - (when (board-contains-p y x). E6 Z' L7 W% I9 _
- (board-toggle y x)6 W6 b+ ~2 ~" _: w4 ]7 s, c2 N) K
- (session "step" (1+ (session "step"))))
0 P5 X V6 K) `+ `* F - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|