wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
5 Y% q6 T& x6 b- H# i6 K, F4 I- S4 z借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
- s* O: E8 p8 u- <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;">;; 定义新的游戏地图
# X0 l; F. L. b; H4 |9 V0 E' D - (def-map "/game/5x5.el" ; 对外开放的URL' c& }+ Z. ?( E: f; ^" u
- 'tutorial-room-0) ; 默认的入口
0 O" M7 ^3 `- ], X9 V7 V4 q* V
" ]$ v& j/ E0 {1 M" R* M: `- Y- ;;; 游戏大厅- j" J7 k9 P9 T* F. F. X2 C
- (def-room living-room
8 _) _3 @, o8 @! ? - ;; 进入该房间后的提示语
1 \/ `4 r, C8 s - "1. 教程9 z9 Z7 U* r t& n+ L+ b
- 2. 入门(3x3)
9 `% B/ L) V3 r' Y - 3. 初级(5x5)
+ h" B" X8 O$ c, C8 h% x - 4. 中级(7x7)) V J8 i; v! o9 e
- 5. 高级(9x9)
. [- Z% o8 v0 H3 i! V5 p n - 0. 关于作者: G/ s; }7 p% b. O* X
- 请选择1-5开始新游戏:"
% v) f3 ~) A! A9 X# c( @* M; V - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
) U/ E2 b* W+ C& e* _: _3 q: n - ("1" tutorial-room-0)/ g2 \* \" `; f' z) n
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
+ l: L. F. U) Q, P - ; 相应的返回也可以为函数,动态返回房间名6 J, V" E3 i* `) \2 U$ c
- (t living-room)) ; 如果条件为t,为永真
. j, N8 G [1 y, h - ; `; X A: i. o& m$ y7 z
- ;;; 作者信息
7 a' s, ]' P) M* n$ j+ I( B0 [ - (def-room about-room
" D+ M6 m# |* _$ O - "作者:redraiment/ _, n. O u+ [4 L( E2 M3 n
- 微博:http://weibo.com/redraiment
5 z8 y9 t# Y% O# N5 g- b2 J - 有任何建议,欢迎在微博或微信上联系redraiment。
5 p. b8 i5 X7 b$ T" z8 A - 请输入任意数字返回游戏大厅。"; p- D5 N, q. ]' z: [4 D2 ^( s. ?9 g
- (t living-room))
2 ^7 ~& y) `3 m0 u
8 w4 f8 H( h' K9 D- ;;; 教程1 t1 [, g# J; W8 q e
- (defvar *wechat-5x5-tutorial-rooms* 0
8 B u4 ^) r. J5 r7 u" e/ X% s3 [ - "The number of tutorial rooms")' Z8 [: N% u, m) t9 \6 G. X& N
- 3 c" N" p1 s6 F4 o+ N6 B
- ;;; 简化教程的定义4 T& h9 O. X7 E+ p0 M8 P, [
- (defun string-last-line (content)
0 \' |- R( ~" S: T" _ - "多行内容组成的字符串中的最后一行"* d3 w. M: _9 G. _- A3 R
- (with-temp-buffer' {% A! R% u% I m4 \2 O
- (insert content)" B8 p: k: r0 z$ A7 B0 F- M8 v: G8 ?
- (buffer-substring (line-beginning-position)
8 V: E y1 v5 X+ {0 v/ d - (point-max))))* V M+ x0 g! @. V
# [5 G( f, s; z* m( P! W E5 V- (defun def-tutorial-room (prompt)
8 T6 B( z3 P- Z' }) H1 b5 E - "根据提示语自动生成教程房间。/ x/ v; j; v% v. H& R6 E% a
1 ^$ d* B2 o) }; B: g- 1. 提取最后一行作为问题;' G& Z# C" K. ?* t# R. V9 z8 w, i
- 2. 分析问题,获取期望用户输入的内容;
$ F% n5 V/ X, o: A - 3. 定义教程房间和重复提问房间。"3 P. M6 a z/ a1 U, v [( e
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
, S$ W- t4 C$ H: D( ]( ?1 z: v - (repeat-room (concat room-name "-repeat"))* L; z0 O7 `5 |0 d% ~% ?3 I
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
( n. J, z7 V8 H! o' k% m - (question (string-last-line prompt))
9 l1 R0 d1 {, N w. ^ - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
# V1 U `; I( T' J5 I - (match-string 1 question)))
+ U5 t" E/ K* A - (doors (if except
w4 l3 W4 J0 e - `((,except ,(intern next-room))! Q- c! B6 `- t
- ("q" living-room)) @0 U$ Q6 K3 Q( _2 q6 W L
- ("Q" living-room)
% S6 P. p, i* Q - (t ,(intern repeat-room)))5 ?& o$ g( \" R- L8 d: [
- '((t living-room))))): m8 b# D5 B3 _" T3 Y5 t. n: O
- (def-room-raw (intern room-name) prompt doors)* M4 D+ N# Q+ l3 H' U, C
- (def-room-raw (intern repeat-room) question doors)))4 } B8 k; z m; i
- ' a1 [! s5 l. h4 D
- (defun def-tutorial (&rest prompts)$ a2 B b7 G! M. G- E/ {. j) r
- "批量生成教程房间。"4 q" B0 I( F }
- (dolist (prompt prompts)
, S" f- U6 c; s$ Z - (def-tutorial-room prompt)))
5 f( O. W& N/ l6 b, v e: D
/ n+ U' u, w1 ^3 r6 P2 I- (def-tutorial+ V7 m/ L& V& `! [
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。7 k% o5 |3 ~4 C: t. F0 `/ H) V
- 1. 教程
) b- ~: l! y) n4 a$ D8 |6 h - 2. 入门(3x3)
/ F7 M9 h9 i4 m' X) S - 3. 初级(5x5)8 Z% S9 C/ m9 c( t' N T2 T
- 4. 中级(7x7)8 U1 D9 _/ K" l, T3 Y
- 5. 高级(9x9)
; O1 E+ c$ U( {5 m1 E3 o: r8 u - 0. 关于作者" l# i( o& z, T5 H: ^
- 请选择1-5开始新游戏:0 [: k- v' Z& {) _
- 您现在正在游戏大厅里。" O* V2 z8 `% Y* Q' {) u
- 请输入“2”进入入门级房间"
9 }4 K" Y' K* ~( v6 } - " ①②③
7 @7 j+ [! l [8 l% X - 1 ■
& ?# B8 H* U( X/ [/ H - 2■■■
+ O2 P5 A9 J$ H5 }& M$ v9 C* |8 X - 3 ■
( R2 w& e% C5 d/ y - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
5 v% W0 y3 b0 M; ^ - 请输入“22”来关闭第2行第2列的窗户。"0 L4 D* _- j9 {6 X. \$ O
- " ①②③& J" @& U+ [1 m/ m
- 1
# t5 z- H/ l3 _ x% d - 2
9 m9 L7 p! q- B7 T5 P4 R - 3 - }$ ^6 T% q% M) K, ] G
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
; U. z4 O7 Z( d+ D( l% I - 请输入“11”,试着开启左上角的窗户。"
7 k/ Q) w8 f& J( v/ a" m1 H - " ①②③
( e9 P+ f3 O1 T" P - 1■■
; b G- }2 K& F" E f9 B - 2■ : C1 [" U7 k1 J# m2 u- m0 j( @
- 3 / K& e E8 ~! h: s
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
( {. \9 V S z- c9 I - 请输入“13”开启右上角的窗户。"
i5 e& n. ^+ X, q4 y; ^3 x - " ①②③* B" t1 J* o- J2 R n6 k/ i9 _) k
- 1■ ■) U9 S: F. x" O! p) p# c1 v5 g) v8 [
- 2■ ■5 k' O) T/ ~0 b+ f- n
- 3 $ a) D2 A+ a! v( J+ e
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
5 X6 E& n6 A: I7 L8 V( h, v0 G. n - 请输入“31”开启左下角的窗户。"
7 G1 A+ k4 K, v8 k7 F# x% u/ T k - " ①②③
9 ` N# d3 t9 X - 1■ ■8 q! Q0 s% n; o( P; h$ o7 R7 I
- 2 ■: k4 Q1 b! y' n
- 3■■
5 Y0 B, s2 z" w5 W. K - 此时,总共有5扇窗户被开启了。, M U" @$ |/ [1 J" D) G' L
- 请输入“33”开启右下角的窗户。"; n1 W9 V4 V( j1 ~
- " ①②③
3 d9 ^: ?- m5 c% T7 C; [2 {1 _# J: z - 1■ ■; l7 x, C# D' u' a
- 2 " I5 E* c! _3 [- [& ?& s) c; j" o
- 3■ ■
- F. G+ e' d5 L/ M: V1 ~ - 现在,只有四个角落的窗户被打开。* A0 \( N% U4 W
- 请输入“22”完成最后一击!"1 u. x0 { s1 l& S* o8 y* @& |
- " ①②③" l& Q( w7 S0 l# D0 S9 m
- 1■■■) ^/ G: c7 Z- q3 f: }
- 2■■■6 P8 o* J n( ~6 O. P2 d
- 3■■■" s! {4 K g* U N
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
/ i6 x! W7 J* _+ a$ ]5 o" y - ' {/ m9 }+ O7 y! v' ^) m+ ^2 { ?
- ;;; 棋盘
7 G! K F# F8 I; r* t% D - (defconst *wechat-5x5-white-chess* 122883 G5 [2 @% N5 y: [4 K3 p4 \- _
- " ")
, {. Z/ k: o3 c- J1 ~* U
5 z# M/ v5 N8 _. Z& C. T6 s, v( F% h- (defconst *wechat-5x5-black-chess* 9632 {# Y* Y7 W+ h n
- "■")
% S: T# l% m+ a# r3 h( n
5 p1 D; k0 S$ H- (defmacro with-board (&rest body)
0 A6 W+ q" T. S* p. X1 R - `(with-temp-buffer
; o ]. G$ ~" }' F, d. \ - (unwind-protect" Q' g, W* f, E3 @; R/ L: Q# b
- (progn
1 h& G, X1 U7 o - (if (session "board")
* J% Y& ^8 l/ U. w/ z - (insert (session "board"))), o2 i# e# k3 F& s) N) j
- ,@body)
* i/ j4 @$ z) w& O/ {+ j4 ?2 B - (session "board" (buffer-string)))))/ K6 h3 ~4 F0 W( O4 ]) y
5 V* Z# c( F# x: h( \7 N7 N- (defun board-init (size)
$ Q, T- X5 |) [ L) u - (session "size" size)
7 F: o- G1 l7 ^. ^7 r - (session "step" 0)2 q/ X: N1 S" ~7 _4 u0 n
- (erase-buffer)3 C5 q) k' m9 V* V! J9 j/ l1 {
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))# ^% ?6 Y- W7 P% Y( P4 k2 I
- (dotimes (row size). y' h* E! d8 t& b* [( g# i# J
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))" f* t& x- {9 }& _& y) C. r" l
- 7 O, J- C$ Q; p. T r( Y
- (defun board-contains-p (y x)
; W R m7 Q: O. j, i - (let ((size (session "size")))
) v& H' O: Z; P7 L7 n - (and (<= 1 y) (<= y size)0 N$ |% X! y4 b' C M5 h, y8 U
- (<= 1 x) (<= x size))))$ H) ]' K9 g1 }4 s
- ) [3 x! F; C5 ~, X/ ^ C
- (defun board-toggle (y x)6 f3 z" v, [6 k6 B8 h
- (when (board-contains-p y x)
2 C$ o' }1 `7 t5 d& B% l1 i0 [ - (goto-line (1+ y))! o' G2 Q R# u# X
- (beginning-of-line)
, E0 E3 v$ b7 W: o* z5 m9 n% ] - (forward-char x)
3 C: |" C7 y" E% Y - (insert (if (= *wechat-5x5-white-chess* (following-char))4 U4 d3 s* n+ c* j( d" G3 ?
- *wechat-5x5-black-chess*1 k- i! F7 S) E
- *wechat-5x5-white-chess*))
; u- r. ]( t5 a# [6 i - (delete-char 1)))0 g. O H( a" l+ j. A8 E
- * u% a7 B; m% F; Z3 I
- (defun board-put (y x)
# n( z0 F1 Z: b% R0 b4 `4 b! z - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
k( D& _( [- z- R: _" X9 v# ^( t) Q - (board-toggle (+ y (first dir))
+ }0 |% S: `5 y: K1 t3 r - (+ x (second dir)))))$ t6 Z3 }# X$ |, b& T+ Z
/ O/ `, y, `! s. o* v7 {" F Z( E- (defun game-over-p ()8 l) n! @3 o% D: c+ Z& q
- (beginning-of-buffer)5 I, G: t7 Q' H8 v" U
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))' H# `$ H* ~0 }7 Q( [0 D
- 5 I, L0 M" f- P' W0 N9 U4 e
- (defun board-show ()6 v5 y" L( E- A
- (with-board2 b; k9 A" H7 D6 K& ^
- (concat (buffer-string)
) g; z' o/ P! Z& o4 S6 }. f- m5 L - (if (game-over-p)
6 C- ~/ C9 h- J6 ]. { - (format "共%d步,输入任意内容返回大厅" (session "step"))6 j" r3 W3 ^$ J- _4 w9 K
- (format "第%d步" (1+ (session "step"))))))) [8 r8 m: ~) b; R6 m3 r
- 3 D7 {: C0 E/ V
- (defun board-position-parse (cmd)
+ V+ P) ` ~ |# P - (if (= (length cmd) 2)
* _2 X3 u, a8 @ Y% }# h: } - (list (string-to-int (substring cmd 0 1))
+ s" C2 z6 t" Y3 W - (string-to-int (substring cmd 1 2)))$ o. X/ L* t0 q' r
- '(0 0)))( a& B7 g) o+ `0 G( \) b7 @
- % e) ~$ {' O9 p! }. A. O
- ;;; 游戏房间
! `" x0 s+ Z1 v9 [( A - (defun game-room-init (cmd)
7 a& E) e: k9 K - (let* ((middle (string-to-int cmd))
( z' W; T6 P0 a$ O! z; p& M - (size (1- (* 2 middle))))9 {' W( S! a) i6 Z
- (with-board
3 B4 n0 c- F @; Q3 u" x, v; J: C# a - (board-init size). e4 f) P3 d; C* c
- (board-put middle middle)))
) T2 [1 q& m! O* y9 K. _ - 'game-room)
7 L4 u7 R4 S: i. R- _. C9 U, m0 t
y$ b2 ]. C/ d- (def-room game-room. q4 d& D) ^7 _9 q$ P
- #'board-show# @: z- ~3 C- x$ ^# g3 O2 e" T
- (t (lambda (cmd)/ @. d& m) ~) @
- (with-board
9 p( f9 h) {( f* [' B+ O - (if (game-over-p)
' _7 A) s, E* h2 D9 n+ C9 N - 'living-room
$ {" w4 y2 O6 ^7 {* ? w% }: q) z - (destructuring-bind (y x) (board-position-parse cmd)
" r( G- a6 D0 \, x - (when (board-contains-p y x)
0 i* [5 l8 o" F& O - (board-toggle y x)
- L' u; o! X- M. Q4 | - (session "step" (1+ (session "step"))))# `; M0 T, c$ j8 R4 H6 h; R! Y+ Y3 _
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|