wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
1 D; I6 s7 e/ d2 t借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- / D. Z! w( L2 Y
- <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;">;; 定义新的游戏地图
/ `# J8 r7 P. r- E - (def-map "/game/5x5.el" ; 对外开放的URL
7 q. Y8 F1 J/ Y, _! \& D - 'tutorial-room-0) ; 默认的入口/ _( W4 l# C8 N) A3 s+ D2 l
- - \, c5 c8 z3 q( E0 }( i. x9 H
- ;;; 游戏大厅6 n9 I- t; p- c6 A( w1 Q9 \
- (def-room living-room- V; s Y' q5 [4 J
- ;; 进入该房间后的提示语2 A& F! t! P9 } ]
- "1. 教程
( |( K5 b" ^5 @: i9 O - 2. 入门(3x3)/ i( w& {% h& h1 ?+ Z
- 3. 初级(5x5)* z; W! T$ o2 S0 z( a
- 4. 中级(7x7)
- u$ N% F, q: r0 a, Q P7 k - 5. 高级(9x9)
( B' R7 Q$ [# F2 H9 F, X - 0. 关于作者& T, X+ J" {# w4 K' y4 D$ G g
- 请选择1-5开始新游戏:"
* d- f" {1 K+ D' D5 r - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名& h$ l' d1 P/ o7 z$ f ~ f. M
- ("1" tutorial-room-0)
( W& A% r% m0 S+ w% M3 B$ j - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配0 c+ ^5 D }. N
- ; 相应的返回也可以为函数,动态返回房间名( g% N0 l9 C L% M+ n, g
- (t living-room)) ; 如果条件为t,为永真
+ W& X3 d6 V1 i7 |6 Z8 [, e e$ `5 e
, t8 t/ h: T6 O7 p) B- ;;; 作者信息! ^! z: |' ~/ D1 k4 C* }& |
- (def-room about-room" ~# C: f* K; B* S" g* M) ?; H& M
- "作者:redraiment
7 s1 `& Q8 M( X& x6 H) W& h1 t5 n! L. r - 微博:http://weibo.com/redraiment
) _+ ^2 E. d e4 n. A - 有任何建议,欢迎在微博或微信上联系redraiment。- V8 v- T4 i; `! T* j# Z
- 请输入任意数字返回游戏大厅。"
- I. k( B6 i" ~8 S - (t living-room))
+ ?; d" e0 p8 Y, o, g% h q6 s% v - ) j: q) @0 R, c# p% [; F
- ;;; 教程
7 N( Z [! l2 b - (defvar *wechat-5x5-tutorial-rooms* 00 Q; W9 A% H" j- @" A
- "The number of tutorial rooms")
% W. x: D+ k3 G - O: j2 H0 x2 s4 @ d
- ;;; 简化教程的定义6 x* p9 ~- K' b" y {( o& `
- (defun string-last-line (content)' t, J x( y! P q7 l$ ^$ q
- "多行内容组成的字符串中的最后一行"
V- b% L3 d: I) V0 |7 I1 m - (with-temp-buffer
( H2 Z6 z z: C' {7 R! w+ E - (insert content)
; e+ x# `( M! H2 l7 X" e6 g - (buffer-substring (line-beginning-position)+ V4 S6 E8 Z7 e; O# f3 B5 v
- (point-max))))
# ~" n% r! F+ Z
" ]7 V, M6 j! l# A- (defun def-tutorial-room (prompt)
$ y7 @" _9 B- U) h8 a; A - "根据提示语自动生成教程房间。& | F7 F3 c Y. k5 s- U
- ' t3 U O% h% _' l
- 1. 提取最后一行作为问题;
: |4 |/ ^# f' s$ Z: Q: {" R" X - 2. 分析问题,获取期望用户输入的内容;* R3 G0 ?% g4 y- ^$ z
- 3. 定义教程房间和重复提问房间。"
0 @: S7 H* ?1 E - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))4 U% Q7 |3 ]# V7 k' M
- (repeat-room (concat room-name "-repeat"))
/ @# U9 r. C# E+ f7 H0 _ - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
/ \/ J" \1 Y: F; m - (question (string-last-line prompt))
5 p2 K1 S0 O# c8 \& R: w& E/ } - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)3 V: r9 d4 k$ J6 l4 _
- (match-string 1 question)))
- X- ~0 c7 X- { J' ]3 ^ - (doors (if except
, ~! q8 y' c% ]7 y. |8 p- c - `((,except ,(intern next-room))
) U6 G @# V) G - ("q" living-room)5 P2 I m2 q0 m! [/ {. `+ G
- ("Q" living-room)
# }4 I, d) _3 ^ - (t ,(intern repeat-room)))
2 z8 H# U, O( M - '((t living-room)))))
3 }% }4 O: J E" Z9 ?- C- E7 ? - (def-room-raw (intern room-name) prompt doors)
( T( m- |6 e$ g - (def-room-raw (intern repeat-room) question doors)))( `4 Y5 r, W4 B4 T6 o1 [2 ?; r4 O# W2 q
- ' O! |. {& w+ S% o
- (defun def-tutorial (&rest prompts)
. r. t P$ Z5 i+ q - "批量生成教程房间。"
^. B: |+ W0 x9 L' V1 i7 p - (dolist (prompt prompts)
! z9 x" ^1 S' P* E - (def-tutorial-room prompt))); b' X9 @* e0 ~1 [
- p+ g8 W+ C3 I! q8 ^ z; L
- (def-tutorial8 j) d: U/ Z5 v* d
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
" e7 N; B6 N( r7 {& d - 1. 教程4 `$ K! O4 g5 o* @4 ]
- 2. 入门(3x3)! c- r( y z {0 j
- 3. 初级(5x5)
8 n# s' |+ y3 \4 Q! B1 n - 4. 中级(7x7): R$ |- S2 ^" Z4 \4 J- l' t$ l0 r/ E
- 5. 高级(9x9)
6 ~! s) |. O) S7 u - 0. 关于作者
) a e; }+ W8 q: \2 _8 S - 请选择1-5开始新游戏:1 X) y: Q4 Y0 D
- 您现在正在游戏大厅里。# I1 [) R" J) p; @1 ?
- 请输入“2”进入入门级房间" |+ t- h8 T8 {( ]# t2 p9 m
- " ①②③
1 J! I& V. T7 K0 m - 1 ■
7 [7 J* t: P O- k6 ?# Z - 2■■■7 o8 l' r& J# L3 r, a' |9 a3 L
- 3 ■
0 ?" _% e: Z0 |! W - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!4 J! N$ l: K* H, V' }
- 请输入“22”来关闭第2行第2列的窗户。"$ Q/ R. w; Z3 L# t9 w- w) g) K
- " ①②③
2 K& F2 q7 b! e/ r! O% C) y - 1
3 o# H+ M, L+ p6 F. C - 2 ( }, L5 x3 n0 a3 L
- 3 T, {" j2 ~( @: [: H* F# w. f' h; l
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。$ w% N, B f% i- B0 B7 g# {
- 请输入“11”,试着开启左上角的窗户。"
& E3 f9 }9 E" d4 z1 ~ - " ①②③
" H. g) z6 ^1 K% X& B" B - 1■■
& ]* Y* ]) \( \8 e) B& c - 2■
' G" E- T# h. [6 i- n2 ? - 3 ! i7 U9 m, e2 B, R' N
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
* F1 Q$ g. e" F8 ^# {) p' J - 请输入“13”开启右上角的窗户。"
+ Q$ S& y/ x2 F {$ [% V# @ - " ①②③
7 _5 G S0 t! z& R+ B - 1■ ■
& E* E* p% \! F* x4 L6 c - 2■ ■$ l7 j2 l5 W! z1 n6 [
- 3
7 D+ h P J- Z% \2 p - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。+ ^ D' _( |! s8 H
- 请输入“31”开启左下角的窗户。"9 M+ k1 ?3 L+ a( l
- " ①②③ M6 g% k' |7 S3 K9 r- n
- 1■ ■
! x: n: `; C9 K. a3 @ V( Z - 2 ■8 f3 ~7 A; I2 L& R% [7 n4 l
- 3■■
* i% p( J0 ?; e# h% ?, \1 o) H - 此时,总共有5扇窗户被开启了。9 T& n5 L+ Y' W5 F7 r0 S L
- 请输入“33”开启右下角的窗户。"( [$ T! t U% z/ H
- " ①②③8 Z- y: l+ s; v9 {6 B3 G
- 1■ ■1 K' b' B+ v" \/ X
- 2 ) Y5 k' O; s% X9 c+ h1 ?7 Q
- 3■ ■
+ N- N# m8 D9 K3 p! r; W5 W - 现在,只有四个角落的窗户被打开。
. k: B( y# f: I* y; w; Y* o% Z2 g5 D - 请输入“22”完成最后一击!"
' K% t% n, g3 ~: G6 z! u% r - " ①②③
9 |1 l- |$ [, V+ i& c7 `; M5 \" ^+ o - 1■■■* }" z9 j4 r0 t( U: @
- 2■■■
; {2 Y' u; a K+ q7 q4 j - 3■■■
: [, _- F0 {% \& | - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")3 s# s" ]/ L% f7 C
* F; o2 e$ i0 ^% @9 |6 `" g& \( N- ;;; 棋盘0 m0 |. S0 v1 q# q1 O
- (defconst *wechat-5x5-white-chess* 12288& ?9 H2 ^1 e. L# |9 S. y
- " ") f/ G1 R" o9 B. e: o5 [* d
% T# P6 ? _0 {* W$ @" l- (defconst *wechat-5x5-black-chess* 9632
) O" o- x3 R, x9 m - "■")
4 `0 l7 f7 E; {; ^3 l2 a - % X$ P0 @ n$ r2 w. E6 s
- (defmacro with-board (&rest body)
* O# j O( T! E9 v7 N6 o - `(with-temp-buffer+ y' [ J; {5 a( h2 w$ Y, b
- (unwind-protect) g* {) s y7 j/ Y l# v( I3 I
- (progn3 C8 L% D- U- Q# G( x0 Q; y
- (if (session "board"); S' k. F+ B2 r" i, o3 g$ h
- (insert (session "board"))), | C; f" u4 A/ ?* t0 j
- ,@body)
. u( w# G# `- q9 h8 K- [! e9 T9 V - (session "board" (buffer-string)))))
- o3 E& ~, H& p
9 W0 h [" J& {! y- (defun board-init (size)+ y/ C5 A1 |7 d5 q. B5 f! x1 N
- (session "size" size)" D3 M; V h+ }; g
- (session "step" 0); K* B4 }: e' E$ K& h+ J" I- ~9 f \
- (erase-buffer)/ t0 I( k: W: i7 G
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))* w) m' o& ^/ r# ^
- (dotimes (row size) x- y& T# k$ x0 G% k0 p) ^1 m+ P
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
$ h! }1 z6 b4 O: k$ w; {& p
+ S% a, Q8 R+ W' z3 r+ {- (defun board-contains-p (y x)" }+ ^' A; d" k/ ?9 S
- (let ((size (session "size")))
6 L; ^$ q. w: V, d- G1 I - (and (<= 1 y) (<= y size)' d4 f' L; Y, v3 O/ G. a7 t( g
- (<= 1 x) (<= x size))))9 m& |# O: W: K* k) }% R+ ]% l' b
4 I, t5 ^, D& p$ }. b2 M& {- (defun board-toggle (y x)
. x& n4 e* O: s; o( o - (when (board-contains-p y x)
1 l' ~5 k3 L' `) S; S! o# K - (goto-line (1+ y))
6 R3 k# b$ v1 O1 I: c# u5 v - (beginning-of-line)
- L$ v- H% z, K - (forward-char x)
7 a) X. c+ M5 v' T. P. ^2 v - (insert (if (= *wechat-5x5-white-chess* (following-char))
2 K9 J+ \* k- ] - *wechat-5x5-black-chess*$ Q8 f$ W" n6 r
- *wechat-5x5-white-chess*))) X. p @& v4 A4 a
- (delete-char 1)))
) `8 D2 [) w+ t& `1 `6 { - & o9 v; Q' u: z0 K
- (defun board-put (y x)5 q, }; q* y$ ^1 g6 G
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0))). G- \8 s+ w) V3 }: n& z" ^0 _
- (board-toggle (+ y (first dir))4 c/ z& E& T+ r
- (+ x (second dir)))))
4 N% l# w# ?3 T - . q/ Z; X- k8 o- U( A
- (defun game-over-p ()' v( D: P- Q+ Z) t8 F7 u; I
- (beginning-of-buffer)$ k# f7 d- y$ k1 I' d* E) l
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
! x/ j- {- }! f* M! Q4 o0 s
# Y$ z$ r2 p0 M/ h! _: ]% }- ?- (defun board-show (): U9 ^. {* A7 ~7 x! v
- (with-board$ F6 g: H- H3 n, d: A- Y: `1 T
- (concat (buffer-string)
- Y- M0 E5 ?' X' n$ H; p9 i. p - (if (game-over-p)
$ M4 a/ @& e1 k C* S - (format "共%d步,输入任意内容返回大厅" (session "step")); V7 S' N- q8 a$ [) K# @6 U7 \
- (format "第%d步" (1+ (session "step")))))))
7 X7 ^) h$ Q0 {: h0 j
" I/ h( n$ B9 |& J/ z- (defun board-position-parse (cmd); p( R% |; B- A9 ]
- (if (= (length cmd) 2)
]' D8 k2 l2 {* K2 Q. S, D6 D: H0 t - (list (string-to-int (substring cmd 0 1))
( o/ Y8 Y$ B) x3 E - (string-to-int (substring cmd 1 2)))! w( x4 D$ J, a) K' u
- '(0 0)))9 x8 V! L f) O. }# _
/ Q( f! j; s! H8 b0 ^6 H- ;;; 游戏房间
# P0 m3 f! U9 C7 P - (defun game-room-init (cmd)! j4 d* [0 J+ r- X2 W
- (let* ((middle (string-to-int cmd)): f- b. q K0 g0 n9 l5 l
- (size (1- (* 2 middle)))). r. ^$ y! _( ?9 X- u+ s+ z& ~
- (with-board
& N4 O& a' M& C! p1 G. L - (board-init size)
( B) |- T, y5 t7 Q- k - (board-put middle middle))), N5 I& |4 P! _, H' i
- 'game-room)
. w4 h% ^: R& c. l) K l. X
7 O- }7 Y% P8 T' H3 h( q4 \- (def-room game-room
. r* T5 W7 ]. H8 I2 L* M- U - #'board-show& N' d& d; I, R$ K' N0 b |
- (t (lambda (cmd)6 y2 N, @- t+ I% Z7 L8 {
- (with-board; Q. n/ R) u; s$ L( u
- (if (game-over-p). k8 ~6 m4 a* H% c4 E
- 'living-room0 h! t) C3 i1 v, r7 s
- (destructuring-bind (y x) (board-position-parse cmd)
% ]5 |" k6 H; i/ U* Y - (when (board-contains-p y x)
8 ^) ]3 }8 q2 U - (board-toggle y x)
: L6 Q3 p2 u, u9 H2 I) r4 y - (session "step" (1+ (session "step"))))
/ R1 N J. Q% i" v3 q' m - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|