wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。* H# ]. w& F& d, \" }1 G
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
0 j. m. O( s! @4 w% n: {- <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;">;; 定义新的游戏地图
% ^' O. `, b4 _5 H* `8 `& J, l - (def-map "/game/5x5.el" ; 对外开放的URL
5 U9 F7 t6 m* ~9 e' x1 s - 'tutorial-room-0) ; 默认的入口 E* J+ U7 r( K1 R5 e
$ _3 G" o& ?8 q: X7 A1 D- ;;; 游戏大厅2 `3 U4 }& b6 ^+ H( r/ ~
- (def-room living-room7 L, h. x i0 N3 q
- ;; 进入该房间后的提示语- F$ X- U( }' R( l+ |9 v* Q
- "1. 教程* ~5 {, \7 B% |) a+ }
- 2. 入门(3x3)
: g) {1 y/ a* T, @2 g6 v k - 3. 初级(5x5)* b2 o& C# x5 T3 p6 I* m6 C
- 4. 中级(7x7)
, w9 i2 w% q# ` - 5. 高级(9x9)
9 T+ b; @6 K' O" K - 0. 关于作者9 _/ B( l! x( _9 }4 B; d/ M' U
- 请选择1-5开始新游戏:": m0 \5 z+ h+ `
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名6 w6 R: c1 s! W8 e# w8 [
- ("1" tutorial-room-0)
: |, F1 o* T7 q7 R - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
. K9 o* Z B9 \; u - ; 相应的返回也可以为函数,动态返回房间名
8 ^3 q. R }* J8 u - (t living-room)) ; 如果条件为t,为永真8 K* W- a. f0 \/ l' h& C1 I
- 0 b; h8 A/ M: V* L7 q: \
- ;;; 作者信息
8 j* t+ X: Y5 W) | - (def-room about-room
l3 y: b. |6 T9 A6 L; X - "作者:redraiment
+ g; L1 _ Q3 N6 k - 微博:http://weibo.com/redraiment+ ]3 c7 c4 P" M O# X
- 有任何建议,欢迎在微博或微信上联系redraiment。" a$ V" L/ S" y" l, G
- 请输入任意数字返回游戏大厅。"
: ^$ w1 s$ G8 |( O1 F7 d - (t living-room))
& p7 `1 ~* L9 Z" d4 }2 b8 P+ u - 8 m( C6 s- ~( `2 G# H
- ;;; 教程
# I H2 _" K, c8 a* \: Q! s2 U - (defvar *wechat-5x5-tutorial-rooms* 0
2 s$ U# x( m$ x( ~% { - "The number of tutorial rooms")
^' i$ s7 _' g7 }6 i# p - & `. {# G0 A$ I9 i. W9 ]* R8 m- o
- ;;; 简化教程的定义; q! d9 H# X2 w$ d5 V. K+ h
- (defun string-last-line (content)
+ T% X9 Z' B9 A% O: m0 F* \, `: [ - "多行内容组成的字符串中的最后一行". c, Z/ J* x R
- (with-temp-buffer
3 [5 i, q4 G+ z$ B/ X; v6 l - (insert content)2 _/ ~: J2 n0 |, d$ @: _
- (buffer-substring (line-beginning-position)
4 h* ?( M6 z9 _& m; ]9 q - (point-max))))
( ?. S# I+ i5 }5 y - ; ]; T" Z' r- W
- (defun def-tutorial-room (prompt), l& T& i4 @1 O! K* M4 O% I/ |
- "根据提示语自动生成教程房间。) o" {5 U# h1 j: u& g
- 9 K7 M! b* ]5 m- Q# P
- 1. 提取最后一行作为问题;0 \1 _3 ^. Z8 _4 B$ p3 s! z
- 2. 分析问题,获取期望用户输入的内容;
8 @* E0 L( Y' `# l - 3. 定义教程房间和重复提问房间。"# x/ J* q! L3 h* H6 e9 T- N3 d( l
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*))); D, w* k+ _4 S2 Q
- (repeat-room (concat room-name "-repeat"))
9 a8 q, G8 d3 _& w/ H4 n0 U - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))4 x. k# F* @8 X$ h% g
- (question (string-last-line prompt))
$ C. c, M6 h$ u2 T' S+ \. | - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
- f/ K: ^! p8 T7 m - (match-string 1 question)))
( l9 `% \. L O6 ~, y - (doors (if except
0 L# b9 ~: t+ s8 J p - `((,except ,(intern next-room))4 A3 N7 T4 I2 D5 s r: C. l1 s" i
- ("q" living-room)9 ?7 y+ [7 {5 z% W
- ("Q" living-room)5 ~" q# c. Y! [. i0 r
- (t ,(intern repeat-room)))/ _9 n- A. Y. h- f/ k) z
- '((t living-room)))))
! g/ A8 `4 F$ I$ [5 m, R3 E: E - (def-room-raw (intern room-name) prompt doors)
/ f- b: l' W# t9 [! I - (def-room-raw (intern repeat-room) question doors)))7 y5 n* f: H* l5 I; |' h& ]& N# e- @
' L( }' f9 r! Y; \1 E0 _- (defun def-tutorial (&rest prompts)% L% g3 n! j/ o$ [/ [) t9 I4 Z
- "批量生成教程房间。"
0 D. X/ `; c' K - (dolist (prompt prompts)5 c' T7 c A, k6 c. y }% Z' L
- (def-tutorial-room prompt)))! k8 g. H, L5 P5 e. F/ |+ ~7 t% u
y7 e4 u. [. o. A4 A9 E2 y, `- (def-tutorial6 F# |' |( Y, M( C* h7 P" D
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。$ {4 _7 P4 ?3 d( R' u
- 1. 教程
% R, A7 D! t w, v- [1 W - 2. 入门(3x3)/ G* e# E8 I4 N8 ]9 U! N q; j
- 3. 初级(5x5)
0 b1 k& c4 z5 r. R+ T2 o - 4. 中级(7x7)6 V* r1 H( e" H9 N
- 5. 高级(9x9)4 Q$ E& W$ K: r( z9 R/ w; ?
- 0. 关于作者/ z: M. M5 X/ {% S+ U- s
- 请选择1-5开始新游戏:' o: G; _( V8 v8 @
- 您现在正在游戏大厅里。
# ~- k2 f$ q' R, J9 E( \ - 请输入“2”进入入门级房间") T* W H- R2 i
- " ①②③3 e) A1 L2 s' P, ~- ? `0 ?1 I
- 1 ■
3 B8 ~9 ^4 U7 C& c; _5 } - 2■■■ M2 y7 f2 J" M b+ z
- 3 ■ : X4 P6 C/ I0 `
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
" A: K$ A$ Z& i9 ` - 请输入“22”来关闭第2行第2列的窗户。") a9 b% @ p. r# ^# V
- " ①②③. P; g, P6 m# F7 h g. m/ V
- 1
& {' Y0 y. ?+ ^/ q$ e - 2 3 U% r. M! `4 k% W" {: y( H
- 3 - X( [4 { d* o4 D9 B3 W. K7 }2 Z
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。 z W$ x! k8 X) A6 l
- 请输入“11”,试着开启左上角的窗户。"( b d! ]: N, B9 |( [3 q
- " ①②③) `: @. ~% z5 c& r* Q
- 1■■
! C% @$ k. \, G" K - 2■ - [7 b* j5 H# g/ e
- 3
8 k- @/ w% K, [9 |3 z - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。4 V$ z4 g O% d5 z+ D
- 请输入“13”开启右上角的窗户。"
; X5 L; \, L6 G% a9 l" w' d* C - " ①②③( a8 H/ j$ l0 k0 K3 i
- 1■ ■5 c4 \' g- |: `$ f
- 2■ ■
/ g% _1 {* a B+ ]: c" M/ e - 3 * G) d/ \" m7 a
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
7 Q; E& D: G' u+ ?9 I/ a5 f8 x, G - 请输入“31”开启左下角的窗户。"
) p: p% W7 F5 d5 Z - " ①②③1 k, J4 R- R3 w$ N& ?4 w
- 1■ ■
; T. i0 g9 u8 k6 S& m, X - 2 ■
! S) @" E" t" q U8 k7 j - 3■■
% E1 D6 U0 |* {8 |8 h$ z b* U - 此时,总共有5扇窗户被开启了。: |" r$ X5 n! e( ]
- 请输入“33”开启右下角的窗户。"
) F' O0 |2 {# c. P - " ①②③
' P1 H; x9 N; X0 D5 |7 } - 1■ ■3 Q( p+ V5 m) y
- 2 ' Y4 M: w: [. g/ @
- 3■ ■
, r. a3 c9 R7 A* D6 {0 l - 现在,只有四个角落的窗户被打开。' g5 _. j; `( G5 ] }+ P8 Q, }
- 请输入“22”完成最后一击!"
4 T# [4 n/ m9 w0 z4 r - " ①②③
/ U: i4 j( O* ^6 b5 g, A - 1■■■2 N4 v- X& q9 g: l
- 2■■■
( e+ d; f0 I( \0 ^) Z" ~7 ~ - 3■■■
( m# f: \% V* y5 r+ s J - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
+ W/ ^5 B9 Z" c( K8 S' K$ R3 Q
, W( I- `* z. W7 g5 S# L- ;;; 棋盘
( l# W/ S* B6 y: ]; M- c/ b - (defconst *wechat-5x5-white-chess* 122884 g) W7 q4 a5 }. P+ G
- " ")
- V0 C. O, x! F9 D% @' @+ j - 5 N2 ^" [) g0 H1 i3 z
- (defconst *wechat-5x5-black-chess* 9632& B. p7 v+ l3 x- ^* U4 q/ G
- "■")8 P3 d C3 j% _- B* R+ n' G. e8 {: D
- ( C" S& ~: X3 P% w
- (defmacro with-board (&rest body)( @: u0 C0 |4 U9 [! `' C. Y
- `(with-temp-buffer
5 C( D' U" H" @" B3 M% n - (unwind-protect
' {2 b2 D7 K/ U! C+ ] - (progn$ F- M# B6 l2 [
- (if (session "board")4 z- O7 E/ N' T+ ]6 a) I8 F$ S
- (insert (session "board")))( S: Q3 [0 \9 U
- ,@body)' W$ ?& o5 Q2 K5 }( q
- (session "board" (buffer-string)))))* z. O# T7 G& _5 s3 [
- 9 Q& Q V- G8 L" G) t% ~: c; e
- (defun board-init (size)7 Y9 z7 L' H! t D0 B+ s+ q
- (session "size" size)
% W, G% Z! ^& D! m* N' a - (session "step" 0)( t- C# L0 @3 `
- (erase-buffer)
/ _4 I# G8 l g/ A3 Y - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨")) W5 b) e4 p/ o) g; [; t
- (dotimes (row size)
/ f$ u1 _) ]1 o$ q" o3 k5 z* y - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
( m0 J' e, d( u! x
/ h4 M7 N1 ]+ _3 {* O r" l- (defun board-contains-p (y x)) G: l& \8 x/ X, v7 Y# d
- (let ((size (session "size"))), j, D. c5 n' @9 Y7 l" G; w
- (and (<= 1 y) (<= y size)
K* V5 \8 }: P5 } - (<= 1 x) (<= x size))))
% c5 k* J" g0 V" d0 ]% `4 a; z: Y - / B) A8 b( ?# l3 z/ F
- (defun board-toggle (y x)
; ~- _, E' C: E - (when (board-contains-p y x). }; R% e, u! o, `! m% M; @. g
- (goto-line (1+ y))% \, Y! u. J* L5 \0 ^
- (beginning-of-line)0 @" ~* @6 ?; {4 o9 j
- (forward-char x)2 x, x! ~0 Z6 z" d
- (insert (if (= *wechat-5x5-white-chess* (following-char))
! |, G& Q1 ^( z4 B) j* N - *wechat-5x5-black-chess*4 ?8 K9 j9 J& R$ `! q+ j# f
- *wechat-5x5-white-chess*))
# f3 l" N% H6 q: @( F - (delete-char 1)))
" Q, b6 m9 Q$ w; b1 A# s' v; a+ { - 8 ^4 h: q* H- \7 u6 f
- (defun board-put (y x)7 X+ E+ m& X2 l! E
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))* m8 S! ^* W; Z4 x
- (board-toggle (+ y (first dir))# ^. {% p# {. N5 T: o9 _
- (+ x (second dir)))))
! [' o# j5 J, W" E W - $ J a" t/ S3 ?) F/ i
- (defun game-over-p () d7 F, t& I$ b- ]; `& x* b
- (beginning-of-buffer)
* V) ]0 Y; w" d# p j7 d) R# `, ^' u - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))$ I9 l/ V# j/ L+ Y# g, h% x
i; }, y+ D0 Q, Z: M- (defun board-show (); E% d1 {* y0 h* u
- (with-board- N4 G3 X! Q1 v1 K1 e5 r, Y, V- R8 J
- (concat (buffer-string)
7 l% ~1 h: m, t h9 h q - (if (game-over-p)
2 {% |+ h" } `+ v- ~ - (format "共%d步,输入任意内容返回大厅" (session "step"))9 G% e% D; K/ c+ k; g8 s9 R- R
- (format "第%d步" (1+ (session "step")))))))
* J" \* n3 o) K9 C4 X; |; l
6 P; N1 w( {6 {5 n) n- (defun board-position-parse (cmd)5 N- i7 |: w# k1 @0 t( C4 Y
- (if (= (length cmd) 2)$ [9 h* H3 c) V% F9 V
- (list (string-to-int (substring cmd 0 1))
+ f( B+ B/ W, D - (string-to-int (substring cmd 1 2)))
% V. T. L8 k1 v! T6 N' ? - '(0 0)))7 I0 ~4 K @# a$ J/ ^
% A* k0 R w+ f! ?: _- ;;; 游戏房间$ n1 @0 t) S- w0 M7 Y
- (defun game-room-init (cmd)
$ |9 ]) S/ s8 n - (let* ((middle (string-to-int cmd))- i/ m6 x {: K, F8 ~
- (size (1- (* 2 middle))))& o- B" ?! d6 q* R
- (with-board
" |& u# _8 ^, M. @+ |; a$ P - (board-init size)
/ g9 V% m! c1 o+ ^& |* J - (board-put middle middle)))5 E$ G; v3 V8 M) w# v
- 'game-room)
$ \" d) S5 {* Q - 5 c1 H0 x4 p: o9 w; y3 u$ A( z
- (def-room game-room9 m) T; e$ Y* U: Q1 N
- #'board-show
: K0 r1 l: _8 |# q3 z# _ - (t (lambda (cmd)
3 R+ ^$ \- L* @( C - (with-board
; V, f2 e# V9 j - (if (game-over-p)9 R$ x% |( E* k4 m6 S& l# q
- 'living-room- u8 ~ @" S$ j8 A
- (destructuring-bind (y x) (board-position-parse cmd)# T- B9 o4 g1 E8 d
- (when (board-contains-p y x)
* G) _! \: m* _6 G: H' ~ - (board-toggle y x)
4 m9 Y+ q/ s: i9 S - (session "step" (1+ (session "step"))))0 H0 g4 A* |3 D
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|