wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
: E% ?2 h& G9 q( x& f2 I( k3 I借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
- }# R4 ~; z/ B& ~! s. l- <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;">;; 定义新的游戏地图" V' t5 Y$ u2 R+ [" _
- (def-map "/game/5x5.el" ; 对外开放的URL
9 J H+ O" S, `3 y/ ~: z - 'tutorial-room-0) ; 默认的入口
% L2 R5 O, O$ }' S
$ a. M( ~' q5 g+ f3 ~" y- ;;; 游戏大厅- V- Y) ?7 f7 d1 j
- (def-room living-room
( S8 f: l4 E9 v/ G4 I - ;; 进入该房间后的提示语
5 \: u1 P( _' a& i( Z( K6 `- @ - "1. 教程$ M9 D+ R0 c C3 e W3 v
- 2. 入门(3x3)
$ |% J4 P1 A; X5 _7 d, X4 g - 3. 初级(5x5)
+ s+ u5 L" s+ W2 ~0 S7 ~1 e - 4. 中级(7x7)& J: X* \) ~3 X! T% H
- 5. 高级(9x9)% I4 O% ?# g; T% ?6 [! T
- 0. 关于作者
% Y/ w% i" c& R* T% L( H% z$ @ - 请选择1-5开始新游戏:"0 h. |( _4 N) A
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名1 n, m A% [. G7 g2 Y" d
- ("1" tutorial-room-0)
O0 Y$ K; g) ~ - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配& w+ y) Q4 N: M5 K' b. `
- ; 相应的返回也可以为函数,动态返回房间名2 P# g4 I1 a& y2 z" A
- (t living-room)) ; 如果条件为t,为永真
8 Y7 E9 V. {: _2 i5 Z
* @1 Y* M, \# t+ t7 \9 y w6 K* B- ;;; 作者信息
+ _/ V( y1 u; z6 G5 f - (def-room about-room
3 A' g4 e7 b4 W' a4 ~' x3 E ~ - "作者:redraiment: X8 e8 Q) Z" d7 g6 ?3 V
- 微博:http://weibo.com/redraiment
* B9 T; ^; J* y$ L - 有任何建议,欢迎在微博或微信上联系redraiment。) c1 {! {4 ~$ }# B; U1 _6 c2 F
- 请输入任意数字返回游戏大厅。") K J7 u& B W9 d7 q( N
- (t living-room))
2 l/ G, A' M2 t8 ` - 3 |; m% M' ^4 Y; W, J' u
- ;;; 教程% w9 H6 v7 ^ w! Y# m
- (defvar *wechat-5x5-tutorial-rooms* 03 K6 d9 S4 w0 ~5 U5 Q* G
- "The number of tutorial rooms")
+ E7 p6 `- P% a% h - ( W# H$ n8 }! p" E6 `' H' X% t9 o
- ;;; 简化教程的定义1 V4 Q: [( ^4 z% i$ {; @
- (defun string-last-line (content)& o; h) j% d* S4 d# S+ S8 T8 E( k
- "多行内容组成的字符串中的最后一行"" N: j; h" Y @
- (with-temp-buffer# b+ ?! o- H: m( u9 p
- (insert content)
4 d( @- R- g" M8 ?" Y, D - (buffer-substring (line-beginning-position)
( q" {5 \. H4 z3 `+ F6 T - (point-max))))
% k. {2 z0 E$ H% r5 [
# g% ^/ Q* ]7 L8 V1 T( u/ a- (defun def-tutorial-room (prompt)1 D0 T+ P% `5 @% _0 A
- "根据提示语自动生成教程房间。6 ^* i/ f9 K- }$ u
0 A; x+ s7 x2 A: l- q- 1. 提取最后一行作为问题;
8 g3 V4 u8 s/ L: ` o% k - 2. 分析问题,获取期望用户输入的内容;3 W+ ` o6 F" o9 w( ]$ D6 w1 S
- 3. 定义教程房间和重复提问房间。"
; b; t+ V: e4 _1 s7 F9 p0 R, Y% U - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
# \+ [/ f5 ^# g/ @ - (repeat-room (concat room-name "-repeat"))3 Y4 I; n/ n8 J, E E
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))% W( w M# l6 K0 `" n
- (question (string-last-line prompt))
/ F+ [9 `* P! I7 T5 B" f0 q - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
: A" Z8 Y, h4 D1 }. y3 a - (match-string 1 question)))1 o+ D0 k; k0 m4 D. h
- (doors (if except
# T6 }8 M+ ~3 t- W- [ - `((,except ,(intern next-room))6 m( C" ^) X' n5 m4 P
- ("q" living-room)) F! ~ ^" I# v B" m9 n- j- s# ^) g
- ("Q" living-room)* v& u: F; p0 B
- (t ,(intern repeat-room)))
0 e1 N2 f1 h" V4 C - '((t living-room)))))7 Z' D- T4 i7 |( ~ F/ p% K
- (def-room-raw (intern room-name) prompt doors)* K2 a" Z8 U" F
- (def-room-raw (intern repeat-room) question doors)))
2 F! s `: ]' y9 `( O0 ]1 g - 0 t5 q' r* K! u5 M8 @
- (defun def-tutorial (&rest prompts)0 T; Q7 R- a7 W
- "批量生成教程房间。"2 @: j3 {4 l" R& m: V, K+ |. O
- (dolist (prompt prompts)
/ M4 ?" j/ A5 P( i4 E9 h - (def-tutorial-room prompt)))
2 d. O8 Z0 L1 J1 V2 H" Q1 C - 3 W9 M0 |% Z/ B, a6 z
- (def-tutorial) y7 b9 m8 { \% {5 Z/ ~
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
& Q0 W0 @, O. k. r9 Y9 S5 {/ o- O - 1. 教程
E5 T5 ^4 V2 Q& Y) B - 2. 入门(3x3)' U4 B& M6 ?0 o: i
- 3. 初级(5x5)
5 f- j& V5 T( k* J) D& r8 |4 L4 A - 4. 中级(7x7)
) m+ f) t7 g+ J7 N - 5. 高级(9x9)
8 N5 z! }3 I; [, R - 0. 关于作者
8 s K3 A' i3 f, v6 V% J - 请选择1-5开始新游戏:
7 b9 o/ C8 Y. ?0 G( T6 K - 您现在正在游戏大厅里。
! v" @" q% I. a Q9 Z9 q - 请输入“2”进入入门级房间", {, \/ P7 F7 W. A! w
- " ①②③
9 h) b6 c& a! V3 j$ O0 b - 1 ■ " `3 U- y; H* a" `* ` I4 j* A) y
- 2■■■0 [7 }9 R* \9 S- T( h
- 3 ■
+ ~0 f" ~& X. k& ` - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
8 i4 ~! F# r3 O9 b - 请输入“22”来关闭第2行第2列的窗户。"+ M0 `8 Q" X0 s; d8 A4 {1 u
- " ①②③
, M8 a7 w! s: u- Y - 1 5 Y0 @' s# B' y* s5 `! j; {5 W4 i- }
- 2
0 s1 S. l0 P' j9 _ E3 r - 3
+ l7 u, N. H8 p2 i7 P" i3 P f# z8 E1 [ - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
, n$ ]' b0 F" ? - 请输入“11”,试着开启左上角的窗户。"
' c7 z N' g1 I" Y. E - " ①②③+ [# G8 }* {8 f/ b. Z
- 1■■ ' j1 Q% E1 W R- T: C% S
- 2■
2 U7 ~6 c; V3 p2 _+ o( B - 3
# Q2 h8 ]* Y3 u. u: r - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。6 }( y+ T% t e- \3 {8 A% B) n4 j" R
- 请输入“13”开启右上角的窗户。"
" g4 q4 D0 [3 N9 l - " ①②③
6 d# w' u9 S4 T/ D P - 1■ ■
3 p) q" z/ E! S9 y% E - 2■ ■
" m3 m. r* O b& j7 w8 |& D - 3
; _& ^2 {8 n3 u! ?5 H - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
% ^: h# w& m1 S+ I2 S# e, ` - 请输入“31”开启左下角的窗户。"8 j6 r/ p% g2 c! B
- " ①②③3 W' `* J. v: [1 A" M
- 1■ ■
& J M' N5 d) t3 f - 2 ■
( r! A6 |) e* {1 O - 3■■
8 v+ ?3 J5 ^4 Y. d4 n9 Y - 此时,总共有5扇窗户被开启了。( ]1 }# ?2 b* w! L/ Y) F( ?+ j
- 请输入“33”开启右下角的窗户。") o" o+ ~$ Y6 A% p( O
- " ①②③
4 T; u+ K( E8 f( |+ { \) i& U: f - 1■ ■% U; i# d! P9 N" E( v% L2 I% {
- 2
, t2 A0 Z- s: d! n4 W; Z, u& ? - 3■ ■0 o0 }6 B; ^8 F+ | k; V- s7 }
- 现在,只有四个角落的窗户被打开。
+ Y& @6 S7 l8 o1 ]. m2 i/ @8 W0 Z - 请输入“22”完成最后一击!"
8 C3 H; G% C% M5 { - " ①②③2 V4 h9 S9 `5 I Y V
- 1■■■, F) m! W/ u( a' @9 D) B
- 2■■■% ^3 Z9 U+ _. U6 Y
- 3■■■: T8 g. V: ?4 W8 X$ A
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
6 _# @0 N& T0 a K% c7 l
; O# o2 Y8 y# ~8 W: G2 O6 d, O: D; n- ;;; 棋盘
g* X2 Z% ^4 ?* l9 L$ m+ r - (defconst *wechat-5x5-white-chess* 12288
! S3 I- T' w4 x: A5 h - " ")
# I; j* r4 k: I" w- T5 B* t
4 t0 h8 U+ K# ^; @8 Y% y1 ^' O1 X- (defconst *wechat-5x5-black-chess* 9632
" Y7 a6 I4 ~3 O; `( P - "■")+ n5 `' d" {% Y B3 `$ B- o
- + X" n+ X3 d3 E. N- N
- (defmacro with-board (&rest body)
4 f) v1 s; E# i! B6 f# i, @ - `(with-temp-buffer
1 w! K8 |; }# Y* K% @ - (unwind-protect
1 ?( x# Y1 ]8 w- R - (progn
4 M/ o% w+ ^" F! t - (if (session "board")
6 e4 z/ S! O: v' y/ j$ [% k - (insert (session "board")))+ J4 o1 v5 Q" {! Y! ?5 O
- ,@body)
* @- Z X& v3 f J! c$ e - (session "board" (buffer-string)))))
" m$ J3 ~8 I- z! \2 P5 [# ` - 4 G; G4 x" h6 p9 }+ m
- (defun board-init (size)& m, c" H0 w5 I6 @$ c0 c
- (session "size" size)4 c2 |3 j* ?! K& v. }% L( Z
- (session "step" 0)
# O- i* T8 v7 [5 U2 f - (erase-buffer): M |; D% h: B+ V# C/ k9 @
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))0 Q* P* U# U/ e* |! v
- (dotimes (row size)
/ E6 S. _" ~7 o - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
% E, g4 i0 C2 w! v& ~" g
! W. k& t0 W( B" T4 N- (defun board-contains-p (y x) ]7 I$ z7 F; k! E, D
- (let ((size (session "size")))4 ~4 P, d7 G- L! {% V1 K
- (and (<= 1 y) (<= y size)/ _- i, o( |# Z, ~
- (<= 1 x) (<= x size))))
9 N8 `( _7 j, f2 S - 4 {6 o6 s" ~( F) Y5 Q
- (defun board-toggle (y x) s7 j" A) |. E, \
- (when (board-contains-p y x)
3 w0 s' |' f1 T4 M* } - (goto-line (1+ y))0 p6 q- E6 e T
- (beginning-of-line)
0 f9 J, d8 E5 Q5 @, v. F - (forward-char x)
& x& i6 i* o% i4 a& x6 J+ d - (insert (if (= *wechat-5x5-white-chess* (following-char))1 h6 \' a8 \1 h
- *wechat-5x5-black-chess*
$ ]! I9 k. } f7 g/ }$ Q* m, D- @5 F) h - *wechat-5x5-white-chess*))4 e! x1 _# T- D* M2 h5 g K
- (delete-char 1)))
& p3 m0 T! ~# C: | - 4 Z9 p5 i6 E# C8 |! u4 [9 Z
- (defun board-put (y x)
; J( b4 w% t, }" ^, S( ] - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))$ U* m) {0 G3 h
- (board-toggle (+ y (first dir))4 n# e$ O' g* r& y- M$ Z2 W% L
- (+ x (second dir)))))
' @0 @5 Z) r: N/ t2 K4 t - 9 j( a {+ b& q: @! b- B& I
- (defun game-over-p ()
) O( V8 b' I. K1 D/ a; b% q - (beginning-of-buffer)9 Q% D: E1 c Y- E. Z, A
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
/ d* \8 Q, A0 @; k
* n. @2 k' J: V; n* G L a- (defun board-show (): G. m0 x5 V; P9 ?: p2 J( F
- (with-board% A8 g7 _! a5 s& l$ }
- (concat (buffer-string)) B. u* s! v& J: E( G
- (if (game-over-p)
# W7 V2 H. p1 g; m% W5 @' } {+ ] - (format "共%d步,输入任意内容返回大厅" (session "step"))5 Q" Q" w- F; Y2 t/ v% o
- (format "第%d步" (1+ (session "step")))))))
6 s/ V3 |/ s) R: q3 g8 ]7 Z
/ N' W3 L) l+ {0 R. G/ \# z- (defun board-position-parse (cmd)
1 Z0 P$ J* ~! v1 o - (if (= (length cmd) 2)
: [) {3 l- h/ c& A$ I# m# d - (list (string-to-int (substring cmd 0 1))
. G5 r$ U$ E& G# U w; T" C - (string-to-int (substring cmd 1 2)))
" x. U* f) h D! [, h - '(0 0)))8 D7 ] ]- g3 g
; J% w' f- l" e) B2 [/ V# T$ }- ;;; 游戏房间8 t$ E4 ^( g, \: B
- (defun game-room-init (cmd)* b) J7 s4 w- C% O9 [
- (let* ((middle (string-to-int cmd))
- y4 c3 x: ~+ W6 e3 }2 | - (size (1- (* 2 middle))))
/ B, m8 C3 @' x9 z5 F - (with-board
- O; p2 C/ E$ L$ X) k M: h - (board-init size)
- M$ t" {' W3 _ y$ I, }7 {( o4 j - (board-put middle middle)))3 j4 o: ^$ E5 c2 `- O9 M, R1 l
- 'game-room)$ t' N |9 A' s) x
5 V0 s& h7 ?0 A3 O- a) s- (def-room game-room6 o, I' t+ \" E U& {5 v1 E' `! w
- #'board-show5 D6 w8 v0 O* {) }
- (t (lambda (cmd)
0 t0 C A: W, e& c3 ^/ l( A7 r0 Z - (with-board
7 s$ q" F/ X3 G( R1 G: Q' j- ]( n- t - (if (game-over-p)* u# ?. A. H+ n3 s. L( ]- d+ e
- 'living-room V) T" E J0 b3 D% [
- (destructuring-bind (y x) (board-position-parse cmd)" `1 @' z/ \2 r. K; x3 P
- (when (board-contains-p y x)
% a @# a8 p6 e' ]% i - (board-toggle y x)
$ f" X' o0 K: D/ N7 c) l' e - (session "step" (1+ (session "step"))))9 [+ k \ r: W
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|