wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
$ b j! c+ J3 q4 u* v" s* x借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- _# q" a" z+ o9 Z e' R
- <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;">;; 定义新的游戏地图
6 W' i1 C+ p! @$ G G! p - (def-map "/game/5x5.el" ; 对外开放的URL: p' D' m0 C( a: k5 W
- 'tutorial-room-0) ; 默认的入口
3 x: W% P2 }: q3 u, C - / `( x3 L J5 w) r1 {
- ;;; 游戏大厅
% p! q2 X$ y3 e4 S. _+ H - (def-room living-room
( U) K/ c. K' ~9 I) e - ;; 进入该房间后的提示语
& U8 L9 G0 N3 [3 u1 b( c0 L - "1. 教程 s! I7 a& @, T2 e0 P) R# `
- 2. 入门(3x3)
' k1 { m1 Q- q3 u/ ] - 3. 初级(5x5)3 x! K' h, X! N
- 4. 中级(7x7)8 F6 x+ Z# O' p: d& k) s
- 5. 高级(9x9)
4 T5 d0 d* m2 v - 0. 关于作者5 z: u; l2 H8 M4 i: ~. g# |
- 请选择1-5开始新游戏:"
% P( [# a+ P) z* I5 b) W, Z1 ` - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名, G0 W' j' Z# @2 n- z' Z
- ("1" tutorial-room-0)- }. s D, i6 l7 w8 i' T
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
9 j$ s5 K$ R# b% O# ~4 M - ; 相应的返回也可以为函数,动态返回房间名% q7 Z* M& w) ]! B, D/ W
- (t living-room)) ; 如果条件为t,为永真 Q. x7 {$ U' H! t; y: L
; G; b0 g+ a/ C% y2 g- I- ;;; 作者信息
# c+ f4 H& U0 l8 S `, ] - (def-room about-room; v6 x3 j+ A' ]8 k: \
- "作者:redraiment/ Q. I, w6 U: Y0 [# t( J1 ?
- 微博:http://weibo.com/redraiment4 g6 J& o+ z9 n; [$ a2 k
- 有任何建议,欢迎在微博或微信上联系redraiment。
' `* o% X+ v) E - 请输入任意数字返回游戏大厅。"* h6 D4 [3 V5 t+ T1 P
- (t living-room))
' N5 ^( ?" _+ W" n( j: `9 B9 a7 ? - ( C- t8 D8 Q+ Q4 ]) s* V
- ;;; 教程9 u4 D0 _& k3 c; y2 g* {& ^) u6 U1 F
- (defvar *wechat-5x5-tutorial-rooms* 0
% g% v$ O) E! r+ |" l3 A - "The number of tutorial rooms")
9 t2 w; V" _" |, U6 s9 O: {+ Z% F; S - & `2 m/ _4 D' J4 W
- ;;; 简化教程的定义
7 \& k) U" q# a& \" Y - (defun string-last-line (content)
# u+ M* B2 a: b: s, d - "多行内容组成的字符串中的最后一行"
* u" C! R C" O' R' _ - (with-temp-buffer
/ u, B8 N. l7 l5 U - (insert content)
8 D1 s' J+ O; C; o0 q - (buffer-substring (line-beginning-position)' y i1 F0 A/ e m; `5 p
- (point-max))))
; y) q. l F3 K: i( n
% g4 i) {* A o! q' J; R b- (defun def-tutorial-room (prompt)
1 a, ]2 V! s( C5 Q/ E - "根据提示语自动生成教程房间。
+ ]0 @" e) J5 H. {
6 S3 f3 {. `9 _6 n: E- 1. 提取最后一行作为问题;0 j4 q0 v S& N! P" ~& X
- 2. 分析问题,获取期望用户输入的内容;
) y8 y& l% V4 L: G6 t6 ^' q - 3. 定义教程房间和重复提问房间。"; {+ K2 G" L1 g8 z5 Q' o' q v$ m* _% V
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
g1 f+ l( E/ u; F - (repeat-room (concat room-name "-repeat"))
# B' H8 @3 N4 h: n! l$ B - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))( i3 x$ z! B# ]
- (question (string-last-line prompt))
* ~& h1 \: U* x* W8 w# t - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)) l; t2 w7 J) a1 e2 q
- (match-string 1 question)))
* S: Y9 c ~7 H2 P3 v7 b0 |* ~ - (doors (if except/ Q2 ^& \7 c' D% J' e d% r R& \
- `((,except ,(intern next-room))
/ ~# `) C2 m1 h - ("q" living-room)
: k) H( f ^1 l) J - ("Q" living-room)9 K7 \4 A/ r! v" T& w! J$ J
- (t ,(intern repeat-room)))
8 P3 f* E( T: y( z& M3 H/ ] - '((t living-room)))))& t. H9 I& w& p6 n4 q4 M
- (def-room-raw (intern room-name) prompt doors)" r. `) w3 I% S+ i( n
- (def-room-raw (intern repeat-room) question doors)))
8 b. [2 y+ _8 Q9 L' N7 ~' i
7 o7 W% b7 h$ R+ h( r% }; A- (defun def-tutorial (&rest prompts). q& L4 o! K5 P/ c% ~
- "批量生成教程房间。"+ V6 Q7 l) I" [" t! ]4 W
- (dolist (prompt prompts)" N9 o( S, T6 T$ F- q* N
- (def-tutorial-room prompt)))
2 T: Q9 N8 q5 e/ m$ F - z- [: i5 h: }1 l
- (def-tutorial8 X8 Q7 x- r7 J8 P) T5 S
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。+ y0 H+ [& |# g7 S0 H
- 1. 教程! P5 X* N1 b' ], |3 d" _
- 2. 入门(3x3)+ W4 d8 C' V4 G: A* M
- 3. 初级(5x5)6 t5 @7 h) l* C
- 4. 中级(7x7)
5 J D+ r0 j U& g" t - 5. 高级(9x9)3 b0 f* S, @+ z
- 0. 关于作者
! Y9 {6 m" x9 k0 m; q, j! g3 R% t - 请选择1-5开始新游戏:; V6 x( e& b& q5 Y! K" I# ^
- 您现在正在游戏大厅里。! f5 l5 ?9 l0 E9 a/ l: x4 ^
- 请输入“2”进入入门级房间"( j4 V1 x w2 `' z- I
- " ①②③8 E+ Y) n5 f, R' G' p* M6 w
- 1 ■ R* H6 T2 C% K2 T) l I. ?" W
- 2■■■
' n/ N6 _/ V }) o* @7 i1 r - 3 ■ r/ }* r8 D1 H, P5 s! r
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
) l$ K$ c' k0 k# E \2 M - 请输入“22”来关闭第2行第2列的窗户。"( R& a3 {$ G) {+ z) `) B( W. }
- " ①②③
3 y. ~* |2 q5 ]7 B( @/ ^! b0 g - 1 ) L; E( G- M4 C% m( o0 G
- 2 ( x. A0 Q1 E8 n1 T) x4 z( A
- 3
$ L/ C) B" w' Q4 p+ }2 R - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。' e/ R$ k6 f4 t- z
- 请输入“11”,试着开启左上角的窗户。"
, e0 o9 x0 s* x2 X# c9 k - " ①②③
( z* d' U. X2 Q2 n - 1■■
8 E& K" b3 J% _. j - 2■
. ^' O1 P9 e, k4 O. x" M - 3 f4 V( l! m. `+ L; b }9 X
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。8 X' ^ F6 H8 P" M8 h& T0 ]1 q: y" s
- 请输入“13”开启右上角的窗户。"1 b- J$ z5 ]7 S7 S3 F
- " ①②③: B: u7 W8 q/ K
- 1■ ■
7 s- P/ d) j! ^0 S) \! T - 2■ ■
, y. l2 F6 A2 e5 u' b. e% i - 3 0 S* Z# p$ b9 Y0 ?( z( ~# U2 z
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。4 Y3 A! D* W) D# ?; ~
- 请输入“31”开启左下角的窗户。"
$ C1 [6 b- ?9 {8 x1 e7 I - " ①②③* f3 u+ G# k% q# r) U
- 1■ ■. W) `. X$ C, P" p. N$ g1 N6 [
- 2 ■- X( [) n, t, f0 Y
- 3■■ 8 T) o R) v0 e6 A
- 此时,总共有5扇窗户被开启了。; g$ G, y3 D8 _0 `( K
- 请输入“33”开启右下角的窗户。"
) _/ e# ` L* ~8 [, @1 e8 U# n - " ①②③0 q9 s6 P8 R3 h& B" y
- 1■ ■- q7 v6 S) Q2 u: D1 D
- 2 . o; T/ x9 U1 f% g. S1 T
- 3■ ■9 j: q2 r1 G7 g' Y
- 现在,只有四个角落的窗户被打开。
2 r" I; Y, n' u+ a- B - 请输入“22”完成最后一击!"3 q9 P# y4 p7 h- D6 ?# A
- " ①②③! _- d0 V$ p' ^) e# k. @
- 1■■■. M3 Q' g7 v% ?4 Q! i3 [
- 2■■■4 g' t h% m4 ~' Z) l
- 3■■■
4 b" L9 ]8 [$ W5 [ - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
2 l& n! E1 V/ ~0 l
% B! y3 \3 i3 y; V6 R4 W7 F$ Z- ;;; 棋盘+ @* `1 e1 j a# h
- (defconst *wechat-5x5-white-chess* 122885 M! U+ [& R! N# j
- " ")8 u/ A0 ?6 C! ~7 N
- + B( t* i. r, R8 A- J6 u
- (defconst *wechat-5x5-black-chess* 9632' H- O- v' S q$ ^1 }* X+ X
- "■")- [. q( e" B: D. h8 j. i
- + ~- y6 G: l' K8 V
- (defmacro with-board (&rest body)
& t4 j: j" ~7 | - `(with-temp-buffer7 b+ m- i6 M% m: p- N
- (unwind-protect
9 u- u* ~" ^8 p2 j% ^7 @ - (progn# @) W2 O, m2 E7 b) P; J$ P
- (if (session "board")( \8 i5 Q5 t* J8 C) D, Q3 ]
- (insert (session "board")))
- r3 S4 `# h* V- G* \2 v - ,@body)
' |$ |2 E: d/ q% R1 c3 g - (session "board" (buffer-string)))))# `0 D+ m4 I: |
- 6 ^- X. i* m% H( i C. o
- (defun board-init (size)+ ]7 ^0 Z# w) M, S$ U# q
- (session "size" size)) Q5 s2 w3 S8 i) |# s: Z
- (session "step" 0)
8 F/ X0 l* q( B* a - (erase-buffer)
- ^" z1 D/ F# K1 v - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨")); N9 ?* o# K. U- v; D0 S
- (dotimes (row size)$ M" o, h( |2 t! Z% V8 E
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*))))) B: L2 m( I+ s: `! r. m& m* K
% H; n! z6 g" m* Q' m8 _* ?- (defun board-contains-p (y x)& g$ E3 s$ N6 h/ ]
- (let ((size (session "size")))
- |4 G" k0 m w# F: n8 x - (and (<= 1 y) (<= y size)
: i6 E- m4 q3 [1 ~# T - (<= 1 x) (<= x size)))) [6 x% f# a& E* \4 }
9 ~6 e/ n3 z2 L4 v0 R- (defun board-toggle (y x), i4 w* q! N1 U3 Y0 E8 J9 N
- (when (board-contains-p y x)
) e6 L; `5 i. c7 j - (goto-line (1+ y))( T5 P, d% N- n t& x3 h+ ?; A1 b
- (beginning-of-line)
/ C4 i8 r, S/ m2 |5 k1 Z - (forward-char x)
, V6 q- G+ y1 I4 C+ n - (insert (if (= *wechat-5x5-white-chess* (following-char))
$ R: K3 j7 X6 [3 s7 ~5 N& Y - *wechat-5x5-black-chess*
6 G: D4 Z& ?% S. d! t( Z+ N$ r0 F - *wechat-5x5-white-chess*))2 `! G: j( e7 Q- x) _
- (delete-char 1)))
: {0 f+ C- ?- q- B - 5 e. B `9 K, F- x
- (defun board-put (y x)1 G0 V0 r* I) }0 E. Y
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
! b, s3 F* e; }$ P - (board-toggle (+ y (first dir))5 v* o6 v" X) N' f- M! q
- (+ x (second dir)))))
4 R6 W( u. l, M
' I+ U# C7 \3 {) x! B8 B- P- ~: C) }5 L- (defun game-over-p ()! y3 D" J; K- l- ~+ g4 h; |
- (beginning-of-buffer)
! E$ P+ M# J1 O - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))' [6 U5 Z1 b9 \4 \2 ]
- ) a2 A' U0 ~" ?/ Q
- (defun board-show ()2 G, ], l; E4 K" s
- (with-board1 f3 q4 v" I9 ~/ |& B
- (concat (buffer-string)
1 T3 {8 F- x' [9 }4 C7 A% e - (if (game-over-p)% R! ]- X$ y0 l/ a9 g9 C, G3 x! J
- (format "共%d步,输入任意内容返回大厅" (session "step"))8 C, C6 r- ?) q/ f5 Y! z
- (format "第%d步" (1+ (session "step")))))))
% r/ I! s& ~' j" x9 | H; D - " b2 n5 _0 E4 I4 s0 _' Q3 u* F
- (defun board-position-parse (cmd)' K# h) L' ?2 L3 f
- (if (= (length cmd) 2) v4 _% Q5 J6 _
- (list (string-to-int (substring cmd 0 1))
9 h- U6 x) N8 Q0 |6 q - (string-to-int (substring cmd 1 2)))+ R! v2 c4 R- E9 v0 O% z# s v2 }
- '(0 0)))1 [3 X" g9 ]8 N' a# {0 R) f
- 5 p6 r0 N7 |$ }% ?
- ;;; 游戏房间
2 @. t( i9 F% _ Q - (defun game-room-init (cmd)" [$ V" Y- M0 Y z
- (let* ((middle (string-to-int cmd))
1 x$ D/ e9 _ M& i+ l - (size (1- (* 2 middle))))) d$ B- b* W ?
- (with-board
7 x: }5 G* e$ l1 q - (board-init size)+ }; R% l4 l- M) Y
- (board-put middle middle)))- |2 B# W5 D& m& m* z
- 'game-room): A' T. Y) v% V: H0 _3 Q
- " ~" S) n. s3 }$ g6 Z
- (def-room game-room1 h+ i1 M( w' C5 V2 i' d
- #'board-show
9 ~8 T5 X# D5 ~# { - (t (lambda (cmd)2 K N: I9 E. Y- G& B
- (with-board
. \. Q0 b' |5 A4 Y6 _( g9 S' Q - (if (game-over-p)
( |* B" Y/ G0 [! c9 D" A - 'living-room+ M: q& m! r% i7 A
- (destructuring-bind (y x) (board-position-parse cmd)- D& J+ p( |: s7 O
- (when (board-contains-p y x)7 h) u7 @8 y, x
- (board-toggle y x)
6 ~ ?; H9 @( \% K - (session "step" (1+ (session "step")))); h# ]8 h- n h' d& A2 f. r3 T. t
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|