wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
) y& q4 x X- p# u. v借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
6 N G J/ R9 \% W0 f- <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;">;; 定义新的游戏地图
) k4 ?- \) A, L - (def-map "/game/5x5.el" ; 对外开放的URL" G0 [- F/ c+ |' J( r* b; z
- 'tutorial-room-0) ; 默认的入口
& l+ p3 p7 {% n* l
) a1 z: u/ r& H- H: i, P- ;;; 游戏大厅! ~5 l9 |# ?2 Q$ U3 `- j" d
- (def-room living-room- v2 h$ ?3 s+ U; U" s, D [
- ;; 进入该房间后的提示语
9 M7 y$ Z. z0 |( l. O) O - "1. 教程
( v; ]! Q; n" F' B8 k V1 l# } - 2. 入门(3x3)
7 a# |6 M- Y& N) n M# o7 i - 3. 初级(5x5)
6 |2 k" n6 N' | - 4. 中级(7x7)! F8 a' L/ p" p/ D! D
- 5. 高级(9x9), z5 P# X. D: B; x+ q
- 0. 关于作者
! S( k" x% S4 y/ _# w+ m! H9 E+ \" q - 请选择1-5开始新游戏:"
' P+ b, q' A9 L2 }+ }% Y6 @8 ` - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
) u# {- z3 Q/ L3 C( Z - ("1" tutorial-room-0)
$ {! L, z/ m, \: q0 v2 {) l( t) j - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
8 l9 U7 b6 Q8 f- L; U$ M" O - ; 相应的返回也可以为函数,动态返回房间名1 g6 Q" K5 D8 N. \1 ~
- (t living-room)) ; 如果条件为t,为永真
: u* \( Z3 V/ U' @1 X! s* } - - X, V8 g1 L! K7 F3 U9 o
- ;;; 作者信息
) K; G( y: T% b# f/ U - (def-room about-room
7 y4 V6 h& _& T - "作者:redraiment2 m2 T7 J( H# W8 v; K3 u
- 微博:http://weibo.com/redraiment
+ J$ ^. `+ @8 Z1 c# A! D - 有任何建议,欢迎在微博或微信上联系redraiment。& W9 i$ X- C6 L3 l8 H! Q
- 请输入任意数字返回游戏大厅。"
. L+ h$ C' a6 l5 F3 | - (t living-room))9 l" r; A4 U/ R8 Y. F" a
2 i% S! M2 k6 ?, o" x- ;;; 教程2 a0 ?6 U3 {( z i2 Q
- (defvar *wechat-5x5-tutorial-rooms* 08 ~- y, W/ V" Z/ U
- "The number of tutorial rooms"): I9 M% A6 q; D6 u
- : [' m3 G) R) n% W
- ;;; 简化教程的定义
6 h9 \8 d- q. Z T" T - (defun string-last-line (content)
7 t3 Q9 | R5 S2 ?3 c+ Z' h2 ^ - "多行内容组成的字符串中的最后一行"$ m9 T+ C& m' q7 N K5 l
- (with-temp-buffer% |" q) g d0 Z
- (insert content)
* F, A F- D$ W0 e3 {$ K- l - (buffer-substring (line-beginning-position)9 C- ?4 W( s/ {1 R# g
- (point-max))))4 |7 {7 H1 `6 H) E- ~) {
- ' `" n/ Q; c- x4 W/ R2 k4 V+ E
- (defun def-tutorial-room (prompt)9 O3 D; z7 ~% o1 U6 ~& Z4 t
- "根据提示语自动生成教程房间。
) V B) o& x0 ]: J. D2 t' I2 A - / y6 k; `. N& D8 q
- 1. 提取最后一行作为问题;
+ o; u1 K' y) y) }0 l. ` - 2. 分析问题,获取期望用户输入的内容;
\' d) S" T0 Q1 K) c8 G; U - 3. 定义教程房间和重复提问房间。"2 v) c' U" N& }- P/ K
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))1 G0 l9 h8 i, h. r0 |, x
- (repeat-room (concat room-name "-repeat"))" w9 G5 o/ j# U& \$ \, H
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
" v% r! |( j8 i1 D( q8 h% Y. m - (question (string-last-line prompt)): j9 b* l6 S$ h! l3 t! O3 q7 e: h- p
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
- Q' R; F0 L# p% O% @ - (match-string 1 question)))0 H* H. n9 a( y/ v% q8 y; o" s
- (doors (if except
' I" H i1 q) Y4 Q, Y - `((,except ,(intern next-room))+ I3 a* ]' @3 F5 E
- ("q" living-room)' Q4 K I# K- h2 U7 }4 _3 A0 b
- ("Q" living-room)9 |0 l; @: ]) `8 E( G% U: V" S: x
- (t ,(intern repeat-room)))
6 r d7 Y+ ]! r. m) u- o) e: f - '((t living-room)))))
5 t8 c9 P0 b) J - (def-room-raw (intern room-name) prompt doors)+ h( n$ j& D! j0 w
- (def-room-raw (intern repeat-room) question doors)))
7 J1 H5 v, \" t - , \+ e% K2 w9 g# R$ a3 t* J4 G0 |
- (defun def-tutorial (&rest prompts)
- x: H D O, g5 a6 F4 h% h& s( W - "批量生成教程房间。"
$ b0 n, C5 U6 C% M! r G! H - (dolist (prompt prompts)/ D6 O! R- M5 G( j) }
- (def-tutorial-room prompt)))! ?' [1 X; @% Q- o2 T( C
- 8 c7 q3 n) B7 x
- (def-tutorial
4 D0 A h# _; R2 r - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
( ]6 J5 F$ j8 ?' I; V - 1. 教程
7 W8 y+ x4 i s - 2. 入门(3x3)* }# D C1 o9 a' Q5 `! g
- 3. 初级(5x5)
8 X: M! T& f) X% C - 4. 中级(7x7)9 d& L+ p+ l) _) f% s7 _
- 5. 高级(9x9)
7 l/ ^! R8 ]; S4 |1 u - 0. 关于作者; O# X7 Z" G% F3 L
- 请选择1-5开始新游戏:
3 ~: n3 X4 g/ V4 A: a# c1 x/ h7 _% M - 您现在正在游戏大厅里。9 g5 F$ V7 M: [( z! Z) \/ J
- 请输入“2”进入入门级房间"2 j. @$ r7 H! V- @6 d
- " ①②③
. b. R( z* [6 r8 W9 ?( F, b# F - 1 ■
1 Q' j: Z" U# y. `/ ~ - 2■■■: j. g6 z' @2 A" J/ ~4 u4 D
- 3 ■ 9 t P3 r5 c7 A' Z+ _# l9 { K
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!/ P( p' Y/ @' w: l9 \. F+ J
- 请输入“22”来关闭第2行第2列的窗户。"
) f5 u8 B5 m0 e8 O+ d - " ①②③
4 n# g5 |! l; l4 l1 N - 1
" Q8 d5 {% I5 U6 _9 e - 2
/ C5 q' c) X' p p - 3
, T: j# |' i9 m/ c( k - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。* F5 P! E. @/ m# `! ?4 g \
- 请输入“11”,试着开启左上角的窗户。"# \2 p( L1 w3 J4 Q) Y1 H4 B/ ?
- " ①②③
2 N& f2 P8 I6 O2 ^5 t - 1■■
# e, u' ]6 [ ^1 p& o" {" g7 p - 2■
- N! _% L6 A% i& t$ h - 3 5 e5 g- z0 q( U" a6 a
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。+ v1 j9 v$ K) O, T
- 请输入“13”开启右上角的窗户。"
# |& D* G3 M, z8 s5 ? - " ①②③
$ @; f+ ^' C! Z' ^+ G& F4 z - 1■ ■4 u, D' u) ^3 A; L
- 2■ ■! b8 `. y) c/ E
- 3 * v6 F. z1 C' n1 O
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。* v4 w' b! ~7 o9 }. U! ` {
- 请输入“31”开启左下角的窗户。"
6 J. D( b$ f' T+ { - " ①②③
( p0 X% n7 h, M9 S6 Q2 H0 @- [$ M - 1■ ■
/ K% M, Q4 G* N( P - 2 ■
' y1 j' a3 |3 R3 R" ^7 f5 k - 3■■
: q$ @5 | n# F - 此时,总共有5扇窗户被开启了。
; e% q1 P& @% k- [8 R& s# j8 J - 请输入“33”开启右下角的窗户。"
1 i; f, D' h5 r2 u - " ①②③
9 \3 m) t" a5 I- C! s- \+ X - 1■ ■
3 I* h. q0 m: W, t& Y& | - 2 / V! z9 v5 W* s" b
- 3■ ■
$ B+ [- i& ?3 ~* C; y - 现在,只有四个角落的窗户被打开。( H2 g& ^# D6 O1 f2 g# p# Z
- 请输入“22”完成最后一击!"
, Q) r8 I& G$ ]* f0 c4 j - " ①②③
- T& a+ z6 Q$ [) V - 1■■■
$ ?) l: Y% i, G# M0 s2 _! D - 2■■■( o4 a3 z1 Z7 T$ P) |8 K/ a
- 3■■■
7 L* T y& N* _1 N& X: `2 L - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
$ ^0 g' w$ A( }* [6 }
R; f8 l( q: X) {- ;;; 棋盘
$ o8 M7 E. \5 T9 [! k* B1 a; a. L+ N - (defconst *wechat-5x5-white-chess* 122885 T) }5 V [/ l. d& q
- " ")- N3 e3 J W1 ^
) I0 N9 i g; S$ y k. R1 r- (defconst *wechat-5x5-black-chess* 9632
# k7 I3 O- l% _" c" ^2 e - "■")
3 c; s1 L. z. T1 ~: K4 @& R0 L! {
5 k- ]* |( a% x- (defmacro with-board (&rest body)* D( x- ~7 i% W. y$ t! A5 n
- `(with-temp-buffer/ y5 y7 p: R2 y# s$ ^7 A, Z9 B
- (unwind-protect0 h" `, p6 [7 w& _& g
- (progn" T D5 S4 j1 s% ~2 F
- (if (session "board")
* e c8 B0 N/ b$ E1 d - (insert (session "board")))7 M0 M! u y7 @/ a9 M/ W% {
- ,@body)! e9 J2 H* G* E: v/ P
- (session "board" (buffer-string)))))* s2 ~6 E$ R5 q. j# d) x6 I$ L
% c# Z1 ^! H' @4 q# E5 Z2 h# c+ E0 y- (defun board-init (size); W/ o0 y c+ j1 _8 O. a1 U3 p
- (session "size" size)
+ H( V* C4 ^7 x; D+ u - (session "step" 0)
4 v" m" W; }* k. Y1 _8 D0 ^1 j - (erase-buffer)
3 p* o* O: x% v7 W9 D' d+ L - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
/ o' U* ^% S$ G# g - (dotimes (row size)
2 F- T' D5 I/ l* A! y - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
/ `$ w" V3 B4 R8 x
: _7 P( ^: X" m6 g* v v, P- (defun board-contains-p (y x)) s8 r/ }/ K O0 J
- (let ((size (session "size")))
7 q- ?( ^ i& C - (and (<= 1 y) (<= y size); ?! J, a$ i! e8 W3 K: S$ S
- (<= 1 x) (<= x size))))" a3 o/ N, g% N, @4 K" c4 ^0 r1 T3 a
- # @/ j5 H+ ~% c" F1 X4 f) w
- (defun board-toggle (y x)
5 z4 p9 M9 L8 P1 `! o2 f - (when (board-contains-p y x)
, s9 g' x0 O( B; O - (goto-line (1+ y))
/ U7 Z' V# E+ H3 h2 e, k - (beginning-of-line): N5 n' Q) o, I; Y
- (forward-char x)
/ z! @1 Y1 _* k - (insert (if (= *wechat-5x5-white-chess* (following-char))) t! O& |7 Z/ N7 X7 {
- *wechat-5x5-black-chess*
+ t" c) `( l9 q% A - *wechat-5x5-white-chess*))6 `' W0 _; p# ~3 u
- (delete-char 1)))
% o( l( Y' V5 B# M
: j' {1 w: e5 A& G3 M- (defun board-put (y x)
6 n* m2 U1 {1 u1 X6 k8 [& [ - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))6 D; e, n2 i7 Y) u2 Y: D2 E
- (board-toggle (+ y (first dir))
" a5 ~+ j" T3 Y. B# l6 ]$ d - (+ x (second dir)))))% ~2 Q9 z$ \2 W8 r2 q8 ^3 J; o7 h$ ~
3 R' E3 o# R! {2 l& E/ f" } O- (defun game-over-p ()
D- x, K, W' n" V1 ^5 X2 h0 v - (beginning-of-buffer), q W2 x2 {' [
- (not (search-forward (string *wechat-5x5-white-chess*) nil t))); j2 }# Z5 s- G
- 4 N6 i' r- T7 R! G9 Z& P) f
- (defun board-show ()
+ P; N5 o8 |( v- S! a& I X. x; k - (with-board
1 a. F6 Y, b. U* B# G( P$ a, P5 C, W - (concat (buffer-string)
: V8 R0 Y1 J, J' | - (if (game-over-p)8 H5 f) t X6 P2 X; h
- (format "共%d步,输入任意内容返回大厅" (session "step"))% n' ~# j0 S( P
- (format "第%d步" (1+ (session "step")))))))' q. D- }/ T) J! e+ [
& a4 _* I0 x$ ~) j( c: ?% [- (defun board-position-parse (cmd)! @8 E3 P5 @$ J2 @
- (if (= (length cmd) 2)
8 ^. N m8 E) l3 z - (list (string-to-int (substring cmd 0 1))
( X, ]- q! `& D# z& k$ q8 y - (string-to-int (substring cmd 1 2)))
9 D! q: v, W0 B! ?6 U - '(0 0)))
$ Z- v8 J% N* e; c2 F - + s0 w0 J1 ^. R+ y' t$ y
- ;;; 游戏房间+ Q0 r0 W% U. G, p9 r6 w* [
- (defun game-room-init (cmd)
# e: m0 D2 E6 y3 l - (let* ((middle (string-to-int cmd)), K7 W+ r# c- s2 \0 A
- (size (1- (* 2 middle))))
9 K9 u7 X G$ K, q - (with-board" _8 R# Y3 J! j% ~& c( g ?' E
- (board-init size) ^( ]5 W6 q: J; W5 h3 z
- (board-put middle middle)))
; x2 p- f& M$ S0 |) V. R - 'game-room)9 U" Y- n- _( Q: x/ {8 J# R
9 P: J5 {6 n# X- (def-room game-room
- z. i w4 K# i0 a' N$ V/ C) | - #'board-show
! J& `5 @: ]/ Q4 h9 | - (t (lambda (cmd)
+ \: J2 p7 q0 [9 d# d2 @ - (with-board9 m: I I. I: O) s
- (if (game-over-p) W+ N8 J8 F( U( w/ p& c
- 'living-room
5 S1 `. ^+ @. N4 _" }3 y4 N$ U( [6 O - (destructuring-bind (y x) (board-position-parse cmd)6 ]+ u2 V m ^! J6 v" P' {
- (when (board-contains-p y x)
( j1 s9 ?! E3 @$ w) I - (board-toggle y x)+ d- D3 S* ~4 A0 ]& t9 l" W
- (session "step" (1+ (session "step"))))
+ F6 q4 T: C! g! ]$ O$ p `! t1 ` - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|