wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。& f& ~6 R# |; f& z3 w+ h' D6 T+ h
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- % F, k+ D* a. e9 z( S5 z0 A
- <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;">;; 定义新的游戏地图# k, W! a/ F, Z" e$ B' N
- (def-map "/game/5x5.el" ; 对外开放的URL1 P# ]: [( e C1 h6 ~
- 'tutorial-room-0) ; 默认的入口
( V/ O, M, ]% ?! I8 {4 ` - / E9 E' ~, }$ D6 @) l, t
- ;;; 游戏大厅2 s7 T3 ?/ x% S; N/ p S
- (def-room living-room
$ G" O& p L8 c) b2 _! Z. ~3 z - ;; 进入该房间后的提示语. ]! R* _ n& A* Y; [
- "1. 教程
: Q1 o- v. B1 c - 2. 入门(3x3)6 i; @' P; C" b: U
- 3. 初级(5x5)& Z9 x" p5 U7 C! {- Q
- 4. 中级(7x7)
8 h, g0 N" _1 d - 5. 高级(9x9)
: Y' n1 g+ ?+ g3 c# I! U* l - 0. 关于作者. V c# S* B8 o6 A4 G8 U! g4 a
- 请选择1-5开始新游戏:"
, F1 E' D h, B' B - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
( i8 U$ u X0 X: \ - ("1" tutorial-room-0)
3 ]1 y2 F6 N1 } - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配7 O2 U7 h4 _! u; N
- ; 相应的返回也可以为函数,动态返回房间名5 C8 B$ h. q. O* b9 n
- (t living-room)) ; 如果条件为t,为永真% q2 X9 v8 V4 P/ F7 m- j* F
- 8 J4 j& n! _+ s
- ;;; 作者信息& ]) M' ]6 Q) p& i. ?3 T
- (def-room about-room! U6 i7 Y" E/ G2 `+ q
- "作者:redraiment
, n1 r* k" D+ j9 k$ u' Y1 i! X/ K: S$ Y - 微博:http://weibo.com/redraiment
: G* l. m- M6 ]- o& E" S - 有任何建议,欢迎在微博或微信上联系redraiment。5 I7 |. K8 x+ s8 q7 }4 ]4 ~; \& R
- 请输入任意数字返回游戏大厅。"
0 G! l; X; G n) [! F - (t living-room))7 g; N) `& Z; _3 ?/ Q) q4 \
4 ?+ t7 ^( t" T- ;;; 教程
8 W; n6 T" L: W) m; a - (defvar *wechat-5x5-tutorial-rooms* 0! Y3 Y8 l. }, h; ~3 k
- "The number of tutorial rooms")6 u! s- [! M, V J* i
; y4 d/ a0 n; b; _. r- ;;; 简化教程的定义; |. M" e0 H1 w; H# V. l* O0 H! I
- (defun string-last-line (content)5 Q+ ?) x% ^0 G" `, K* ]0 g
- "多行内容组成的字符串中的最后一行"$ L' B) r8 |: E) X- o
- (with-temp-buffer
( g$ l: V ?5 V+ s! S - (insert content), \+ R0 ?! D' t+ z" K- X! s- k
- (buffer-substring (line-beginning-position)
- _; `( c* g* q - (point-max))))' `% |( v/ n6 o9 g
- ) e6 X9 g7 A9 e/ I7 @& Q
- (defun def-tutorial-room (prompt)/ r) i5 ~% R# M: e
- "根据提示语自动生成教程房间。8 ^) e9 i( B7 {1 Y9 e2 |
- : M. [' O/ H* ~- z5 L8 o1 s/ `; I
- 1. 提取最后一行作为问题;6 n- o$ w( B' O, k8 Z4 A
- 2. 分析问题,获取期望用户输入的内容;) i! n5 @( u3 G$ P+ V
- 3. 定义教程房间和重复提问房间。"1 n* m, `& E* F
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))' m+ C; O8 ~1 N3 @ T/ t2 ?4 i5 w7 i
- (repeat-room (concat room-name "-repeat"))
7 m3 |; N4 O T. x - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))3 P( j: Y+ m7 |( D, X y
- (question (string-last-line prompt))/ C2 k) B, W5 N. k5 H
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question) ?5 U3 Q* U8 C7 h" h% `
- (match-string 1 question)))
0 M$ X+ M# k, J* n3 f5 Z, b - (doors (if except0 b$ o( ]1 `! P" U# w* {5 y
- `((,except ,(intern next-room))
S/ w( O' ]) }1 L3 M - ("q" living-room)* W- J# G9 }( h/ e7 i, D: D. i
- ("Q" living-room)# _( G9 N9 q* L: Z5 U
- (t ,(intern repeat-room)))
% {$ e9 K6 K$ G - '((t living-room)))))( E7 s+ E M. B% T; K- v+ ^" |: K
- (def-room-raw (intern room-name) prompt doors)2 J3 o$ J; \* g% i8 O( b. F! `/ I* `/ _
- (def-room-raw (intern repeat-room) question doors)))
% H" J8 m4 s3 f1 k& ~2 F
: Z4 j. H0 i+ b- D: h% t6 F- (defun def-tutorial (&rest prompts) Q9 ]* T3 i+ G# F0 n5 A
- "批量生成教程房间。"- O" b1 z6 f( D( ]; u2 [) v0 C I8 K
- (dolist (prompt prompts)1 e S3 m7 {; Q& M9 ~+ N- J
- (def-tutorial-room prompt)))9 i4 G9 Y0 Z; z$ Q1 c: r' T
- # ~8 n3 T e6 \# _' C7 p% u0 v
- (def-tutorial
' F: X4 t- j# x s3 | - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
5 \4 v& M. ~# ?& W - 1. 教程1 g6 L8 y1 ~7 J0 F9 B6 A' \7 d% q% b
- 2. 入门(3x3)
" H7 {" y# j- x! ?( g - 3. 初级(5x5)- y4 e% s4 y1 j6 C
- 4. 中级(7x7)
- h% a; `% R% `& O4 \) }' i" l - 5. 高级(9x9)* X( _ B- h( L. j& [
- 0. 关于作者
9 N/ `. H3 O9 x/ o1 W+ Q7 ^# B - 请选择1-5开始新游戏:. ?; R" V' G0 l% B( [ t
- 您现在正在游戏大厅里。
7 u4 k/ D6 e2 \) | C1 z - 请输入“2”进入入门级房间"
3 N5 h) \. u+ ~7 D - " ①②③
6 ]1 F$ [# Q3 U- X( s4 H" H3 w - 1 ■
4 M0 L# N q/ i - 2■■■
$ A% u# }( G4 \# ~( [ - 3 ■ ( i* Z7 ]6 ?0 t" Y1 }2 e% G
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!2 q9 H) h- l( ^
- 请输入“22”来关闭第2行第2列的窗户。"( t8 }5 n4 i; P( }3 l& |/ Q: E
- " ①②③
% \# ~2 S8 `2 w( \/ c - 1 2 ~+ y; Y! F+ b4 `# j% d4 U
- 2 $ I+ d; Q2 @5 N5 r( z
- 3
& \. L8 Q( d$ @) \ - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
+ |7 o8 y& [/ S, B# | - 请输入“11”,试着开启左上角的窗户。"8 W; u7 }6 L+ }
- " ①②③' F4 b3 b3 J9 q# ]2 u, ?
- 1■■ E" r c% ]* w$ m% `! P" D
- 2■ - z2 r$ p& M' ]5 L
- 3
: T, U2 l1 I) U - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。" P3 j p- s! h4 X$ w
- 请输入“13”开启右上角的窗户。"0 D* `" p ^7 J, ^3 v, h7 Y9 P
- " ①②③
3 t( f1 h7 l( t6 C - 1■ ■
$ A9 a% C9 \/ N- g* P - 2■ ■4 I$ q5 T# H2 @3 ^' f: } U
- 3 ( {, T; j; p7 ?0 N
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
( z' M' J& ^& O9 ` - 请输入“31”开启左下角的窗户。"
/ ?5 q! A! N/ d3 o6 ~/ e" f - " ①②③
: P% c5 D; {1 c - 1■ ■
0 K. i# I0 _3 R8 h1 \/ ~) C+ u - 2 ■6 |5 x- V! s$ o" T/ \$ q
- 3■■
; t. m+ {8 h L/ O0 F - 此时,总共有5扇窗户被开启了。
2 z, N( b2 e% v" T, c - 请输入“33”开启右下角的窗户。"
( W& X+ E+ o. H: ]8 b D - " ①②③9 ?+ \% A4 c$ a
- 1■ ■7 h/ u7 _# l0 S+ j, o
- 2
0 U1 U$ y( o/ q1 g - 3■ ■; O$ K& S- I# ?
- 现在,只有四个角落的窗户被打开。
1 T4 g# ?" k, e, S& V; x - 请输入“22”完成最后一击!"
3 c ?; Q' u6 B" s1 K; | - " ①②③. E/ c" W2 J8 A% ?/ W. m
- 1■■■
+ s' X) v. K9 X' q* K - 2■■■8 m# Z2 C) I- c) d2 V, R
- 3■■■ X- v# I$ ^, a. [" N' B& }
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")+ m$ `) Z8 I; b
& a I1 l( D! J2 h' n& B- e- ;;; 棋盘
( f* O. S9 C v6 _) D* h9 g - (defconst *wechat-5x5-white-chess* 122886 q9 v6 p6 @$ G; y3 t
- " ")
3 W9 V6 T& a' t6 a) W8 I- T2 H" T# l
! b4 \$ a! F6 l1 C1 W9 z- (defconst *wechat-5x5-black-chess* 96325 J: C3 a& p& u6 |5 p& r n
- "■")
6 f! S9 i' X5 B - 8 e) r' p2 c& G2 Q/ g. v9 L
- (defmacro with-board (&rest body)8 [) ?% z8 v, M# r
- `(with-temp-buffer; l T0 |! M. M) u, z
- (unwind-protect
; I9 ]5 x/ g! e - (progn
3 W# w$ H$ F+ p a$ e! j/ |! {, e - (if (session "board")
; H& E# \$ s- p) x, c. A! P9 d - (insert (session "board")))/ g* ]6 e5 F; T9 h. a
- ,@body)
4 L8 g2 T3 I- X' R8 x5 D3 ^/ p - (session "board" (buffer-string)))))
; Y: Z) _0 Q4 v8 M/ g- R - ! Z9 o/ r6 |0 n4 v0 U6 w1 M; @
- (defun board-init (size)- [* s: e4 L% Y! {. ^ S. H6 k
- (session "size" size)) I, x! r. H7 ~8 ^) R
- (session "step" 0)
4 t0 n2 N n9 D- m8 a2 i K6 m0 D( R - (erase-buffer)5 E9 y3 J" r- ?/ F! k+ j
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
( d5 X- U- |/ @0 f d6 D - (dotimes (row size) ]- t: n+ H6 o/ v# W1 O
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
0 K4 }' i: D! M8 b* K( ? - : Z. Z8 ~2 \) m! U) q
- (defun board-contains-p (y x)( S }4 p6 S# |+ q/ x& M- Q& A6 ?0 m! p
- (let ((size (session "size")))5 k. Z1 C/ ^! H! c1 V
- (and (<= 1 y) (<= y size)
+ Q( Z, r; W6 K$ [" T2 { - (<= 1 x) (<= x size))))
' N3 w5 K, Z+ r( X" s - ; |; Q9 [0 H" Y" l0 [
- (defun board-toggle (y x)/ U' d0 D0 ~1 _
- (when (board-contains-p y x)
# u9 P0 v* z' M9 P' y - (goto-line (1+ y))
5 k* F$ E2 i% e' [- o( x' i. i - (beginning-of-line)
6 Q ^/ b) m7 ?, _- v. w - (forward-char x)
& y% h+ R9 l0 ~; M8 b - (insert (if (= *wechat-5x5-white-chess* (following-char))
# n2 v2 R! o* {5 r7 T: F! M3 u. L1 D. b5 D - *wechat-5x5-black-chess*
$ A* T; u9 U, h# V& e3 r6 Q - *wechat-5x5-white-chess*))
* i! x5 @6 P3 f/ F! h4 g: U3 G ] - (delete-char 1)))- C7 j/ ?3 t6 z1 H
- i8 {/ v" F& a; b; \. D
- (defun board-put (y x)
. T+ {- O) G, \& g8 [9 {) ~" H; k - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
- |$ W* y0 _' G* L) R R! ~6 f2 H - (board-toggle (+ y (first dir))
. T/ r& P: h. O0 X - (+ x (second dir)))))0 T* ^. M+ g7 \0 Y; s }
! u- ?9 a1 ^9 F( u) g- (defun game-over-p ()
N+ Q5 M h8 L0 o6 w - (beginning-of-buffer)7 G3 _& p2 e2 `) H' X- m4 n2 J
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))& n. b* h% N5 B* p* X6 F: t8 W7 Q
- * v5 C9 P* j2 X. ]$ l1 \$ u
- (defun board-show ()) s8 d- q" X G, J! d
- (with-board+ c3 g: E+ J, C
- (concat (buffer-string)# R$ Z! c' x9 X# }" w i% }: I E1 t
- (if (game-over-p)) C: E+ K" [$ t. b5 p
- (format "共%d步,输入任意内容返回大厅" (session "step"))
+ L$ t2 L3 s4 ]3 M - (format "第%d步" (1+ (session "step")))))))
# W; i8 Z) X% K; J7 o+ k
+ }# j! ?& w# P# ~" l' }6 [1 d6 ^8 ?/ p- (defun board-position-parse (cmd)
4 h1 s( o' V6 Z- Z - (if (= (length cmd) 2)
( T* Q" A8 {8 s5 |) _* h+ [8 `+ m - (list (string-to-int (substring cmd 0 1))
1 m. d% E8 l8 q - (string-to-int (substring cmd 1 2)))+ p: B' N' ~+ x# M. `. S
- '(0 0)))# T% J& q1 ^5 b* c8 B$ O0 B5 l
- 8 |' `+ ~4 @ y. @' I' p
- ;;; 游戏房间4 ~+ Z( J$ T+ l2 _
- (defun game-room-init (cmd)7 C0 e6 B" w* P# R2 q' T
- (let* ((middle (string-to-int cmd))
2 o0 Q3 s1 p7 x+ P4 ^! M) v# R - (size (1- (* 2 middle))))
0 L9 I8 h# Y8 _0 Z - (with-board
! q# m2 O( V) e# x - (board-init size)" a5 E/ Y. n$ u2 {% S2 [" o
- (board-put middle middle))); C3 j1 O* e8 Z
- 'game-room)& M6 M3 `' w8 h$ J. c. k& `0 q
- " ^9 z( Q* F6 @& R
- (def-room game-room
) Z1 o% i# b1 p* [, X - #'board-show
d; C% b+ c4 W& P- w9 I - (t (lambda (cmd)
k" L, W; x9 t) D0 I( { - (with-board
' j/ b( y* d1 J" ^; D - (if (game-over-p)
~& U/ ]8 F' b5 l0 Q; S - 'living-room
- H2 D9 v" A5 g - (destructuring-bind (y x) (board-position-parse cmd). ]' V( H" B3 N: _" m
- (when (board-contains-p y x)
" M" K( J: m0 Y - (board-toggle y x)1 j, R l$ L5 Y2 k0 }
- (session "step" (1+ (session "step"))))
% K" q* U k6 W4 ]- s+ a - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|