wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。) u) `) c; s+ K. n1 F0 ?
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
# n2 b" v) f: m% b- <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;">;; 定义新的游戏地图% e* b/ h" Z" Y) A
- (def-map "/game/5x5.el" ; 对外开放的URL
9 Z; o8 B3 X* X( u2 V$ V+ V1 v - 'tutorial-room-0) ; 默认的入口8 B1 x) J) F } S+ n
) Q- l1 G1 d v4 u- ;;; 游戏大厅+ N* l7 @4 L$ ^! o9 n
- (def-room living-room
- } ^4 D. z* |- K - ;; 进入该房间后的提示语( j( Q. ]; N1 r- T O* \. Y
- "1. 教程
/ W" N ?3 w/ h7 M3 O+ v6 B - 2. 入门(3x3)
4 g; K1 |' V* d5 ^' G, x5 K5 f0 y - 3. 初级(5x5)
) P) U+ e- G# X& a$ u7 g! E% u - 4. 中级(7x7)5 ?, B" j& x4 Q8 I |4 J
- 5. 高级(9x9)
, m0 e' G, t, D - 0. 关于作者 {' `+ g+ X0 ~! i# { \$ u
- 请选择1-5开始新游戏:"! E8 L n' C* j& A# ]% L% w+ S
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名9 } Y1 c6 g6 z
- ("1" tutorial-room-0)
0 D$ E* r7 u) B. g/ }# D% e - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配# c+ g; L4 L6 M3 D9 }% U
- ; 相应的返回也可以为函数,动态返回房间名/ m% k1 k, ]* l( ? ?
- (t living-room)) ; 如果条件为t,为永真* Z% z5 Z+ s7 \( w1 K D
4 ^" j' i9 e3 F6 P9 W' D3 b- ;;; 作者信息
7 E+ y8 t4 b0 s: b% s - (def-room about-room
4 k1 B" i0 d+ z. y. w6 r - "作者:redraiment r4 [) T v z- z% n' d5 f. l0 s0 s
- 微博:http://weibo.com/redraiment
' u" V7 n7 |4 X5 X4 T g/ H# x - 有任何建议,欢迎在微博或微信上联系redraiment。
/ h# l1 B8 @% O - 请输入任意数字返回游戏大厅。"
x& n( N' h( j* {0 C - (t living-room))
+ y0 S0 B& O# b& ]1 H
) l2 }( _& B+ O1 \( d5 {* ~- ;;; 教程) }& D3 W8 x2 _% m' I( }
- (defvar *wechat-5x5-tutorial-rooms* 0* K% _& c" |! R
- "The number of tutorial rooms")& L) b$ h" ]4 f) C! _% L
; d+ N% l1 N$ m7 m- v; O& g" U- ;;; 简化教程的定义4 N$ l8 i9 e2 A' H4 C& N7 s4 [
- (defun string-last-line (content)
( `) f6 C, n% |3 k/ P - "多行内容组成的字符串中的最后一行"' t9 L; l' G6 S5 Z/ r2 J
- (with-temp-buffer
; S1 n5 D5 t, K: W" z: f0 q - (insert content)( c$ ~4 | K5 V, w2 S
- (buffer-substring (line-beginning-position)
. v0 m! O& D' d% ^7 L - (point-max))))( g' Y. u" N6 Y& f" l8 ]- [
2 p" y3 @# c/ b! |; B% j- (defun def-tutorial-room (prompt); j" M/ A" j' H. e- I7 j: Y7 F! p5 B
- "根据提示语自动生成教程房间。
; r# K. l! i) `; }5 b - " G* @4 A: x( V9 w! {( j2 g Y( W. d6 c
- 1. 提取最后一行作为问题;
" e% F0 w7 f- O: h3 X/ ?0 j - 2. 分析问题,获取期望用户输入的内容;
; I, b8 k3 ?* u/ ]( p1 r - 3. 定义教程房间和重复提问房间。"2 O% S1 ?0 u: y* B1 r! X1 w+ \7 C
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
1 M8 V# m" @9 R/ \* W - (repeat-room (concat room-name "-repeat"))
# _4 ~4 x2 |! ]9 k% t' p3 M - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
$ d w. c& m5 ?6 Q% Y8 B7 A - (question (string-last-line prompt))2 C z) T. u$ X4 ~: R
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
" m7 d' _7 l; b% ?/ w t! p* {% J - (match-string 1 question)))8 R2 C/ N, F6 \" ]: r! V
- (doors (if except- F* [6 P0 ?, r4 A- J0 ?. y! P
- `((,except ,(intern next-room))5 c9 p: w3 f1 G1 k( `
- ("q" living-room)
4 g8 H5 ^3 N) c6 P - ("Q" living-room)4 x4 ~7 X" O v% Z, r
- (t ,(intern repeat-room)))
* f- U8 n/ @6 @! t' C/ R! K! D - '((t living-room))))); h, ?. G: z+ @) l; C$ g' y+ c
- (def-room-raw (intern room-name) prompt doors)
2 F9 v. ^6 c" X; `0 V4 w - (def-room-raw (intern repeat-room) question doors)))
) H6 t- C# O, J, `0 T* N B
$ @, e* S2 @: _& M- (defun def-tutorial (&rest prompts)
0 b1 t4 O! p4 U' ~ - "批量生成教程房间。"3 x# e4 ~+ V d2 G) ^1 D
- (dolist (prompt prompts)
1 X4 @4 i5 \( ~' h/ i - (def-tutorial-room prompt))), a2 Z2 @* w' g! S
- , I0 Z& _0 V$ w! [- Z' R
- (def-tutorial. D) S5 t8 a" C d- c! N
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。% V5 L* U7 }+ E, P0 F, b; U* S
- 1. 教程) H# _( M$ \ c
- 2. 入门(3x3)
% Q' Y" |* o& W5 s3 [ - 3. 初级(5x5)/ B$ J* S4 D4 M$ \7 {
- 4. 中级(7x7)0 p* g5 n) Z% W! T
- 5. 高级(9x9)8 f( ^ k) @5 D. H
- 0. 关于作者
2 w8 Z) C! }/ b5 X9 r6 _! M - 请选择1-5开始新游戏:2 [4 N) g7 v- c6 w [
- 您现在正在游戏大厅里。) _! w# t9 d+ p4 @$ a0 n
- 请输入“2”进入入门级房间"
/ j- `0 K/ u* X: e6 }2 U - " ①②③
5 f" Y- B' N. q! z, v; N, g. [ - 1 ■ * |# K: j% e7 y/ l! o: B/ f# M. B
- 2■■■
' f) Z. u* p3 b0 L& K - 3 ■
/ T. N/ U$ J v1 Q! m' n - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!; N& S0 G: _4 m4 Y6 j9 b7 v( H1 E
- 请输入“22”来关闭第2行第2列的窗户。"" T4 K: F' W, c0 l- r, ]
- " ①②③9 x1 v! ^8 V& @* |9 O) w& F) [* ?
- 1 ) `. r! d. m4 T2 q. Y8 I9 O8 `0 @
- 2
$ ]7 `( ]0 x( c3 K - 3 * R+ Z9 [; q j6 n4 F+ H2 C1 F
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。) ~ T ^; d+ a% m
- 请输入“11”,试着开启左上角的窗户。"
6 {6 L# x# y- c! \) G - " ①②③: V% a4 F" b9 H
- 1■■ $ O, z3 ^! W- {: G8 I
- 2■ . D, c I7 V/ l4 @
- 3 . b+ ?/ v9 R2 b
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。! C* h' Y& U. D4 t/ X* u
- 请输入“13”开启右上角的窗户。"! X5 n% C7 U: t, B; b7 a9 x
- " ①②③, D8 M/ P! B5 P- i+ v6 I
- 1■ ■
, j3 G5 u/ v. E/ `% h - 2■ ■6 U1 X+ ?* Q! K4 Q1 M- m( E* q
- 3
0 B% X! V9 v& K+ a- A1 U. [ f - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
, f' `, P- a5 D5 h% L9 U- u - 请输入“31”开启左下角的窗户。"
# }9 C4 y+ W1 e T9 I1 R- X7 E* l - " ①②③
4 @4 U- P! w0 q, A: W - 1■ ■$ ^- B Q, H+ m8 U- A, U% y. ?. _+ p2 v
- 2 ■$ E5 s0 ~/ p% h& |
- 3■■
2 u ]) J& K) h" U7 j - 此时,总共有5扇窗户被开启了。" l- R8 a+ t/ |, g7 F9 J
- 请输入“33”开启右下角的窗户。"
0 }# S, ~& X+ S - " ①②③# b: {8 h5 U" \; c
- 1■ ■8 D% |/ B9 A y& l$ s1 z
- 2 v9 o* K4 d2 n- t1 [2 K6 ~0 ~
- 3■ ■
0 X* e6 i2 U! B+ k; I8 g, ] - 现在,只有四个角落的窗户被打开。
) B+ S/ `: [* M3 n7 |6 _ - 请输入“22”完成最后一击!"
# u1 e; }5 D K5 h - " ①②③; r+ s( w0 J% t3 T7 U) s8 G
- 1■■■
. H8 S) N0 ^4 v, ]. M* T" q - 2■■■+ \: y! t9 L$ G4 _
- 3■■■* O" U" ~, |- C) n% U
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")* v5 r8 V2 N9 L; K) q5 m
- 9 _* g% ?9 y1 o0 ~7 B) y" d
- ;;; 棋盘; L, k# }" b8 J1 p
- (defconst *wechat-5x5-white-chess* 12288
: m1 f; Y, z1 q( a, S3 l - " ")
3 j$ I. N! e: G. t - $ Y5 Z) U# S+ I
- (defconst *wechat-5x5-black-chess* 9632% h0 W5 ^" ?0 Q {+ X% C
- "■")
' h# [) k+ Q0 c+ H% \ - 6 o/ R" o) G' A- o! {+ ~
- (defmacro with-board (&rest body)
' T0 I" S1 _6 S9 `7 Y5 |: ]/ R - `(with-temp-buffer
% I2 ^7 L: }2 _) F - (unwind-protect8 ^8 V# R. e' ^
- (progn4 z! G4 e; \: ? h! t& U- L
- (if (session "board")
1 L; L/ l N, b: \! G& f3 \ - (insert (session "board")))
9 M1 F2 R6 k! D, X7 F - ,@body)- @* X; ~9 Z; v) L
- (session "board" (buffer-string)))))
' `7 E7 d# F) m0 S! {; T& j. t
, ]& ^+ C, Q, y* J$ m- (defun board-init (size)
3 |2 q- O) X+ e, I3 |" `6 { - (session "size" size)
' ~* S8 W- _& Y5 q( O - (session "step" 0)7 s( r F% V/ d) o( x5 a1 f
- (erase-buffer)* f/ N L0 }8 A
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
8 }7 f5 u9 a3 {9 \) w Q0 C - (dotimes (row size)! f4 z$ Z6 M8 u* s. V
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))$ u' T0 t6 n% A# z n
1 U' C I( l# V, \" [2 n8 {9 M- (defun board-contains-p (y x)
* _, A$ m8 n2 v e - (let ((size (session "size")))& ?/ o& G7 F l8 {) V9 q
- (and (<= 1 y) (<= y size)
; k8 D. u# v* h/ _+ B - (<= 1 x) (<= x size))))7 W: ~2 w; \9 L& O0 n$ r, l4 E
0 X: {* E( b( ~ W+ Z+ R- (defun board-toggle (y x). ]7 |& d. m+ f+ O& Z6 l
- (when (board-contains-p y x)
3 |; q9 K1 }+ F6 J3 ~8 e' Z8 v% V - (goto-line (1+ y))4 j% a3 \3 b" M( J: ^( M$ g
- (beginning-of-line)
6 u9 I) r. p; p5 m4 G! L - (forward-char x)+ s0 X8 ^ X+ @+ O6 Z0 t: W1 C" l% Q
- (insert (if (= *wechat-5x5-white-chess* (following-char))
2 U) P4 M* ]' U$ b3 E' A$ m+ f& W - *wechat-5x5-black-chess*; ]9 p4 ~& C! U9 [ A
- *wechat-5x5-white-chess*))& ?4 X) i' L( Q: h, U, v9 C7 H% }& [
- (delete-char 1))): F& P/ w Z! R* U
- & `; I/ x: q% N% z' J- o
- (defun board-put (y x)" m' a6 t7 e1 r) {5 A) Y8 j( X
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
9 u! L w. X! Y, N# q( `; k - (board-toggle (+ y (first dir))( b" m! w8 c- s" ?% A. C
- (+ x (second dir)))))
+ g8 W+ q0 b' S1 C, L! J3 P& _
7 q' p0 o5 _1 l6 F+ d6 j- (defun game-over-p ()
$ S# B, N3 \1 R4 S - (beginning-of-buffer)
: W2 G/ t* T& D7 s - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
, E; K4 F; z, G. [- F
! ^/ ?0 I$ m4 x L# J- (defun board-show ()
) u/ D* m( P, \7 U8 x - (with-board" A1 T N5 x; n' y4 j- A: P
- (concat (buffer-string)
$ N! J9 a0 e+ b2 b( A7 k - (if (game-over-p)0 h( l' `; |* L' G8 [* | Q
- (format "共%d步,输入任意内容返回大厅" (session "step"))
! }+ F2 d. K& `; b2 F - (format "第%d步" (1+ (session "step")))))))
7 j% N9 S/ Q2 \3 I, K - ' {4 H: j6 }" ^+ @5 m& [
- (defun board-position-parse (cmd)
0 ~( H5 E4 R' i$ m; h) E - (if (= (length cmd) 2)! o/ E& y! }5 S+ m
- (list (string-to-int (substring cmd 0 1))3 j- u2 k. l( z y" h* T
- (string-to-int (substring cmd 1 2)))
; o I0 p: D/ x" A( l4 \% G2 N - '(0 0)))
0 N' A' w8 @4 }7 q3 P R- f - 5 c2 Y# r; J. i
- ;;; 游戏房间. F" W; D# a$ @$ C3 s* ]5 I$ T
- (defun game-room-init (cmd)
]( p) A1 Q) o) W - (let* ((middle (string-to-int cmd))9 n0 C8 m: R# a: p7 v3 l
- (size (1- (* 2 middle))))
& ^' C& }4 O$ y- _8 O - (with-board
a& W0 S" [. ~$ U$ a% f* k* L+ u& y - (board-init size)& z9 c* H4 J+ a- a! s4 V
- (board-put middle middle)))1 P1 F( C1 h2 \
- 'game-room)
, A+ g# [0 Y, r* y* u
2 J N6 K: Y7 A- f2 }, {- (def-room game-room
: {1 N8 j% s5 }/ K7 w; |6 w - #'board-show# K% d, n+ { W1 s
- (t (lambda (cmd)( ]" S! U) ^4 @0 P7 f. ^+ D
- (with-board
, P v$ |. W5 m6 [ - (if (game-over-p)
: I, `. a1 I" D" S0 a - 'living-room! b; J) m6 k4 N5 w* y, J( x! E9 v
- (destructuring-bind (y x) (board-position-parse cmd), }) l( p2 ?. t/ Y. ^2 ^. u/ a
- (when (board-contains-p y x)- ]- s, Z/ Y! U! f1 j5 e% u
- (board-toggle y x)& {* S% P2 F3 ]4 O; w4 l( A
- (session "step" (1+ (session "step"))))
( s& ~$ J% s, _0 f - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|