wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。- |3 @+ t' |! e. t3 z4 u% v
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
! r, c1 b, T6 I- <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;">;; 定义新的游戏地图
; Q5 Y( f M8 F9 i - (def-map "/game/5x5.el" ; 对外开放的URL
: z, s7 O' ^! R4 ?5 y4 l" I2 y - 'tutorial-room-0) ; 默认的入口
8 P& A/ ?2 I3 ]- C. N
' h5 Q. B3 n- T$ m- ;;; 游戏大厅
6 c3 p- D+ h' F0 K8 n - (def-room living-room
- b; _. O- i+ I- u9 H6 {, M' X - ;; 进入该房间后的提示语- ^# j7 p( a, G m, s8 Q
- "1. 教程
" N) x$ L+ q- X - 2. 入门(3x3)# L4 L/ U4 E( X" M' C8 e' }
- 3. 初级(5x5)
/ G+ ^# M. e! p& j - 4. 中级(7x7)5 _/ Q( v" a( _; l) q) ^
- 5. 高级(9x9)" {0 _9 ~* N2 {; q7 Q, h
- 0. 关于作者# z1 z0 \9 l. \9 R. t
- 请选择1-5开始新游戏:" [- A5 c/ [9 ?6 J& O! j- }
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名! y6 a! {' @: }. D f6 c8 z3 o
- ("1" tutorial-room-0)9 R2 p" H, {* G* j! a
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
; b. \. y' O u4 S' z) p - ; 相应的返回也可以为函数,动态返回房间名
3 Q5 [" N# q, w - (t living-room)) ; 如果条件为t,为永真# I* y6 W5 X1 n5 R2 D2 [
- 8 W' `" a/ h3 ?
- ;;; 作者信息" ^; @1 }4 l- `4 l" P
- (def-room about-room
! p2 @( h) K4 d# i! }1 A - "作者:redraiment6 A/ j4 R1 }, {# u2 ]
- 微博:http://weibo.com/redraiment7 z; f% q" E1 P' A
- 有任何建议,欢迎在微博或微信上联系redraiment。
. b0 |! T' m w/ H* B, H - 请输入任意数字返回游戏大厅。", x) d- C; U) g4 k6 c
- (t living-room))9 M# V" O& V+ x" J' G( f2 @9 J
- 5 C9 t$ v$ L: e+ J, J5 d
- ;;; 教程2 d Z0 y* V: |+ }. y. ?; X# d, R/ L
- (defvar *wechat-5x5-tutorial-rooms* 0
) I7 ]8 l) N# X) h: m8 p3 _ - "The number of tutorial rooms")
* U6 p8 k" G* X- H, A
, z; {! d% t( I' K' W- ;;; 简化教程的定义' q- Q3 \$ j1 c% R" \' f- X
- (defun string-last-line (content)' C; ]+ q% ~/ r9 U
- "多行内容组成的字符串中的最后一行"0 ~* G, O& D" @; u9 J ~3 I! n
- (with-temp-buffer1 K* \0 F4 X( S7 u1 \# G
- (insert content)4 p7 C6 _; S+ O7 [ m$ R. ?3 _
- (buffer-substring (line-beginning-position)
/ C4 p$ f! {" B5 g - (point-max)))) g, m( \$ L5 P. b3 h5 a) i: A, u
" Q. n0 b: b6 ^. i5 ?1 Z( f- (defun def-tutorial-room (prompt)* B+ u* L& p1 v- x4 @$ s8 J1 P
- "根据提示语自动生成教程房间。
& N" g u" ~. b5 `- ^- w' } - ' X7 ]! R, e4 }
- 1. 提取最后一行作为问题;
% K, U: G$ Y+ i/ y% S - 2. 分析问题,获取期望用户输入的内容;; b6 K+ f8 R& |% u% Q
- 3. 定义教程房间和重复提问房间。"0 a5 A1 e/ x1 Z
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
( k( w; o6 Y! b: ? - (repeat-room (concat room-name "-repeat"))
1 V! t; x- o4 C) }7 G1 x9 Y+ W' R8 [ - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
4 L0 v& Z6 l c4 l0 T- ~ - (question (string-last-line prompt))3 k2 x* N+ b: ?5 u9 |
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question) ?& W# e$ t2 |1 u3 k; u" T
- (match-string 1 question)))3 l5 I% t/ a# |/ O# O
- (doors (if except
5 E" V/ O& h' e$ z- b) d/ b - `((,except ,(intern next-room))
9 U5 h5 Y& a$ n+ G2 T - ("q" living-room)' k% Q5 k1 X) J' b7 `4 E
- ("Q" living-room): {0 {/ H' Y# W) z& ~+ O
- (t ,(intern repeat-room)))/ O) [. ~' L8 ~# q4 E! ~/ ]
- '((t living-room)))))
( X" l* Z1 ^ { - (def-room-raw (intern room-name) prompt doors)
( X8 N8 E8 j$ `1 E; ~6 R9 L - (def-room-raw (intern repeat-room) question doors)))
3 \% U8 M# F1 w: b5 v - & B7 }( e4 ]1 P
- (defun def-tutorial (&rest prompts)
/ u b7 j+ d# { - "批量生成教程房间。"+ ^1 U9 G8 j' O2 f, E; J
- (dolist (prompt prompts)
& I% K* u2 \" G- Y - (def-tutorial-room prompt)))
; J6 S9 L! a/ U - ( u3 k$ ^1 q' Q
- (def-tutorial
0 j9 U( `; C* m" Y# G9 H' i% o - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
. ^1 r6 O$ Z. Q- l/ h6 D3 m - 1. 教程
8 ?) Y& w; O/ b/ t [; W - 2. 入门(3x3)
2 V, W! ]/ z$ g4 k* x: E3 p' k" O - 3. 初级(5x5)
+ [( K. i9 \( ^3 h0 { - 4. 中级(7x7)
& e$ v* g; p' W5 P2 o$ b( x9 i - 5. 高级(9x9)
' {% p8 N% ?2 ^6 N4 m* P2 q6 R - 0. 关于作者; c6 {' f D2 A) k
- 请选择1-5开始新游戏:5 a4 O8 Z& Z2 e$ }
- 您现在正在游戏大厅里。& ^! H, H) f0 P: p; J
- 请输入“2”进入入门级房间"
. A# i. R: C j, ~ - " ①②③+ y( L; O. {" A D7 \7 d
- 1 ■ ; Q1 i8 S; C& i6 _
- 2■■■
/ x; \! G/ z) T" e - 3 ■ Y3 c. A& A" x5 m
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
8 V3 \7 P) G5 k; b! x0 F - 请输入“22”来关闭第2行第2列的窗户。". G! O7 \- d U7 E& b( b
- " ①②③
H/ c/ x# A% @4 B5 }( X - 1 % ~) X* V4 u$ v
- 2 0 v% O% |' A1 ]5 F9 Z. K! ]
- 3 - l" W, q1 u1 h/ X& u
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。! M3 m' [# J9 }7 u+ \0 E4 T
- 请输入“11”,试着开启左上角的窗户。"
) `7 r& p( _) i9 ?7 N* _% ] - " ①②③
3 R- |/ z! Q- c: o. A, K. q$ i y - 1■■
! z5 J; K. c8 N6 h - 2■ * A* ^. d, w2 _% | G# @# ?
- 3
6 i. K& } m! Z8 d4 R1 c1 {8 @, q - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
5 d5 P2 V+ i1 s7 [3 t/ V- @ - 请输入“13”开启右上角的窗户。"' ^; ?2 ?5 E2 z0 K! n- k; C
- " ①②③/ J' J) f% A) `! g) I3 V5 ]
- 1■ ■9 `# F/ F, Q; ~0 f" T
- 2■ ■
" Z8 a9 i- C5 g, ? - 3
% n; [- k$ \3 p - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
# ]& K2 }6 v; M8 {7 G7 ^% x0 j - 请输入“31”开启左下角的窗户。"
, N% t& m" Q/ D! W6 V: X$ o - " ①②③
: J% J3 ]) A6 _7 L& n: y - 1■ ■
! A$ C8 t. g6 H7 P/ x - 2 ■
0 \) L' V# W% w( u - 3■■
! S" e' d8 B; i. y - 此时,总共有5扇窗户被开启了。2 a& L7 T7 [* T' o) l# ~ a
- 请输入“33”开启右下角的窗户。"" I; L3 |+ P1 q: @
- " ①②③: ~. P, S }' \1 E" L" H: @- X
- 1■ ■
9 f/ e& ~5 a* `$ y6 A1 ]+ ^ - 2 6 e2 j+ h& t1 h- K% n5 U. v
- 3■ ■
2 H' V, }2 | s; z - 现在,只有四个角落的窗户被打开。% U, h% U( _' A- a. T. n. z! K
- 请输入“22”完成最后一击!"
" U: O- k% n* f8 z - " ①②③
- k, T( q" n. p! d/ _) t - 1■■■7 M9 E: Y& K) A3 V( s
- 2■■■
2 v( z6 J- V* _! g0 b* g5 d - 3■■■% X* W! X" E1 A; p& \3 X
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
3 A$ e: F0 P& D# F: M - 6 ]/ S' u- l' @) @6 W, T
- ;;; 棋盘& `* q' M1 N/ P
- (defconst *wechat-5x5-white-chess* 12288
# E% }6 t" y9 R - " ")8 m0 p; Z; l* x' I4 J
+ y, X$ b- i# W- (defconst *wechat-5x5-black-chess* 9632
& z% i; T/ D6 [8 E% }! U1 a! n - "■")3 M# z( C# z3 i, w1 {# N1 Y
$ t+ V9 ?; _- G8 l# { [! m- (defmacro with-board (&rest body)
# K# u; f5 r5 ~6 h$ `- d! p - `(with-temp-buffer
, m+ q, L/ p9 ~5 G2 S' u! Y- a& L - (unwind-protect
/ Z) s, b/ [7 m) N. W9 l. E - (progn
: s5 ~$ Q; ?, v" C; A - (if (session "board")& g9 K L0 i- P. x1 W
- (insert (session "board")))
8 I8 T6 u) W# f1 [: n. ?. ~: k, w - ,@body)! w( p7 H- Y% N7 t. }
- (session "board" (buffer-string)))))
/ n6 C' J; F+ C - - i' y1 J6 j) y0 F9 X
- (defun board-init (size)
3 K5 q- d* \" m" ]* K% m5 F4 b - (session "size" size)
3 N5 U% Q) \, \! `1 } r3 A; V3 I - (session "step" 0)
0 w1 J" k5 S" ]5 E - (erase-buffer)' F- d3 B$ n$ w# G$ K5 E
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
: W c, {% J1 p- z' W) Q% g - (dotimes (row size)8 ~. [: z; Q' b# V e6 z3 f
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))1 F3 h6 X4 V6 g+ [" o# |
- : d" q k1 R, u0 \
- (defun board-contains-p (y x)9 Z0 Q# ~+ J2 f
- (let ((size (session "size")))
0 Z4 O* ~8 z1 u - (and (<= 1 y) (<= y size)) X; w6 s& [' [7 `4 C7 \) ]: {
- (<= 1 x) (<= x size))))& |3 R: |/ H' f
- 9 z( U& C; v% \6 y: `" k
- (defun board-toggle (y x)
$ l3 T9 N% d9 ]+ A: @ - (when (board-contains-p y x)" S. m# R+ ~- b. Y! D& h
- (goto-line (1+ y)). i9 _3 j( a! p1 D7 Z" y) Z
- (beginning-of-line)
! |9 A4 I. V4 {; [& J8 ~1 H& ~! Z - (forward-char x)+ W( x# H1 t: V/ w$ s4 ?
- (insert (if (= *wechat-5x5-white-chess* (following-char))2 p; y' X+ w P5 ]
- *wechat-5x5-black-chess** z, e- x/ W0 G q* d
- *wechat-5x5-white-chess*))- Y) K9 M/ d) C* y G
- (delete-char 1))); x( V/ M9 o7 q& `
- ) {; E! t3 Z H9 O9 ^
- (defun board-put (y x)
" K, t a$ ]3 J/ a. C - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))3 Q8 w2 L: O; U) `! s
- (board-toggle (+ y (first dir))4 P9 B' W. [: }9 A
- (+ x (second dir)))))2 j1 F% R3 T" A) W3 b
6 [; a. a, a4 j) [) |- (defun game-over-p ()( u! B, O: ^) C6 M! t3 Q
- (beginning-of-buffer)
2 h' N/ a8 f5 u0 h5 E" M2 z - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
" r! u e8 V. @( D+ a* O
9 W3 X* i5 c9 s: X- (defun board-show ()
! ~" m$ C+ e% w* s2 E( ] - (with-board+ Q+ `' g7 [/ K
- (concat (buffer-string)) j* Q1 K+ D P% A
- (if (game-over-p)
8 @0 q. H" A$ U( R( _2 M - (format "共%d步,输入任意内容返回大厅" (session "step"))1 J6 Z/ U) u0 F, _9 O
- (format "第%d步" (1+ (session "step")))))))
; G/ f+ W0 [: K3 a. R @
4 s# ~3 s2 ~4 H* E3 m- (defun board-position-parse (cmd)& L8 W) U- J6 f# l/ R
- (if (= (length cmd) 2) O& U4 e4 [5 s$ K9 i& W0 @
- (list (string-to-int (substring cmd 0 1))
" t7 n; |+ y9 _$ r% H% n9 U - (string-to-int (substring cmd 1 2)))5 R. v1 I, l$ \0 T. L
- '(0 0)))
0 I0 h; c9 j9 R' f1 U3 [5 G8 e
! w2 k4 [1 y7 F8 W- ;;; 游戏房间: D4 t6 B# q. x2 ?. h
- (defun game-room-init (cmd)* q* v7 c7 |8 p" @
- (let* ((middle (string-to-int cmd))
* r% j2 t- W! ~ - (size (1- (* 2 middle)))). A' R: n3 `0 ^5 N# U) L9 P+ P
- (with-board5 o x! Z* t9 F; b9 M J9 |
- (board-init size)0 q6 T: M7 v; M6 g
- (board-put middle middle)))9 n" F2 @* l& \) N4 L$ Q
- 'game-room)
1 o( o3 s! X/ B9 P8 i; K$ I+ V8 i% d% ~
0 |' y: A. A5 a E0 A- (def-room game-room* R! n1 ^- S2 n0 Q
- #'board-show1 S6 k- E0 K7 {$ ^# ]% {
- (t (lambda (cmd)
/ S& y8 C# x+ _: r: s - (with-board( D. {. T ]" m+ p% B s! `2 @
- (if (game-over-p)
# L. X. w9 Z2 q6 M1 U - 'living-room8 { q9 ~! }+ E P; U2 c' ~, c
- (destructuring-bind (y x) (board-position-parse cmd)
! K; j! E9 C6 J0 K. h - (when (board-contains-p y x)
% X! a2 v7 D5 |. c - (board-toggle y x)
) z1 J3 U* K0 H) s) ~) K# Y - (session "step" (1+ (session "step"))))
4 B2 ?; u0 p# k4 `' z$ u - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|