wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。+ {( {& W' B2 ~
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- 4 ]7 Q7 V9 t3 O# ^/ p" C
- <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;">;; 定义新的游戏地图
3 S o; y h" p" ^ - (def-map "/game/5x5.el" ; 对外开放的URL
+ {7 ?5 e& ^# [6 X - 'tutorial-room-0) ; 默认的入口
. P. O8 i" }+ W0 c& J1 ]" [
) U$ t3 S+ `( Y& a0 r8 N- ;;; 游戏大厅
' C" l1 @" C0 H! c$ f - (def-room living-room
) D" m' T: W, f/ Z& j( p' q/ S - ;; 进入该房间后的提示语
# b" v; h. e& Y# y2 c8 c - "1. 教程
+ l; e* q! ]4 Z3 G) t9 R - 2. 入门(3x3)1 J! z$ B' ~% O) \1 k3 w7 D
- 3. 初级(5x5)
; e* C8 I: @1 t0 `% ~$ C4 T- I - 4. 中级(7x7)% n2 A" N, t1 n2 @/ L5 {/ ^* h) [
- 5. 高级(9x9)
' u& Z+ ^% T' b - 0. 关于作者# L! ^- `& v `, o. k
- 请选择1-5开始新游戏:"
! j4 B- l. h; X, u" i5 J' S! V - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
4 Y& H5 W, l+ S/ F - ("1" tutorial-room-0)
1 x& K8 E/ e% z+ L2 Y! C - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
9 H6 u9 _7 C, G! N7 { - ; 相应的返回也可以为函数,动态返回房间名; B: B$ q: K* f4 c, M
- (t living-room)) ; 如果条件为t,为永真 B, L$ \, X8 b5 U+ I
- ) y/ [. G1 Y& S* x, C
- ;;; 作者信息
: [; B9 N- v F' q! A+ {1 P: y - (def-room about-room3 O1 v4 s$ m5 F! T; k8 _! i
- "作者:redraiment
0 z5 q1 m, Z, I! d9 M1 T X - 微博:http://weibo.com/redraiment/ r! I; s! x( p7 W5 o: r
- 有任何建议,欢迎在微博或微信上联系redraiment。. D' o, o- {3 `9 O4 o3 }. E
- 请输入任意数字返回游戏大厅。"9 `) J( n6 u4 K
- (t living-room))
' M$ D3 Z! F# j g/ m, C- M
$ Z/ }$ ?( j8 u7 V" s( Y- ;;; 教程
& C# _$ L; J3 G9 x8 [: U) U- P! W - (defvar *wechat-5x5-tutorial-rooms* 0+ {; C7 h# D {) K/ N; e% _. r6 W
- "The number of tutorial rooms")0 s+ u# T6 ^) _
. m: t5 B6 ~% U% V( ]; Z- ;;; 简化教程的定义6 e# Y. }; s6 C
- (defun string-last-line (content)
; h) ^ E9 X/ R/ i3 d' E) T - "多行内容组成的字符串中的最后一行"' c- i+ I# v/ A
- (with-temp-buffer
1 Z3 g) `& ]* R' }' m) g - (insert content)
; F) B3 M! d8 Q& i" D - (buffer-substring (line-beginning-position)" Y0 q; Z& K1 h% [# j1 L! _
- (point-max))))
+ O" v/ b* |! ? - 1 Y7 m+ @3 r$ ~( [
- (defun def-tutorial-room (prompt)7 D; o/ |6 K7 A1 N# k
- "根据提示语自动生成教程房间。
7 M+ q, K6 U# K1 O* ^- \
8 Q! @5 y5 x6 O/ X: n8 ^- 1. 提取最后一行作为问题;
6 J8 |- l# n2 o( Y. g, w$ o - 2. 分析问题,获取期望用户输入的内容;
! j3 @# ^7 p" l6 q6 v6 [ - 3. 定义教程房间和重复提问房间。"
- Z" J; `7 a' w# ~' P# n1 _ - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
- S8 R4 ?* M! H- b7 N - (repeat-room (concat room-name "-repeat"))
3 C; e* i8 U8 V& |/ a, m' N - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
$ }4 O5 r* z" p0 _; _2 n0 r2 d+ i - (question (string-last-line prompt))
$ S- W, ^# Z9 N7 P - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
) v2 c& u) ?0 k5 [, _( n! ^ - (match-string 1 question)))
' e; D$ V8 [4 O2 l# c - (doors (if except: S( m; I- U7 l, W" M
- `((,except ,(intern next-room))
' N" G$ C7 ^. b; K0 A$ \ - ("q" living-room)
# x; {1 n. [" G ?4 q5 @/ T6 Z3 z( w - ("Q" living-room). i. s1 p. T! V, W, C! r( ^9 K
- (t ,(intern repeat-room)))
+ m( i6 e6 @4 I* j. Z/ n - '((t living-room)))))
& s# z. {3 v7 M, l - (def-room-raw (intern room-name) prompt doors)
& T! T) Y% r0 C5 A* O! G - (def-room-raw (intern repeat-room) question doors)))
# E% M$ C) D5 v% F - ' q) }8 j* Q' E5 Q- h, C6 ~# t
- (defun def-tutorial (&rest prompts)
8 o& |) H5 M. ? - "批量生成教程房间。"7 U9 N& S2 C N9 b+ O
- (dolist (prompt prompts)
2 j% p, p) e: h4 u" ^+ o - (def-tutorial-room prompt)))7 N) ?: `" T! i# P! O; h
- 3 X2 I/ [8 P! Q) t* e& j0 S
- (def-tutorial& K* S j; W$ p7 }5 G
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。1 p+ w6 y; a9 |& a& j9 N2 s% q& _
- 1. 教程
+ d) {( _- D5 _( ~4 L - 2. 入门(3x3)" }! W' ~% b- f$ v' D. e8 F, N
- 3. 初级(5x5)- E! n9 x8 J: S) }
- 4. 中级(7x7)" Q4 s- Y% l' N- C5 V
- 5. 高级(9x9)) t% X1 ?9 c6 }+ S* E+ I( S+ k- x/ d
- 0. 关于作者
# H& a, V" e+ V) ?, Q" V4 x - 请选择1-5开始新游戏:4 O8 T0 b; x9 W/ f: ?0 Y% m- \ d
- 您现在正在游戏大厅里。
% k+ y( }; A3 U: g& U2 y - 请输入“2”进入入门级房间"- |7 b# a& R" o
- " ①②③. |& W" ~7 n2 O& c5 K# e' ~7 u
- 1 ■ $ P" r- i% h0 K" c9 E
- 2■■■( X: l! b, j, s
- 3 ■ ) b c L7 u. k$ t+ p+ @" G- K
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!( Y7 T" G/ W# Y- ], w) \
- 请输入“22”来关闭第2行第2列的窗户。"0 t* S2 n6 I9 _. A" M9 j1 L' H
- " ①②③
0 p6 \, n' Y$ K - 1
1 D8 P6 F( I( {6 x - 2 4 Y; a8 @) S" W6 k% e
- 3
* K6 _' K* H$ j. H - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
9 g; {' ?8 r: _; } - 请输入“11”,试着开启左上角的窗户。"8 a1 P# h) b# j( J: b# Q5 k
- " ①②③( R* H8 i) B% b. Y6 @1 L1 z
- 1■■
" ~" W% Y9 x! S+ ]% z8 I7 J1 w - 2■
* ?& ` q* [' `. M# [ - 3 ) J; b7 _: G- g' w7 }
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
) T" ?4 w; d9 A$ w& Q- n - 请输入“13”开启右上角的窗户。"! Z# S7 S7 f/ m/ J
- " ①②③; F! f w7 t: b8 P6 a
- 1■ ■ s* h. Z* |9 _" g5 g
- 2■ ■
9 c7 W% x6 v. Z. W - 3 ! x! S, R, d( w1 D" V( [ ]
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。5 m; s$ f+ f- z: C+ D+ ]
- 请输入“31”开启左下角的窗户。"
, ^" q" Z* {7 M" p X - " ①②③1 X! |! U' X/ Q1 H8 `
- 1■ ■+ r* H" w4 Z+ t7 d: m# @* {
- 2 ■* N% I) t& L- G& |8 U3 C7 g; k
- 3■■ 9 Q; a$ t( X7 f$ z- o' A3 z
- 此时,总共有5扇窗户被开启了。8 a9 ?9 X2 v! R/ Z2 J$ ~, U M1 V
- 请输入“33”开启右下角的窗户。"5 \ E" u0 n( b* ^8 z! F5 g
- " ①②③
' l; Y5 ]6 H; t0 m - 1■ ■) L+ j# ]2 J) B2 i/ f0 u
- 2 % X/ f% i( `; R, ]- c
- 3■ ■
7 s# V) ]2 M; g5 l# ^$ U( P$ @ - 现在,只有四个角落的窗户被打开。
7 b: h+ a4 ?# w- ^8 s6 G( b - 请输入“22”完成最后一击!"
" {. d8 h g0 ]( `/ ]& Q3 M - " ①②③
, `; D- o3 u) r; V k# F - 1■■■
+ J1 n1 ~; O' u5 d - 2■■■, Z" ~0 R+ m2 j2 V- b: a$ r
- 3■■■
6 V" n7 D( n& R' h4 e" C/ \ - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
; O* u6 H4 n) p+ G' ?* Q
) q- N7 j9 h, g; k. r8 n& J- ;;; 棋盘 p/ |% m' T) Y9 X# T5 i
- (defconst *wechat-5x5-white-chess* 12288
4 F: }. r, N# G V' q7 K2 b3 v - " ")
8 Z( U. i, o9 w- {/ y - + O6 F1 h' R# c
- (defconst *wechat-5x5-black-chess* 96324 V5 ]" Q, I1 x7 H% }
- "■")+ |- Q/ W. v/ @ k: [
/ e l: f; K) e- ]- (defmacro with-board (&rest body)5 @; k& _9 L- g& o
- `(with-temp-buffer/ N" \ f' l& o" a! k& L
- (unwind-protect# c- Z' H: z% M$ v) A! ~
- (progn* P9 W& ]4 M: U
- (if (session "board")
6 y# K7 D/ @1 S, G* r+ x - (insert (session "board")))* T) K- y2 d6 D( [3 c5 w$ @- k2 j
- ,@body)6 ^4 @9 Q( V" U3 }) z6 ^: k# m# y
- (session "board" (buffer-string))))): K* R& D$ z0 d- b. V+ F
1 \2 X, G' U: {( W* F0 {+ s- (defun board-init (size)
; L) M% z( l- o* y - (session "size" size)
4 ]; C5 P/ F, q, g" H/ d. u - (session "step" 0). [9 n+ K2 z0 V( O: I9 ?# i7 d( s0 b
- (erase-buffer)& c9 c5 g) ]) d+ ?
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
* P) K( S" B2 O0 Q$ h( x0 b - (dotimes (row size): r: `6 K. a8 H: X( H
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))4 X# k' b$ w9 c5 u4 D
- 3 l6 B8 Q a' d' i! G
- (defun board-contains-p (y x)
8 M/ x+ D% }. o/ g) T# a+ A - (let ((size (session "size")))
6 m/ b0 j; c: @: A3 m( F - (and (<= 1 y) (<= y size)( X: j5 R$ t/ F0 Y. U& j& C8 G) f. V
- (<= 1 x) (<= x size))))
2 }4 k2 v, H/ \/ G2 |. H5 O6 V
/ U! ~6 k1 j* N2 S. {- (defun board-toggle (y x)
8 F8 c2 G0 S! U0 u - (when (board-contains-p y x)+ z3 ~. B& I5 B. D% `! m
- (goto-line (1+ y))
% ^* K1 e$ Y9 F2 J, s - (beginning-of-line). @" r# m. A) z; U- O9 y( ~
- (forward-char x)( ~2 S0 ?& U$ A) {! `8 w
- (insert (if (= *wechat-5x5-white-chess* (following-char))
! `3 H! E r5 \# y5 U - *wechat-5x5-black-chess*0 h8 Q7 w8 F8 Y# g: C
- *wechat-5x5-white-chess*))
4 ?! e# ~+ Y& a. x0 M: n: s% I - (delete-char 1)))
- U6 i5 g1 a- n2 @6 _' U
% E) A& p3 d1 b4 n8 }0 }$ D. T1 |- V5 [- (defun board-put (y x)
+ ]* o; |$ }( s( ^ - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
# A# J! y6 A& ^- o( q3 l - (board-toggle (+ y (first dir))0 T, v8 w$ H1 L% j. G1 \) |3 }& ?; o
- (+ x (second dir))))). E) n$ z9 n3 U" J% R+ Z2 l! N
) \/ c( d& M* E! ~! C* _: t- (defun game-over-p ()% O3 B1 s7 ~$ e
- (beginning-of-buffer)# i; {! {' f0 D) v3 n' o# W; W, K
- (not (search-forward (string *wechat-5x5-white-chess*) nil t))), Q5 s% k* d+ i# E
- # h f- E9 M$ D3 e4 U9 ]
- (defun board-show () t2 H# A4 K' U& j5 ~: o2 Z& b
- (with-board9 z: [+ C }: Y! x
- (concat (buffer-string)
/ a$ N, ~1 B! k0 V - (if (game-over-p)
9 |2 s6 z l- D% h/ f - (format "共%d步,输入任意内容返回大厅" (session "step")), G d5 c8 q8 F1 b, ]
- (format "第%d步" (1+ (session "step")))))))
8 [+ G" D; F& u) n4 n, _9 X
: q' s3 N' w# O( a- (defun board-position-parse (cmd)
) D- t* q. v' X" R4 L - (if (= (length cmd) 2)
1 ?$ U: K7 j9 C5 j! [9 _, E - (list (string-to-int (substring cmd 0 1))
, D0 E h' w" V - (string-to-int (substring cmd 1 2)))2 |2 s8 W ?' u
- '(0 0)))
% X& W2 l; `6 _2 U& ~. m3 C - . o4 ^4 b2 P T1 q1 D- E+ |
- ;;; 游戏房间
7 l( {0 N- q3 s - (defun game-room-init (cmd)
5 F! G! M9 `" ? - (let* ((middle (string-to-int cmd))
' o9 h) h: ]/ m4 f) ` - (size (1- (* 2 middle))))& B3 i4 h1 l( J
- (with-board# W" N4 H4 y6 c9 w. G) @
- (board-init size)
, D! W9 P9 z2 W/ G& u5 o U - (board-put middle middle)))2 i* g0 i Z6 x
- 'game-room)9 g$ L5 O# \2 |1 Q9 _' ?
- + m0 s+ {& p1 d+ V6 _' z, s' o
- (def-room game-room
3 X4 _; f. s4 Z, w8 W - #'board-show
) Z2 ~+ ]1 E) g# a3 D* I3 D1 k - (t (lambda (cmd)7 D# l+ G1 K, P) Z# A, {5 W2 Y; \
- (with-board
: j" E: i, S2 T$ |, Y8 b - (if (game-over-p)6 y) |" I4 d' q4 u
- 'living-room# [2 a7 {3 P$ K9 U+ c, c
- (destructuring-bind (y x) (board-position-parse cmd)
* {* }/ B& x- H, I - (when (board-contains-p y x)
7 b! r C5 P7 G7 q! K# b8 q - (board-toggle y x)
$ Y0 ~ R/ {5 n/ T M4 v# K - (session "step" (1+ (session "step"))))* C, Z/ N$ b' X4 t: h7 q
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|