wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
( r6 S' [$ t% p( S6 e借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
2 |' s: M% j0 r9 G$ y. o( G- <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;">;; 定义新的游戏地图4 j% s, ^, @) Q, a7 z9 a3 f
- (def-map "/game/5x5.el" ; 对外开放的URL
. h4 v" E) h' a U! X0 K - 'tutorial-room-0) ; 默认的入口/ U& j- s0 J2 P8 P0 b/ F& O( Z
) y. R: z, N' D- ;;; 游戏大厅
! F0 F7 x, W Q* f9 \9 j - (def-room living-room
Q& S, Q6 l1 Z$ N! b - ;; 进入该房间后的提示语
A, F( F9 I9 ?, ?2 V0 ~ }. o5 u - "1. 教程
; ^5 ?9 Z& Y3 \- D - 2. 入门(3x3)
* A3 U1 Z! f/ @* o U - 3. 初级(5x5)
* u" y, x ?4 r$ Z. w - 4. 中级(7x7), j5 x1 Z9 i' N
- 5. 高级(9x9)9 G) S4 h# C3 Q) ]# C0 D
- 0. 关于作者4 Q" e& ~- c' C2 [
- 请选择1-5开始新游戏:". U6 r7 |* w8 X+ a- f
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
4 h! Q8 b# G2 K: P6 M# q7 ~ - ("1" tutorial-room-0)
8 V/ l2 y) F9 e6 z' S1 `) f. t - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
& w* y4 S* G4 T' \8 i9 a- F3 k- | - ; 相应的返回也可以为函数,动态返回房间名$ n$ T+ n8 u8 r
- (t living-room)) ; 如果条件为t,为永真# q/ y4 V' h3 B3 B- s6 E
- ) {' Y: {1 e8 P4 s8 z2 s9 M' L4 Q
- ;;; 作者信息
% e. H/ F2 c5 q/ w; e$ b5 U - (def-room about-room* k! O* r) V( x- u
- "作者:redraiment
) e. j) I2 k' \ - 微博:http://weibo.com/redraiment
2 {; f& a5 Q: o7 s. p - 有任何建议,欢迎在微博或微信上联系redraiment。
7 ^$ B" a, b* P' [+ | - 请输入任意数字返回游戏大厅。"
1 ~& e9 h& w4 Y1 ?. } p! ^8 f - (t living-room))
6 ~) I% ? _7 V
! g/ O- o* F4 B5 Y7 `- ;;; 教程, Y4 W' i3 Y1 \7 t- }
- (defvar *wechat-5x5-tutorial-rooms* 0
2 @% N+ }2 `' I( I \, q - "The number of tutorial rooms")
4 u% E, z1 q; k& @6 {7 n
4 ?9 p, R7 W1 q9 r- ;;; 简化教程的定义% t( x+ c M `% q! f% J
- (defun string-last-line (content)1 v$ M! x9 z; Z( E% T! J, I2 h4 @
- "多行内容组成的字符串中的最后一行"' O6 _( m4 x$ B0 J. d
- (with-temp-buffer1 D, a* U, [9 j6 v7 b7 J. [3 I
- (insert content)
8 s/ }" u5 f& U - (buffer-substring (line-beginning-position)
1 J2 b+ h1 `) G7 a - (point-max))))
5 I3 p9 U/ g+ C- X2 M8 ^+ U - * d& X6 F( L- _$ ?8 R; {
- (defun def-tutorial-room (prompt)
$ J" `2 w. ]# z; }3 m6 K - "根据提示语自动生成教程房间。 D$ `1 Z* n. |
- 1 D( Z. P' `% ^4 n- [& K
- 1. 提取最后一行作为问题;
' X+ [. z: r: m" S! r+ F - 2. 分析问题,获取期望用户输入的内容;
, b6 y7 S3 j3 L$ o9 w% G/ J - 3. 定义教程房间和重复提问房间。"5 R- E1 W- d. @. N2 L/ R
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))4 z4 E3 u" e/ R' |9 C( ]7 D, O
- (repeat-room (concat room-name "-repeat"))0 I! i$ G2 ~" F
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))( h5 K+ _: ^/ M' z Y1 _- [
- (question (string-last-line prompt))
! A3 b% K2 v* F2 G: y - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
" q& c$ J4 w0 R0 K& m; q - (match-string 1 question)))
3 N% n$ M) Z! \* b - (doors (if except* g, z( {3 F) X' G+ F J/ n2 @
- `((,except ,(intern next-room)); f' V) }& }$ }* F
- ("q" living-room)
% l; S0 J- m0 K- y* e- m+ u - ("Q" living-room)3 y* i/ D& }- e2 M# K
- (t ,(intern repeat-room)))
1 C- w0 b9 m7 y - '((t living-room)))))" a$ e9 H& ~3 o( |
- (def-room-raw (intern room-name) prompt doors)4 X" Q: Q4 Z- d) O; n5 s/ p
- (def-room-raw (intern repeat-room) question doors)))
( U2 \1 w- p" W" w8 P8 O7 `6 k - ) q0 p" \& r1 t4 J. I! s A- N
- (defun def-tutorial (&rest prompts)
$ k& t% R7 C, f9 E - "批量生成教程房间。"
; Z# I+ j" _% I+ x3 \3 Q - (dolist (prompt prompts)$ e* ^9 A' C1 ~& |: H- V9 G! { k
- (def-tutorial-room prompt))) ?7 T- P; ?$ T
- ; c9 T! O0 Z# L
- (def-tutorial
8 y3 W4 u0 A# P - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。+ Z8 i9 m; f$ m: A! V" J/ ?7 I- ?3 U
- 1. 教程$ \# L. ^4 l* d' k; L
- 2. 入门(3x3)- L" H0 j( ]2 n. r9 g
- 3. 初级(5x5)
, ^3 y* p7 q* _- ]4 B+ O" Z - 4. 中级(7x7)
. {& V1 }0 p% |$ v: t, I7 z - 5. 高级(9x9)/ L$ ]6 o5 X9 _5 _& W
- 0. 关于作者" D+ c, A. p' d0 |# o
- 请选择1-5开始新游戏:7 c. Y9 s) P+ z% E7 T
- 您现在正在游戏大厅里。/ I3 g5 A p+ M# n/ M+ d8 \) Y
- 请输入“2”进入入门级房间"
+ N/ M6 b' O% {# N - " ①②③2 w$ P+ C# `' R8 T% W
- 1 ■ " h* q: J* Q- T- g; h' ]* N
- 2■■■9 v8 G3 P. c% @* b
- 3 ■ . D3 o1 y2 }( W+ t2 b6 J
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
4 V0 o7 m) W- |7 l+ B - 请输入“22”来关闭第2行第2列的窗户。"
$ i" t# i! r+ ~4 r/ L( G - " ①②③3 u3 n1 n$ V- c6 v t u- i
- 1 , Q2 T7 U9 ?4 Y" U8 K
- 2 % ?2 W9 ~( N4 `: `
- 3
$ b. [' k4 U6 p7 C2 t' S( }/ D" v - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。: M1 w& ~: Q, @8 {; F5 s( p% j
- 请输入“11”,试着开启左上角的窗户。"0 G R% Y6 V h4 E* p, o& r( u
- " ①②③* V3 P& L, u" h1 b' x. l1 h
- 1■■
% ~# z0 ^8 Z' z- s' l# R - 2■
: W' @1 b7 Z1 g* ? - 3
4 g8 @6 i# _6 C; ] - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。; {) } g$ ~4 _, p. m9 w* Q$ G
- 请输入“13”开启右上角的窗户。"+ b1 o$ i* O- T) p' O6 S- G
- " ①②③3 x3 @+ U9 }0 {# B1 r
- 1■ ■2 ?' [9 T3 T* N& L! W- p( \5 V
- 2■ ■
. P- a6 S8 E C - 3 + b1 b# K9 d$ m m% z$ k) n7 F
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
+ m4 }' ~8 U- A( R9 D3 `: R - 请输入“31”开启左下角的窗户。"% w: o4 a9 d! @& K
- " ①②③
! `* I, N! [0 u - 1■ ■
1 W8 f' x- u, N4 w - 2 ■
~3 W2 t# R4 U; a - 3■■ 4 b% w4 T0 Q3 j- g7 d
- 此时,总共有5扇窗户被开启了。
. [: h4 e( d5 g1 ^) A8 {* z" r - 请输入“33”开启右下角的窗户。") o1 u, V; A6 }1 S! O1 Q
- " ①②③1 D) G, N1 E- h/ I2 f
- 1■ ■
0 r0 F9 |9 Y) ^" F+ y - 2
8 ^& e1 `- Z9 ]3 ^ - 3■ ■% r- ^+ `4 C* f. E- O* _ L+ Z
- 现在,只有四个角落的窗户被打开。6 a2 q0 q! i6 K8 \
- 请输入“22”完成最后一击!"
; Y! a4 u6 Q' S- n/ A3 x - " ①②③
% B' b9 z! J# m8 l i5 \ - 1■■■! W8 E0 ?, n$ D: J2 P' k( N0 K/ y
- 2■■■
$ B# W( S% V: i! M9 N& f; [# `" L - 3■■■
9 z! |/ Y, n9 D; @4 { - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")# L' A& a2 E* Q
- & k) S+ F) F5 N) L, l; D$ n( E
- ;;; 棋盘
+ O$ E$ o' [6 j2 ~0 `( ^ - (defconst *wechat-5x5-white-chess* 12288 D6 L! I5 B$ T( p F: x
- " ")
6 s# h- A! V3 N - ; i5 ~% M1 `. p
- (defconst *wechat-5x5-black-chess* 9632! m) H* b- Q3 H! {
- "■")" A( ?0 _7 L5 q
- ! D9 H2 k0 W( u
- (defmacro with-board (&rest body)# n6 v& ~1 ^$ ]' ~
- `(with-temp-buffer
, t& ]/ Z* E" i9 R7 _ P - (unwind-protect
: c" O. K; B0 R& t6 w* J* m/ W - (progn
: S* E- ~8 d( |+ y2 E1 W - (if (session "board"): K' S3 A$ A* d. l! E: n* R
- (insert (session "board")))
5 P* m+ U3 L0 ], g& t1 G, z0 e - ,@body)
3 U' d+ p1 B. x: c5 u - (session "board" (buffer-string)))))0 l- l- @- c8 J& u- n9 }; t; _
; o, O6 o8 Y- v- N5 @ j T4 a- (defun board-init (size)0 s4 E' ~$ H. o7 w6 ?: W6 M9 n
- (session "size" size)
% U8 j, H- n" R( a! B v! t - (session "step" 0), T# m5 v% J0 Y6 Y
- (erase-buffer)
* q' y G# E: k2 \+ x. U+ r - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))# D" w; W4 K5 `9 z; D6 k
- (dotimes (row size)) T: ~$ }2 C0 ^6 K4 u; x6 G' B$ `# m
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
! B, C6 n0 t8 u. k1 I2 o# _ - - [2 f9 o4 d+ ?6 r
- (defun board-contains-p (y x)5 R1 F; J4 b8 M, N5 k
- (let ((size (session "size")))& c/ O, n9 j0 H. v; O# x4 H
- (and (<= 1 y) (<= y size)
$ k9 T4 ]" x2 b* o+ h - (<= 1 x) (<= x size))))
; F! V0 U* I v( \; o4 g - % ]+ Y' `4 O% P) T# O; ?+ o. v
- (defun board-toggle (y x)1 _, h3 h6 f P" A/ P. i2 }: ]
- (when (board-contains-p y x)- e- P# e+ _* I' x1 C7 T- N8 L
- (goto-line (1+ y)); R9 D S; ~- i; m
- (beginning-of-line)7 N, M5 W" D4 T D- A
- (forward-char x), \* z2 ~ x: U5 X) \4 m
- (insert (if (= *wechat-5x5-white-chess* (following-char)); b& b; l6 u9 X9 d! `3 S4 [: M
- *wechat-5x5-black-chess*
5 D0 O1 J# k& V1 I: a. F+ t2 Y - *wechat-5x5-white-chess*))
4 V7 D6 a: c Z/ Q - (delete-char 1)))
: {; `& s7 F- b) A8 V - 5 H$ E Q1 P) X$ C
- (defun board-put (y x)
+ R' P( b. U ?* o - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0))). ^4 V: P0 f1 A1 g
- (board-toggle (+ y (first dir))
; ]1 k! ?2 b2 L1 j - (+ x (second dir)))))! n$ M0 n( P% |* D' }
$ a* q1 G3 }% |9 @- (defun game-over-p ()6 ~/ g( a7 v2 _0 [# G2 E
- (beginning-of-buffer)" ~( ^4 y; ~2 J7 b7 y& L
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))4 p- M3 k3 P6 ?- u
( {3 k. S6 x: l; z& R& {' v; ^0 @- (defun board-show ()" t* c6 j7 R4 c4 x3 a4 C
- (with-board" ]! I, g" i! U) A3 i+ ?. w. V4 c* V) N/ P
- (concat (buffer-string)
* _' d6 L. v! M/ U$ H, d - (if (game-over-p)
, k# f2 ?4 { y, O' g - (format "共%d步,输入任意内容返回大厅" (session "step"))
% |3 Q8 M$ w, h - (format "第%d步" (1+ (session "step")))))))% i5 m5 j- z: f: x6 s; h
2 Y* E: R x: S& i2 ~8 v- (defun board-position-parse (cmd)
e R, K: [3 I* ~ - (if (= (length cmd) 2)# V! |5 i9 e) z/ d1 W
- (list (string-to-int (substring cmd 0 1))+ z3 o3 E+ ]& E" E1 v- G3 Q
- (string-to-int (substring cmd 1 2)))
7 Q! w* d# C5 P - '(0 0)))
# J$ z8 q2 s, p* c, w! H; v! n& i - * b, c( `1 b: _2 F. q
- ;;; 游戏房间
% @- E ]5 q) v- ~# [ - (defun game-room-init (cmd)& }$ u5 F" u: U. B# c, E
- (let* ((middle (string-to-int cmd))' p* D3 j& [( H8 z3 C5 H" ?
- (size (1- (* 2 middle))))8 k" e: x# c' R/ K( ?& h5 D
- (with-board; f U' Y0 h$ u6 d
- (board-init size)
3 ^: [4 z: F& G0 M: o - (board-put middle middle)))/ s( i5 L! |8 ?1 e+ F8 P; G% ]+ E7 ]
- 'game-room)
4 k0 ?4 s5 T% x) |4 D/ g
1 U& \. @, _. K8 ]4 ^( W }- (def-room game-room% ~$ z8 L6 c* X- n, [
- #'board-show* s* z1 W9 N2 E2 s: i* \3 w
- (t (lambda (cmd)% l6 s2 y, N0 \3 P/ p' Z1 h
- (with-board
* h. j5 u: R4 N- W - (if (game-over-p)
6 c/ E& N! K' B) U - 'living-room6 |. A' t% X3 |! @& K! O8 o
- (destructuring-bind (y x) (board-position-parse cmd)8 w/ ?- b/ S( O- k
- (when (board-contains-p y x)
4 g9 H5 O* h' O, b - (board-toggle y x)! v" q% v$ S. |1 `7 V; t+ x8 E
- (session "step" (1+ (session "step"))))
1 S' N" K# z7 P+ u - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|