wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。& n! G9 j& G9 d( D Q
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
" d" m( ~( T- a, H5 ]4 w5 ~6 T& }$ ?- <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( @# E6 @' y& F2 R# f* x& w
- (def-map "/game/5x5.el" ; 对外开放的URL! C* A/ [, o- h
- 'tutorial-room-0) ; 默认的入口6 P9 A' X9 N- g5 T
2 ]+ b/ l/ O* k4 c- ;;; 游戏大厅
2 }% @+ I2 }; f( h - (def-room living-room& z$ Q# p) L6 P% x
- ;; 进入该房间后的提示语7 x2 Y" [$ b! A9 `
- "1. 教程
" l) B# Y3 B& Q5 u( ? - 2. 入门(3x3)
& S( U3 I- A2 q4 [' [ - 3. 初级(5x5)/ D4 F+ |% Q6 H' j# e/ ]; {
- 4. 中级(7x7)2 b0 f& \; B* |/ q0 s' g
- 5. 高级(9x9)
3 J7 R, t8 N* _; U! c - 0. 关于作者5 H4 X( r& n K+ I( b3 |+ b
- 请选择1-5开始新游戏:"% f* S6 G- R+ }/ L# W6 G. `$ }
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
* M. q! y! k# h* L! j+ y2 } - ("1" tutorial-room-0)
7 a" y- K4 n6 v" n# a+ \3 B7 | - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配$ V0 b2 w0 l# `; R
- ; 相应的返回也可以为函数,动态返回房间名
~7 f. P3 A1 J0 Y3 [6 _; ` - (t living-room)) ; 如果条件为t,为永真8 a8 y: L: p# U3 y
- 1 E5 d0 x! B% u Y6 y" D; P" Q
- ;;; 作者信息
5 ^& {3 E$ i/ E2 H - (def-room about-room! L, ~$ b4 w- n! B
- "作者:redraiment
% ^. k* }3 [1 Q! _- v - 微博:http://weibo.com/redraiment9 U S6 m" ^+ F! t1 ]
- 有任何建议,欢迎在微博或微信上联系redraiment。( L+ S5 Q2 g) i9 R4 S
- 请输入任意数字返回游戏大厅。"
4 M. i' S3 f& [* }5 S - (t living-room)), t5 {* r) X# g4 U8 Q
- ; @7 R0 J/ q( |0 y) {
- ;;; 教程
+ h8 R4 J# [9 S& |7 w; ` - (defvar *wechat-5x5-tutorial-rooms* 0
6 ?; e. v! a5 W- z - "The number of tutorial rooms")0 \4 X+ W- u1 ^" I c8 H! W. v. G
- 8 _( Y$ @- f! j- o C# g' ?$ h: _# }, }+ c
- ;;; 简化教程的定义 {. X- T, O( @
- (defun string-last-line (content)) t* O* s5 W! ~( K+ \8 d6 d6 G
- "多行内容组成的字符串中的最后一行"; `& W5 A5 O( J1 Q5 L
- (with-temp-buffer
$ e' {/ i6 W6 G) J$ r2 M - (insert content)- ~% {9 q4 v7 S
- (buffer-substring (line-beginning-position)
! m8 y0 S" {: R; U* T+ g* ` - (point-max))))( |7 Y; K1 u* S
- : E4 s! P# o& Y8 N
- (defun def-tutorial-room (prompt)
! ~+ t' S- R+ O4 z( X2 }( Z& T - "根据提示语自动生成教程房间。' z) U# v5 ~ _' m
* j, j, r7 S; \" ]! B- 1. 提取最后一行作为问题;
$ u1 H' _% E$ Z, b - 2. 分析问题,获取期望用户输入的内容;+ ^; I" J2 {$ ~, Q$ q4 u# E
- 3. 定义教程房间和重复提问房间。", I; ?5 h% O9 K: i3 _; h3 C
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
$ S6 a) A8 C( x& | T - (repeat-room (concat room-name "-repeat"))
" Y- }7 {8 ?) G6 u {: u - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
\: L% T" f9 n; C# C$ p- m" H - (question (string-last-line prompt))
6 X w4 m0 I- x+ m( p - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
7 d+ I. Y; R- {0 P - (match-string 1 question)))
+ J9 p# j/ _' f6 s: V8 e - (doors (if except
1 h6 f' ?' |2 W% l - `((,except ,(intern next-room))
" O9 Z. u1 W3 @) S+ O - ("q" living-room)6 |: ~* Z0 L6 N0 h
- ("Q" living-room)
7 s" f7 r# S; R+ t% y' b - (t ,(intern repeat-room)))# _9 T- q3 s8 [6 T) P
- '((t living-room)))))
9 Q- f% W& j( v) }' ]9 ]" l - (def-room-raw (intern room-name) prompt doors)
5 q! u1 R0 n8 ~8 l2 ~' o' _/ D, w3 r - (def-room-raw (intern repeat-room) question doors)))* a1 J9 O g; a# b' ^( o! N" Q
- 8 G% C5 @+ q0 W" a0 U5 u* x
- (defun def-tutorial (&rest prompts)& K4 U" \3 i x% D
- "批量生成教程房间。"1 ~% _+ \2 G7 M! l+ L f+ G6 V1 P
- (dolist (prompt prompts)
- I# S; q# [# S1 p; R- o: w - (def-tutorial-room prompt)))
- V5 q' B+ z0 h+ @ - % x$ S3 y3 |% D7 u5 ^
- (def-tutorial
5 b/ g }: ]7 v* E - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
2 Z5 {% L" M- x, K+ K& E - 1. 教程
$ ]* e4 W4 E0 E7 B7 G/ e - 2. 入门(3x3)% T4 Y4 c5 m3 U2 Z; W& q4 z" q
- 3. 初级(5x5)0 C z; a/ ` L; ?7 _, h1 P
- 4. 中级(7x7)
0 W8 k' k a4 i) k+ V4 I - 5. 高级(9x9)
W/ @% Y0 @6 a) Q - 0. 关于作者
" r3 c1 O% z$ S3 M+ T4 s7 n0 L - 请选择1-5开始新游戏:, j. ?1 r9 l+ m& K7 J/ B
- 您现在正在游戏大厅里。
$ w7 P5 N3 s8 E8 p' @. U - 请输入“2”进入入门级房间"' E1 {2 H! J( K. q# M& }' A/ i
- " ①②③
/ Q3 }, z8 |, K5 c% k- ? - 1 ■ b( I: L5 K/ x. D
- 2■■■
& n m# g! _3 K3 { - 3 ■ - b' f8 h3 A$ p+ u6 E, Y
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
' ^8 ~- w8 T9 u% c, u# G3 W0 V9 y - 请输入“22”来关闭第2行第2列的窗户。"
/ u2 F# `# P, G; u5 G; T/ X - " ①②③
$ I5 c! I; z4 z2 V - 1 5 F: K. }( F+ ]- T3 n9 c
- 2 ' D) s* E( E6 B, O' y
- 3 8 e i' {" s" g6 g% f/ c
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。! X3 Z$ E( Y" Z( p+ q
- 请输入“11”,试着开启左上角的窗户。"
* S" y) r7 d, m% ~6 A$ m# q, t - " ①②③0 R8 K$ G2 E8 h1 y7 V5 D# E- g' C2 T' j
- 1■■
2 C& J; {: y2 L, s* h/ H - 2■
4 ^. L' B. M1 p9 ^2 c8 A* M; q - 3 * E0 Q; g. i7 V
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。5 ^% }1 S: ~/ d2 b0 b
- 请输入“13”开启右上角的窗户。"
6 c% A' L! l/ e4 K. ~3 C& j9 P - " ①②③
3 N2 c1 I; {- d( @+ T, \ - 1■ ■! O! Y; g5 h3 H
- 2■ ■$ ~3 U( q; G6 g, w# ]: v! _9 O
- 3 8 m; W/ O, [: z* c9 E
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
" y5 |: d# w' A! z) D+ S - 请输入“31”开启左下角的窗户。"( h3 _. h; F5 m4 {+ l; z( w4 l
- " ①②③$ [: }' f& [) e7 P6 w" t
- 1■ ■
. Z0 @( A, h" e$ ?% a - 2 ■1 ~# [0 h" {% Z, i8 E. J
- 3■■ 0 Y$ g: t/ g9 a& G/ K( ]) `3 A
- 此时,总共有5扇窗户被开启了。
) O; V% M! j/ z9 }' }; S+ h$ J - 请输入“33”开启右下角的窗户。"& ?$ p) |2 z+ a8 x7 C8 I0 V
- " ①②③5 X' i5 v& h2 h& S w7 A
- 1■ ■; d" d6 t) ]4 T+ V* G
- 2
% b: ` y/ j/ J, g7 Q0 s - 3■ ■2 p' T+ F4 v" i% @# x. C# i9 @
- 现在,只有四个角落的窗户被打开。
4 `% a1 m# {# e: t; O - 请输入“22”完成最后一击!"9 E+ c4 C2 h$ [+ z: P+ t( K
- " ①②③% y1 k6 y5 A# J5 d5 c
- 1■■■
/ Z4 B6 |5 `, u7 y" z - 2■■■
2 f" b: j- e' T - 3■■■4 X0 H( O( x4 j n5 P3 F
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")# j T& {% W4 |* a
- 0 x& z* `" ?+ a! I' r+ o1 ]- E
- ;;; 棋盘- j% `; j3 B4 S4 q3 w6 b
- (defconst *wechat-5x5-white-chess* 12288
1 w0 m( m* ^5 c7 x - " ")$ a6 [9 _) w1 p9 Z3 l1 u% n
2 Y8 }7 b' w4 L* k! r9 X/ s& t0 r- (defconst *wechat-5x5-black-chess* 96328 O6 c7 a: z) W! W. z
- "■")
* K' }; Y# Y9 R. b1 ~( ]
* g0 w% ^$ H1 h, i* J4 M% K3 a' B9 c- (defmacro with-board (&rest body)
( }9 c0 o* _2 a4 ]) K - `(with-temp-buffer( L/ [. d( L2 J
- (unwind-protect/ J- Q* V# _* D: @5 U- ?$ Q8 p5 {% p
- (progn, V. h3 X1 `- `$ a4 s
- (if (session "board")
. H) ? r8 Q' |, g7 R% o1 H7 r - (insert (session "board")))# O, r8 J* Y! W% A3 a+ ?! r/ f
- ,@body)
& T \; ~ Z8 f, Q8 P - (session "board" (buffer-string)))))
- X7 I: A* W, ^' R' B1 O3 k8 u: P
9 G( c0 a" u- x1 {- (defun board-init (size)
) _0 Z* P: v% L: L - (session "size" size)
/ X. S! @8 g% k# i6 n( a - (session "step" 0)
+ B( C+ L7 [+ q% ~6 Z5 J - (erase-buffer)
' x: J: o7 W- P9 } g - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
, ?( z4 s5 ]( a8 _+ Y - (dotimes (row size)
0 m8 v. [! u5 U* l' V6 ` - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*))))): N6 b2 s9 C6 ?- g9 j
- + v2 m# b: i( [ ]/ K4 t. I
- (defun board-contains-p (y x) c# ^ S( d6 N$ d' V
- (let ((size (session "size")))
+ X+ Q7 V }0 ^3 H, Y4 r; H - (and (<= 1 y) (<= y size)( h5 J3 }5 e( d# A0 t
- (<= 1 x) (<= x size))))
7 H @, I0 f$ N - $ U3 t& D7 S. S0 n7 D9 Z* o
- (defun board-toggle (y x). b; \/ M. ]" p) y; Q' i. A' X4 Y) e6 X
- (when (board-contains-p y x)
" t7 S- `$ n1 w# B, U% Q$ h - (goto-line (1+ y))' J$ e9 O7 }, v) L
- (beginning-of-line)
3 q0 i0 i) H! b3 w! |9 S - (forward-char x)
) v8 Z* t. d, I) r5 I' w0 b/ i0 Y - (insert (if (= *wechat-5x5-white-chess* (following-char))
v6 L# l' \" k. q, F4 @1 i - *wechat-5x5-black-chess*
}' N- X ?$ N9 [# h/ ? - *wechat-5x5-white-chess*)) A* g4 d6 L( l3 j
- (delete-char 1)))
7 R4 k3 P* h6 Q& S2 j; E, ] - 5 V1 T$ l& J0 r4 u! B! o6 A
- (defun board-put (y x)
$ w4 d6 t$ f' C7 N1 i9 l: t% N - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))! K& b: |2 e" f( P: ~. w
- (board-toggle (+ y (first dir))# ~" a1 n. w" Y3 V2 e/ g' e# g
- (+ x (second dir)))))
! Y" d: H+ ]* r z$ V. }* k - % f6 [- I [6 R+ v/ m* _4 J
- (defun game-over-p ()
. V! T- n2 e T" ]9 x N$ D, ^" N - (beginning-of-buffer)% w" i/ F% `1 \ ^, p1 G
- (not (search-forward (string *wechat-5x5-white-chess*) nil t))): E5 X( L9 s: t3 |2 d1 \* S
- ]3 D3 {5 N7 q0 a z6 d- (defun board-show ()6 [0 b$ d8 u( |) {- p) u
- (with-board! R$ ~5 L; e' ?. z! y7 X
- (concat (buffer-string)) \- {: i$ d% K! j/ a
- (if (game-over-p)& A/ D' G* S) L3 o7 V/ _
- (format "共%d步,输入任意内容返回大厅" (session "step"))0 `; k3 K- D8 n: u
- (format "第%d步" (1+ (session "step"))))))): a) C" `" x: q t9 z9 d* `7 K9 Z
$ ^$ K# m( i7 l- (defun board-position-parse (cmd)
8 J8 Y$ b9 ^. A2 v; }; @& @" k. R) S - (if (= (length cmd) 2)
9 g9 a% U4 J: y+ Z5 n - (list (string-to-int (substring cmd 0 1))
4 \: c/ k( d. g0 t - (string-to-int (substring cmd 1 2)))4 J: G7 O4 G7 o. L; \6 E. b. n: x
- '(0 0)))
% x" [) K, {* G) n0 Q# ^4 W - 4 r) H, O4 U: h' O s6 p* i
- ;;; 游戏房间
& j7 H9 Z3 I9 |& ~# J7 \$ b - (defun game-room-init (cmd)
" e$ A$ g! X3 ]+ @8 T$ O/ b# @) J - (let* ((middle (string-to-int cmd))/ |! V' ]' T7 D. |9 w0 g5 b9 \- h
- (size (1- (* 2 middle))))# r9 g* F+ E. e$ P/ L
- (with-board$ E) D5 E. B4 s! H f
- (board-init size)% _! ]" W4 O8 R; {- f2 K
- (board-put middle middle)))$ S5 ]/ S* b% r& {
- 'game-room)( Q# l0 S# l, b
* [/ _* g# o* Y0 s$ a- (def-room game-room5 c* t$ R+ F* N
- #'board-show( Y; f. c4 D i- r9 z0 U: X! ^
- (t (lambda (cmd)/ w4 u% u- T5 O2 p& I
- (with-board
; [3 y: x. S4 |$ S) x - (if (game-over-p)5 L4 Z# o" Q) p$ o. ~/ o
- 'living-room; M) g$ I* |# w4 e) X+ Q
- (destructuring-bind (y x) (board-position-parse cmd) S h8 l- ]! S3 _1 T# i
- (when (board-contains-p y x)
0 ]! }2 t0 X+ z8 A; W' K - (board-toggle y x)' o* w- q! W" m! r# ^. J
- (session "step" (1+ (session "step"))))7 X" ~- }0 K' m( t8 G* ?5 @
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|