wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。8 j5 M$ P9 a0 z' O
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- / [% G2 l, P& J% S4 S4 g2 \
- <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;">;; 定义新的游戏地图* W; j4 S# n# ~0 n* V9 Y# _ k" e
- (def-map "/game/5x5.el" ; 对外开放的URL. J l, Y2 ]$ p/ R7 \$ K
- 'tutorial-room-0) ; 默认的入口, \7 R$ o1 g1 [! l
/ s% l$ y1 D: b- u- ;;; 游戏大厅6 }' F' Q7 A$ A- ]" B) n7 m
- (def-room living-room
( ~" m8 h9 d/ g/ e( g8 K - ;; 进入该房间后的提示语, J% u7 }5 G+ S8 L7 Y8 Z
- "1. 教程
3 O$ S4 D4 \- c) X - 2. 入门(3x3)+ X+ b# T9 }/ Q H. |) {
- 3. 初级(5x5)
7 f1 J: F f4 }; h - 4. 中级(7x7)4 ?% @$ W# k9 D) ?+ P* M! R6 _+ X
- 5. 高级(9x9)7 R7 ~7 u8 Y) v! [9 p# L/ ?6 _) D; V) |
- 0. 关于作者
+ D( j' s* m- c R' |' O- b - 请选择1-5开始新游戏:"+ ^2 _, x0 m* m8 H7 }6 z% I, z" [
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名: J0 ~9 t/ g* X- [3 j1 z
- ("1" tutorial-room-0)
+ H) s/ }6 [0 Z! b - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配# D/ F% {" O: E* N' @
- ; 相应的返回也可以为函数,动态返回房间名
# Y6 t# n# Y( p9 S# z7 g& [ - (t living-room)) ; 如果条件为t,为永真
2 U. b" y9 Z, B* u8 D - 2 V) E0 N6 [3 @3 Y2 ?
- ;;; 作者信息8 o! ]8 Q( U8 l
- (def-room about-room2 r4 a: ^" s% T# n6 h
- "作者:redraiment
7 m6 u+ k$ M6 l - 微博:http://weibo.com/redraiment
- h. w9 b+ d: e. ?" m - 有任何建议,欢迎在微博或微信上联系redraiment。) z9 ?- s, p! j9 _
- 请输入任意数字返回游戏大厅。"% ^- p3 o4 y' a- M1 K8 g" k
- (t living-room))
, H# E( {) s" N- C' x - 5 R! r0 R( S$ @
- ;;; 教程
" Y/ y; n. w% [( f1 W. {, ~6 ]) q - (defvar *wechat-5x5-tutorial-rooms* 05 T2 W7 p' ~' \. {2 j7 b7 q2 E. ^# h
- "The number of tutorial rooms")
+ h; Y% Z* U0 o# @- I, v5 O
% c8 f: ]* c+ Q5 j8 m$ d1 x- ;;; 简化教程的定义5 V% i. Z( R; V, K4 [3 z
- (defun string-last-line (content)
) W9 y. O% \& l) t% l - "多行内容组成的字符串中的最后一行"
{+ N; H& S+ L/ Q; k; \ P6 i - (with-temp-buffer
) N5 \) y- l8 G, A - (insert content). P- G6 ^3 p! F2 \7 O+ A9 {0 w# j4 [
- (buffer-substring (line-beginning-position)6 J# b+ q9 S o! ^; k, r
- (point-max))))9 u3 _$ b2 l" b6 ]0 h4 w+ T
( E# k3 S7 c7 }0 X( q9 p3 F- (defun def-tutorial-room (prompt)
; y+ L0 A, b# p$ x - "根据提示语自动生成教程房间。
6 s2 Z5 } a; \# p& Q - 9 ?; w, g3 o M" k: k3 i
- 1. 提取最后一行作为问题;/ q! L4 B" c7 ?% k, }+ o
- 2. 分析问题,获取期望用户输入的内容;
) `* q2 t% N h& G: s - 3. 定义教程房间和重复提问房间。"* K3 j: t( f/ e! S% C, S) E
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
6 s) f% r0 Z6 r) r! G! f( } - (repeat-room (concat room-name "-repeat"))+ u) ~: j+ V' T
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))# |( G9 Z% M$ M% `
- (question (string-last-line prompt))
, M* j6 S2 P6 C: l+ I) r - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)6 Z! l {2 Q+ A
- (match-string 1 question)))
4 ]+ c! r. }9 p: a0 k - (doors (if except0 S% V! \' a! o) j
- `((,except ,(intern next-room))
* J- J1 X& k5 |+ A" V - ("q" living-room)
4 h+ H& M- [$ F5 `# i5 J - ("Q" living-room)7 ]* u# \+ l. y; f& `2 f8 c# e$ {
- (t ,(intern repeat-room))); w+ x& k+ G; E/ n$ }( o" r5 e
- '((t living-room)))))
1 F+ u6 ~; V: K! ]4 q - (def-room-raw (intern room-name) prompt doors). q( x8 j1 R1 X3 q* ]
- (def-room-raw (intern repeat-room) question doors)))! ?7 ~& v, x8 ?5 `
- ) a, X* t) e6 ~6 ?- G
- (defun def-tutorial (&rest prompts)
- T& f8 c* c" l2 L: t - "批量生成教程房间。"6 N. ]- {$ N. h; r& p2 W
- (dolist (prompt prompts)9 o4 B- F0 u- N' b7 `3 M0 H
- (def-tutorial-room prompt)))
6 r, ~: }4 h9 X1 x2 z
' C9 u; n2 ^3 G$ _* X i( B- (def-tutorial* }5 a6 F" }; O5 C0 i/ t
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
G/ I" a0 `7 J& L - 1. 教程
+ D; d# T+ ] o# ` - 2. 入门(3x3)
* l$ W- Z" m2 H# Z) _ Y" ] - 3. 初级(5x5)
* H1 s2 V r1 u# L7 ]7 J - 4. 中级(7x7)
5 v0 G7 V, p3 v3 A - 5. 高级(9x9), l- k* {8 ^ I
- 0. 关于作者& R; c4 P5 N& K2 W+ n9 f
- 请选择1-5开始新游戏:; [/ l T* A# H* _6 M
- 您现在正在游戏大厅里。
/ K( }5 O6 }$ d, Q6 w# ~. C% E - 请输入“2”进入入门级房间"+ v5 D) I0 x. G$ D
- " ①②③
' H( Q' Q8 s5 n- ^- S - 1 ■ % l6 B3 q/ ]1 V9 u1 f* \
- 2■■■4 m5 K- n! W8 ?1 K
- 3 ■
! C4 V3 E5 m& D9 p0 j - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
: [ C* O5 D# _6 \8 A) N - 请输入“22”来关闭第2行第2列的窗户。"
% e( P' U c) Z: W8 R - " ①②③8 @' r! T7 [5 H' t; j
- 1
8 P, r/ h% X/ x3 A" E - 2
% ]4 P0 h F+ ] E1 j - 3
" t5 l* w+ M3 E - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
) X$ \4 X! [) Q" {2 @: X4 O2 c& A - 请输入“11”,试着开启左上角的窗户。"
7 @1 c: _4 v' z+ N3 i - " ①②③
: {/ @1 \! |$ I2 S6 {4 L2 y: U - 1■■ & L, y t1 T) n" p
- 2■ ' w* j7 `/ a6 Y
- 3 & X L8 o: v3 u z* c( K
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。7 A( E, u& `6 e4 v& M1 s' |. _
- 请输入“13”开启右上角的窗户。"
' H- d3 f. m! l v - " ①②③( w: y% s |! z G. b
- 1■ ■0 }) w; H& u2 ?/ d0 `" q- i
- 2■ ■
1 V/ E6 B4 N- p5 S6 ?* w4 } - 3 A$ A, I, D* u% N
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
! {2 K" t2 `' m- r3 ~+ p" _0 } - 请输入“31”开启左下角的窗户。"
- U8 e ?8 u6 O; D/ v c% a: j - " ①②③8 X" i! b6 p, b7 D6 ^
- 1■ ■. H& x9 H* S* n' d3 ]
- 2 ■
& y T! ]/ C3 \" v7 H% T* Z( r - 3■■ * P" t0 c3 U: C5 @+ v5 c- Y
- 此时,总共有5扇窗户被开启了。5 x& N- ^. \4 X$ R- S& I1 d
- 请输入“33”开启右下角的窗户。"
; N* T8 t# S- |6 s4 R l - " ①②③
9 i5 _. }0 n3 N9 l - 1■ ■
& r1 e% z; H/ H3 n; I - 2 5 ^2 b0 M/ a5 ?, R; a
- 3■ ■9 z7 V' W3 v5 u. f* J- M7 ]
- 现在,只有四个角落的窗户被打开。! x; N" |$ C9 G( S/ F( ]
- 请输入“22”完成最后一击!"7 l ]2 p3 e5 w. F$ s& w* k+ G+ I
- " ①②③
6 k! U5 o( p. n! @: n+ ~3 t8 d& A& l - 1■■■: f1 y# e0 n3 c. k+ |3 S
- 2■■■
; L+ \+ ]9 b! o+ @ - 3■■■* d+ u3 y" x; C0 X7 z) J
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")7 C8 m5 s; j4 x3 Y, X
3 O; F8 O# {# P" m' P- ;;; 棋盘
0 p( Z- h2 u+ \/ E, \, c- b$ z# x) @1 B - (defconst *wechat-5x5-white-chess* 12288
# [- V# T+ a3 z4 `- \ - " ")- D3 a; X+ O1 U+ C. T
- 2 x. m! ` p5 N' \# o) \/ s. c" J
- (defconst *wechat-5x5-black-chess* 9632
) W9 G" N$ ^2 i$ ^5 \ b: x7 o - "■")) {( B! C6 u( B2 e3 J% b
- # U: g$ ?% R& w6 ^( v
- (defmacro with-board (&rest body)! K: K6 u$ a% L% M$ N% @+ n, w
- `(with-temp-buffer& |; S" ], z) U7 {) h
- (unwind-protect* ?! C9 ]1 @. H$ f! f
- (progn
! A6 H, p5 g. P6 }" w( X - (if (session "board")* H+ F, j, @6 f3 t) |
- (insert (session "board")))0 N3 N( p- |, a$ J m7 r
- ,@body)
' _9 }( S( c+ \8 W& m' \ - (session "board" (buffer-string)))))$ a& d+ m) ^% B- Q( O
- ^" O% d4 D$ h! t- (defun board-init (size)
5 v! X5 J3 @& T" [0 l) P - (session "size" size)+ H5 E' Q+ N5 m4 ^1 \
- (session "step" 0)( N8 H8 V. w- Y7 j5 D- A& W4 @: E
- (erase-buffer)
- _& C; {7 x- z& a, s$ r* { - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
; b" n* d; F2 H - (dotimes (row size); }1 O6 \6 {. }/ _8 w4 K
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
5 d3 v& z3 C/ n' v! ]
. R" o5 {/ o7 Q9 _ R) @& M; O6 c- (defun board-contains-p (y x)
) R) g" j2 N( ]9 L& Q - (let ((size (session "size")))9 p, ?- V: c1 f0 a( O7 ^
- (and (<= 1 y) (<= y size)
; O! r! |4 n; e7 @: l) H; l: H - (<= 1 x) (<= x size)))), W# v" `, Z0 p/ \7 Q
- 8 p2 J1 c; O1 a. W
- (defun board-toggle (y x)
3 m4 r0 m7 R7 C% ~( u - (when (board-contains-p y x)
$ _2 X! b* t2 J! r& ^ - (goto-line (1+ y)): d [ B0 t& ^$ B9 N: M# R$ i
- (beginning-of-line)
5 S Q5 t1 g2 M" L b2 Q - (forward-char x)# W6 B+ X1 m- [ T( ~6 K
- (insert (if (= *wechat-5x5-white-chess* (following-char))
# p3 C' T- }9 U - *wechat-5x5-black-chess*
% _: s- |4 B9 w - *wechat-5x5-white-chess*))5 y; e, w; w8 D8 G/ b# ~: H
- (delete-char 1))) J) s: J8 [1 f2 U+ a" D
. S* O/ L# T r" h9 m- (defun board-put (y x)
9 O, D6 b( @, `9 P - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
. e0 X, Y: Q7 g& w. D - (board-toggle (+ y (first dir)) ^+ Z! K, L4 R& z& N7 s1 H1 [
- (+ x (second dir)))))
+ U1 T! N+ n* y& S - # \- F2 o* V# S: w( @
- (defun game-over-p ()
1 b5 e* h6 L) @- }( n* v y' @: X - (beginning-of-buffer)0 E, v2 t4 W V; w
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
9 n6 S) ~7 V% l1 a* Q5 i
3 Q" f5 e7 p& G8 _( L3 j6 x3 t0 p0 {2 ]- (defun board-show (): O9 ~% P, ?7 z
- (with-board
2 T9 r# j' f& f+ T% _ - (concat (buffer-string)
n( H) `% u5 ^0 h8 V - (if (game-over-p)7 O2 M- @' \( Q/ S0 Z' |
- (format "共%d步,输入任意内容返回大厅" (session "step"))! C% m, G" [9 M4 g/ j, w
- (format "第%d步" (1+ (session "step")))))))
' r0 \) N: }" y9 ` - 4 n. P' V7 [- b: Z- d q0 N
- (defun board-position-parse (cmd)
& n+ ^8 J8 O7 D, y; G+ t - (if (= (length cmd) 2)
* v+ x( C2 [8 y. h2 G# n+ d7 e! } - (list (string-to-int (substring cmd 0 1))0 p; v- K ]8 H4 s3 U
- (string-to-int (substring cmd 1 2)))2 T1 H7 f; E, J5 A# j7 J n
- '(0 0)))6 B+ I- H9 I8 X# p" O
" g! ?& u" r( g* [* Y9 G; o1 T- ;;; 游戏房间 _2 j0 U) D9 m7 G# q) N
- (defun game-room-init (cmd)2 C! I) R( A& t% P+ m& ~
- (let* ((middle (string-to-int cmd))
* Z3 q) _5 k3 i) f4 e( i3 Y7 j - (size (1- (* 2 middle)))): d( w4 ~$ ]6 K3 q/ Q8 ^* W: o5 \
- (with-board
4 N* j) M. h& {, _ - (board-init size)5 w2 I0 m$ Q! j0 ^
- (board-put middle middle)))
! |, `0 q9 @& j- H - 'game-room)
0 I5 M0 y7 z8 G' e) s$ ]( \& N2 k5 b - $ x' O+ ^. v, N% d) y
- (def-room game-room, P+ ?5 \6 S/ F$ L( O
- #'board-show4 e1 }( q9 j+ t% d2 `8 `$ I
- (t (lambda (cmd)# o+ U _) e$ @* F i% U' a% G( }9 v
- (with-board
1 Q7 s7 Y3 a$ p- O% |9 G3 Z1 [. q - (if (game-over-p), v: G2 @) x2 r0 j: P
- 'living-room& W% D; R' A9 V% ? C
- (destructuring-bind (y x) (board-position-parse cmd)
" a4 z/ `9 h% `2 D" B+ p& \4 ~ - (when (board-contains-p y x)% {7 _3 D2 A- k' K; U& S [
- (board-toggle y x)
2 K4 ?0 A) G) c% V4 [% e4 x - (session "step" (1+ (session "step"))))
1 g$ H- } q5 e& a( G - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|