wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。/ E/ O1 M$ n: H7 R# g! g
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
, X t2 ?7 ], U* L& B- <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;">;; 定义新的游戏地图) w. \. F- F7 n0 a
- (def-map "/game/5x5.el" ; 对外开放的URL8 p- I; q! q4 i# ~7 t' y Z
- 'tutorial-room-0) ; 默认的入口
- b. m$ \4 s3 W4 G4 {+ z - : T; m0 N R5 U* S! u: W) x+ E
- ;;; 游戏大厅
8 y M" S6 O9 ] - (def-room living-room
# H/ |: X; x& A9 c; E/ s, D - ;; 进入该房间后的提示语
: k5 d. S( D- V. u0 @/ l& F - "1. 教程: V$ B5 @8 _$ O( w
- 2. 入门(3x3)9 E$ M& I5 s* c) h
- 3. 初级(5x5)
- t9 ?0 Y& J1 G: v! V \ x( i - 4. 中级(7x7)
/ V9 {. z1 I' U4 B' \ K* m7 U! Z - 5. 高级(9x9)
6 C$ f5 H% a2 E+ L! |% D8 d - 0. 关于作者
+ O# E& E* p" `9 c P4 B8 [& E - 请选择1-5开始新游戏:"" F! C8 x* L& y/ u7 t
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名7 P2 l. b% l9 h7 y) _0 Y* l. q. p. R
- ("1" tutorial-room-0)
. X1 W6 s4 `+ w9 B9 J; B2 F& b - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配0 C5 w v+ K+ X( I9 m. {+ _. c
- ; 相应的返回也可以为函数,动态返回房间名: G, X" r" w2 k# P* o/ u/ S
- (t living-room)) ; 如果条件为t,为永真# K. R A3 n& U% p% Y5 y- A
- + n; @# g% _( P% ~1 l' o4 W6 Y' `
- ;;; 作者信息8 k* M! o- e) k" I1 X! O
- (def-room about-room
6 K* | {' J% e7 R" u5 q( s - "作者:redraiment% C% H* `, N# L$ P H
- 微博:http://weibo.com/redraiment
3 |) _" [& q6 ^$ P7 i - 有任何建议,欢迎在微博或微信上联系redraiment。
' D$ [: O9 M% X* t f - 请输入任意数字返回游戏大厅。"
, G; G6 n! Q* d) h Z - (t living-room))& M: X! \3 \, ^2 }
- ( p/ j" C) P+ t h, g8 Z4 K& Y
- ;;; 教程
* E2 D. d0 J& d' } d9 o - (defvar *wechat-5x5-tutorial-rooms* 0
6 |+ ]. }) ?: ]+ I6 N - "The number of tutorial rooms")
' L" _( s' I% N* ~- y - 1 j* K; j- e5 v" C0 _. B
- ;;; 简化教程的定义% o( T- A; M! d U! u
- (defun string-last-line (content)1 L3 [6 G) }/ ]/ ^9 ^
- "多行内容组成的字符串中的最后一行"
% {8 ^- k3 Y/ O2 g; d - (with-temp-buffer& Y4 i5 }' p( Q+ z) E/ B! @3 _
- (insert content)% R u3 t3 u* Q2 Z* }: }- y
- (buffer-substring (line-beginning-position)( s- C/ m' A- R. Y
- (point-max))))# b* [" T: d( b. q. b1 T
- ) j- H2 N/ h$ b0 K
- (defun def-tutorial-room (prompt)( q) k' Z# P F* N
- "根据提示语自动生成教程房间。1 d* u% G) g4 p3 X$ _3 f
- 4 p9 Y7 _, |/ @, B2 P+ r
- 1. 提取最后一行作为问题;6 S+ }( I e; p+ k5 p
- 2. 分析问题,获取期望用户输入的内容;
# y; v1 C; w/ E - 3. 定义教程房间和重复提问房间。"
/ ^& l P" X9 I6 ?+ o6 t - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))8 d; Y+ J6 J2 l$ q) M9 v
- (repeat-room (concat room-name "-repeat"))& ~% R/ Y& l) A \ C7 `2 F
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))) X5 Y4 C- s6 ~3 Q4 ^; t
- (question (string-last-line prompt))" `# j, ]6 D" y& g! P6 h& i
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
5 u$ V$ N; ?1 h0 K8 Q9 J0 z3 ? - (match-string 1 question)))
$ h; O' j3 D' K' J9 h. l - (doors (if except, z: X g( B9 ^1 j
- `((,except ,(intern next-room))) ~' \3 u1 a9 W$ v1 W
- ("q" living-room)
8 R* Y- e$ |2 p9 r1 J - ("Q" living-room)
7 a: F' r0 r1 w& ^ - (t ,(intern repeat-room)))
* x; b6 g# V4 i1 f9 U' ]/ V - '((t living-room)))))
0 k7 ?: @* _/ ]- d/ r3 t: { - (def-room-raw (intern room-name) prompt doors)9 o) l9 {/ s, U0 Z- S/ f8 b9 m
- (def-room-raw (intern repeat-room) question doors))) k, `' U! v, m( h& Y9 v! p
- ! H9 Z$ R7 t! o8 P
- (defun def-tutorial (&rest prompts)6 T8 x5 H2 N1 w1 ?# }" {: \
- "批量生成教程房间。". F6 Y0 P/ ]4 l
- (dolist (prompt prompts)& y5 J. a8 V6 ?( A
- (def-tutorial-room prompt)))
- X1 Q- X7 u# l% q' {6 s
! ?0 O& W% F" u0 L2 S* K) l- (def-tutorial( i: O O" P7 |1 Y
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。& O# O( `' ?4 m& a0 k
- 1. 教程$ i0 ]: v8 ?! x7 y# G
- 2. 入门(3x3)
, ~4 m" |5 g* d( q - 3. 初级(5x5)0 c/ ?" ]; S9 C9 O) j
- 4. 中级(7x7)
5 B$ v7 ~% K9 P9 s, e& ?; v - 5. 高级(9x9). Y! f+ l7 i6 v& E$ Q+ I
- 0. 关于作者
* a% W4 S6 ] a% g \ - 请选择1-5开始新游戏:
4 i+ g- I) g" P' H - 您现在正在游戏大厅里。; j1 s3 z0 W, o; T+ g/ j; G! h5 s
- 请输入“2”进入入门级房间"
j5 }' B2 N7 a/ A+ E. @1 M, [ - " ①②③. _9 S4 d& p, S( w% }9 Z# G7 \
- 1 ■
5 d9 Z" E6 W( A' [% r2 \ - 2■■■( O' n) ~: x9 s% V1 ?) v2 Z
- 3 ■ 7 ~, u' L) K& F3 t' Z* G# d
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
( C I) W# r8 v9 ^ - 请输入“22”来关闭第2行第2列的窗户。", {/ L0 ?* v* y& \* H; y# E
- " ①②③
' t6 P) ?2 _# T3 O+ Q7 B) O1 r - 1
% y* |0 t |. ~, _0 i/ g6 ]( d - 2
- s5 A0 }- f: p$ l - 3
: |* n7 m& I7 E! X - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。! L1 u& N J3 r: |& F( G' E6 ~+ p
- 请输入“11”,试着开启左上角的窗户。"
+ S6 K- h; X- W# \% h. t9 u - " ①②③' J. I4 T3 F+ \; }+ j/ `. v
- 1■■
$ U- ?2 p! e3 l9 p: k, Q - 2■ & l2 O. k- b3 |( X+ b: x9 Y
- 3 8 ?' E3 L8 z# w7 Y- \1 _- }+ e
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
1 [3 g+ E4 V7 J; t c4 O - 请输入“13”开启右上角的窗户。"& H; D$ B* L2 ?, M- K
- " ①②③
x+ W B$ M( u0 N$ h3 A - 1■ ■. V) r* G* G: V% [& I
- 2■ ■& e" Y1 D# z. L4 @" G
- 3
7 h4 S# [/ {2 O3 ?' A' z - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
: K& u; Q. }9 f. i3 Z+ S3 i7 d9 t- l0 ? - 请输入“31”开启左下角的窗户。"
! @* r& v4 }) ]+ u - " ①②③6 z) U2 W8 w% i/ P s
- 1■ ■+ f7 V; T1 B& ^: I6 r1 b
- 2 ■: e. w/ r# M! t3 i( w6 U
- 3■■ J( j) l& M2 {3 q
- 此时,总共有5扇窗户被开启了。6 _( w+ d$ }( t3 F' @2 o
- 请输入“33”开启右下角的窗户。"
0 H3 ]6 R& V" s& O - " ①②③" `% q" S, N# I& U: _' t4 S2 w( ]6 ?
- 1■ ■
% s0 S) E# X! @- M2 o - 2 , B& L' t- O) ?+ m$ e& S7 k: q
- 3■ ■& _" N- \$ n* D' K) Q
- 现在,只有四个角落的窗户被打开。! J( u% s. y; W$ a6 l: o' D- z
- 请输入“22”完成最后一击!"
) h/ v& {& y: A( ]4 t - " ①②③
" U f1 _& d2 |& Q3 [$ l - 1■■■
; t) B% C* t( ] - 2■■■
9 n& g x! `: ^3 b) ^* a - 3■■■5 \' Q4 e! y4 N+ {( G$ l1 F" @
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!"); n6 L2 X+ q5 ^+ `$ l4 F
- \3 `4 b; l+ U" Y
- ;;; 棋盘
: y7 U9 E- e* `& q - (defconst *wechat-5x5-white-chess* 12288
1 S4 \' P; y6 S6 b7 d0 k0 g - " ")
. D r# _' S& T# m/ q - : R; q; R) B1 ? M. E% L7 r0 h: w$ |
- (defconst *wechat-5x5-black-chess* 9632' M8 \' ^ ~5 n9 [4 [ J& p
- "■")
' a U& F& c7 w
2 J1 @- [& Y: v4 W) r- (defmacro with-board (&rest body)
, h- A1 e' |# k6 y2 t - `(with-temp-buffer
% j" Q& J+ p2 l- w& ?% c% s - (unwind-protect+ m6 y1 n+ y- }
- (progn
/ f+ D4 }: m5 U5 ]7 h7 T4 F$ u - (if (session "board")/ D5 E3 S1 i; p0 z( ~+ L \6 ]
- (insert (session "board")))& [6 i% H; l1 A/ a. x X
- ,@body)$ T3 d; f3 X e% z9 P
- (session "board" (buffer-string))))), T, S3 _1 N0 E; S
- 4 n- o+ s+ B2 x- _0 I6 ]* _6 v
- (defun board-init (size)
) N3 c# [( ?! _" u( U: f - (session "size" size)
: |- \' L5 P" z P& C - (session "step" 0)* u) t, J" W- h, j* A X5 }
- (erase-buffer): B5 V% B' h3 i. n( `+ B$ f: D+ o* K3 a
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨")), @+ ?6 H/ R' L; T- Z' [% o$ K
- (dotimes (row size)
) V( \2 {7 @( Y6 p6 J: d - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
9 @" V* A0 t% o6 F S
& `. U; f. R) T7 d% N; A! S$ t- (defun board-contains-p (y x); L5 g" `- L4 b$ K+ T
- (let ((size (session "size")))
0 o+ B5 @) @+ [! T/ S - (and (<= 1 y) (<= y size)
+ A! r: n* f; z& U5 y) y - (<= 1 x) (<= x size))))
3 a% l. \) I/ T - , M6 s/ K( s/ ?" C
- (defun board-toggle (y x)6 T7 @! M V4 q) @. g- s* t
- (when (board-contains-p y x)
& ]0 {7 c& i! |4 z1 q - (goto-line (1+ y))# b* y6 u4 j; z4 n- b. Q9 a
- (beginning-of-line)& w" d H+ s* A- K9 y7 N( [
- (forward-char x)- ~- Y% y+ A9 ?
- (insert (if (= *wechat-5x5-white-chess* (following-char))
) q- [( j0 @ S( F7 Y& _ - *wechat-5x5-black-chess*( P: f0 Q, _+ ?" }0 I/ {
- *wechat-5x5-white-chess*))
: F" P, ?0 w* j. A7 h1 _6 I* p - (delete-char 1)))
+ t: u7 r/ O! x) }( S( u5 B - 0 G4 R3 `5 p! Q Z" r
- (defun board-put (y x)
6 E3 F& j* k8 I/ X7 w% H4 T; h - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
6 b! ]: l# A' y" s2 x9 n, E - (board-toggle (+ y (first dir))
& y @- K- J' t9 L8 X+ B - (+ x (second dir)))))
8 X3 g* A3 K7 i- T: c - 1 c. p0 G" U$ w5 E* x4 p5 {, l
- (defun game-over-p ()) t C1 x* K$ t5 m& O x4 y+ V$ V
- (beginning-of-buffer)
8 Y. U. y, i6 g; V' e2 g - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
0 \* x" B! V6 d2 X9 k8 A - . O. B, _; J5 z. _% E! `- t" w
- (defun board-show ()3 i% Z( H! A; F
- (with-board
2 K6 i) G; b# P2 o& l - (concat (buffer-string)
6 ]9 [6 k" Z; q: a3 [ - (if (game-over-p)9 f7 s) q& ^* t6 ~+ k4 `( Q& G
- (format "共%d步,输入任意内容返回大厅" (session "step"))
) a) o9 e4 T5 a- p - (format "第%d步" (1+ (session "step")))))))# d; R9 O3 T1 o7 g: m2 ?
8 E8 w1 v- I0 c- (defun board-position-parse (cmd)
7 N: O3 e8 y! c- u - (if (= (length cmd) 2)
; S5 ~3 ?& ^& X+ S2 D3 Y - (list (string-to-int (substring cmd 0 1))3 g/ Z0 `, C/ u5 B, {6 T; L
- (string-to-int (substring cmd 1 2))). E, Z" _( E6 Q0 w @
- '(0 0)))+ M" h+ W& P$ z1 X
- 2 P% A8 j' b# h
- ;;; 游戏房间; C0 z; m4 i- ]7 `0 P0 y) A) w
- (defun game-room-init (cmd)
# a+ N/ U3 I W8 o# e6 r1 \ - (let* ((middle (string-to-int cmd))( x% e$ R# g5 M
- (size (1- (* 2 middle))))
( I. h Q0 g+ M \0 P - (with-board+ K' y) }" V7 ^% U" k7 y0 T
- (board-init size)
9 }" X" Q1 }; h# {9 O. k- K0 Z9 I! ` - (board-put middle middle)))
+ ^" m' M- |' ?9 q) N2 f - 'game-room). B5 D4 Z$ I9 I+ l q4 o
- ; v4 K9 Q7 K' G9 y4 M
- (def-room game-room7 S& n. M6 ]2 Z+ u$ T
- #'board-show7 I+ G Z: Y) W+ [" B( O: E: ]
- (t (lambda (cmd)
r3 p, n E# R% E/ g - (with-board" r1 I& |- U5 {4 b8 `
- (if (game-over-p)
0 _- f# L; L, n# k; o% d( M3 M/ m* f - 'living-room
& c& C$ Q$ b1 L3 i" i - (destructuring-bind (y x) (board-position-parse cmd)
G8 K7 t9 S% \5 X0 w - (when (board-contains-p y x)
8 }# Q$ ?" t& B7 p ]' p - (board-toggle y x)
4 p4 o( @& m* M7 G3 @ - (session "step" (1+ (session "step"))))
# [ p5 n" q( p% t: I - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|