wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。; v0 y; z8 L/ I
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
, h* s/ [+ E# z1 K" x- <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: P, B$ Y; y+ t
- (def-map "/game/5x5.el" ; 对外开放的URL8 A* j+ j* L( Y9 c- g) K2 V
- 'tutorial-room-0) ; 默认的入口
. h* H' }/ D6 X0 H" B: P; o- s4 ~
; A o! j( M" I& o- ;;; 游戏大厅% w+ N, w5 s" x' c+ u! ]
- (def-room living-room, C: G' Q/ @2 N T2 W- x$ a
- ;; 进入该房间后的提示语
3 d9 A5 S; s6 ?7 [% G - "1. 教程
( w- Q# E" l' f! C - 2. 入门(3x3)
- Y8 X9 Y. _$ f - 3. 初级(5x5)
. G% I$ P$ q0 C7 z/ d6 D - 4. 中级(7x7)/ I! i$ m! a' |, D6 F, H- ~6 b
- 5. 高级(9x9), _: Q* b+ j4 A
- 0. 关于作者! w0 P0 i( ~! I- V8 U
- 请选择1-5开始新游戏:"
' w+ z4 [8 J: d - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
) m" W2 |9 h- G9 v" O3 H' u - ("1" tutorial-room-0)
; u- _+ m2 N! A; B) z/ \: Z - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
- Q9 `- h8 |* m! k4 A: y - ; 相应的返回也可以为函数,动态返回房间名1 N* ~* w, ^& Y; C5 g% V
- (t living-room)) ; 如果条件为t,为永真
# b, d1 S; T1 }( A- r# | - ; E, |- W6 S8 I/ h% K
- ;;; 作者信息; M. W' W j& K- {( a, Z! s
- (def-room about-room
% @ @7 c5 V; s1 i8 L& R - "作者:redraiment
$ L1 b( L7 z1 C$ B4 Q+ b. S Q - 微博:http://weibo.com/redraiment% ]% Z) b) B" h% l2 X# h( }
- 有任何建议,欢迎在微博或微信上联系redraiment。. d$ |# w2 t9 h/ `' C1 j) E# n' R
- 请输入任意数字返回游戏大厅。"
1 o, {9 }: |2 T' }- f - (t living-room))
8 E/ e& ~& K6 [8 W; B2 K - 6 v- z# N8 C8 p6 S9 j8 Z9 S
- ;;; 教程
! U3 p; B# J1 o( c# \ - (defvar *wechat-5x5-tutorial-rooms* 0
* N7 M. U# `: A/ W; M - "The number of tutorial rooms")
8 M+ |, t# h9 O$ l! v. E - ) f. f3 z7 a7 i- Y, d( |7 g2 o
- ;;; 简化教程的定义: M8 T1 y, U# r+ x
- (defun string-last-line (content)
1 y8 L$ N" ~! b# [: z& k - "多行内容组成的字符串中的最后一行"
. ]9 y$ r4 X) v% w- c W6 v+ |( N) M - (with-temp-buffer
( A1 L! Z, x! L - (insert content)+ V0 y/ S$ i, \& _& `+ P5 i
- (buffer-substring (line-beginning-position), O% ~( k( }, O+ \
- (point-max))))# p2 q' j7 L( z& _( U& Q# T
5 p: M( R! a0 c- (defun def-tutorial-room (prompt)# M! ?" b7 x2 K4 b: v
- "根据提示语自动生成教程房间。
7 X5 k7 d, c; S7 c* E8 M; a, {* l - , x, F+ y4 ]( h2 O* [( i
- 1. 提取最后一行作为问题;/ q: X+ D" [( B6 [+ x% J( e
- 2. 分析问题,获取期望用户输入的内容;1 |0 r; `; h& l3 t% [8 c4 Y
- 3. 定义教程房间和重复提问房间。"9 Q& M0 e* D% I* S1 z& K7 D
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
' f" x3 n2 W+ J - (repeat-room (concat room-name "-repeat"))& C q4 ~2 @* Y" n+ F2 T- |
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
* |/ w& W" E9 l7 T, L( `( n - (question (string-last-line prompt))
" `& `9 ~7 O2 @ - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
8 ^: r+ Y3 Y2 d1 x: U - (match-string 1 question)))
5 n. C# l- I ]3 Z7 B9 m8 Z# U - (doors (if except7 H1 K) z& r! v3 B+ ^) J
- `((,except ,(intern next-room))
5 M, @& k3 ~6 X9 a% g - ("q" living-room)
( ^4 Z, j1 a5 H E. J) s+ r - ("Q" living-room)
* O$ P! P+ H+ E [' y N - (t ,(intern repeat-room)))* S; }& U/ h: n9 y
- '((t living-room)))))
2 x& r. ?' v8 `2 ?& N - (def-room-raw (intern room-name) prompt doors)
7 K* b! L$ B+ A - (def-room-raw (intern repeat-room) question doors))), Z0 o* Z" G% p# P; c) m. Y/ z! i
9 {. A$ h- X- P b: i4 m- (defun def-tutorial (&rest prompts)
9 l% `- U8 W6 X! Q3 z0 O3 W- F! C1 k6 N - "批量生成教程房间。"
1 Z# H- d# H! ^2 K7 T# ~ - (dolist (prompt prompts)
9 k. A8 y9 F" } q) S/ K: ^ - (def-tutorial-room prompt)))
% A2 P6 j; A9 Q n: k. _; G% l# Q
5 A5 D# a* p6 u8 f w+ K- (def-tutorial
% [" P( M+ d& [/ K. Q2 p - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。! M7 B; T. u( x# L# B7 t* h
- 1. 教程 c! ?! c0 u9 `& e8 {! c% @0 H8 i
- 2. 入门(3x3)
S# W7 d$ i9 L8 Q4 a - 3. 初级(5x5)
% p1 g3 J7 {8 E% Q# _7 G4 | - 4. 中级(7x7)
6 w- a8 Q) U& g4 u S - 5. 高级(9x9)
- F8 m3 D' ?" B5 ~: J+ F' w! G4 ^6 } - 0. 关于作者9 \% d( u4 b2 J L
- 请选择1-5开始新游戏:7 V7 n0 m$ E) l8 B" P
- 您现在正在游戏大厅里。+ F( v9 X# ?; j( C9 W9 W2 y
- 请输入“2”进入入门级房间"
3 ^* z6 H5 E* q/ N" s9 s8 S - " ①②③, W) P" V/ J6 {) K: `+ e0 \7 E
- 1 ■
0 I+ E+ `% k4 L$ d - 2■■■
1 H8 _/ `3 x+ X) a - 3 ■
! @1 A3 E: e4 @' R8 D' r - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!# J6 {0 b9 T8 o; C: z$ U) m/ ~
- 请输入“22”来关闭第2行第2列的窗户。"
: G) T; S: ?( j3 h - " ①②③7 U* V/ t8 T3 z% m k
- 1
: w! `/ V/ s1 ]* M8 \ - 2 ' H& x) w1 C: w! i, F
- 3
8 n+ ?" v f3 {4 v/ @ - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。' ?1 @* K6 u0 f0 C
- 请输入“11”,试着开启左上角的窗户。"
4 t4 j0 S* N$ z6 X. ]/ a5 _) z - " ①②③( @! C) G9 ?. k& I, ?) f$ V* J& j
- 1■■
P% S. Z1 h, {) k7 J" z - 2■ * Z2 l& f1 P) H
- 3
% V; o, s4 |9 Q - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。% }4 }7 U% N# u
- 请输入“13”开启右上角的窗户。"* d. |' ^; Z& w& p1 A
- " ①②③
% K6 r8 J. c" f; h( o/ g - 1■ ■) |) l" a B. b4 V1 O) n3 y
- 2■ ■2 I H6 e* z0 Q, B6 |5 f6 ]
- 3
8 n5 i* Q2 C# x9 \ - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
* W# n* f! r9 f" x2 S' ?) P - 请输入“31”开启左下角的窗户。"' v+ n4 r `( a" I
- " ①②③1 x, u5 [) c' D5 f `7 a! n# W
- 1■ ■
2 a) o" w7 O" j - 2 ■: M9 G) K$ o6 b, v. [
- 3■■
- k/ s7 @) n6 W! l$ B" J) O - 此时,总共有5扇窗户被开启了。( ~/ B% u# q$ E5 P2 H' W5 M
- 请输入“33”开启右下角的窗户。"
( h2 Y/ @9 R8 c6 k4 P0 i } - " ①②③' J! o# w$ \& _- t& x
- 1■ ■' F A: m+ h Z. i' x7 O
- 2
6 b+ s7 w; C) m% g2 Q' h - 3■ ■6 d' b5 c- ?/ s3 a7 w6 s
- 现在,只有四个角落的窗户被打开。
. M2 P( _: L {0 s - 请输入“22”完成最后一击!"3 J$ B% d2 C; [4 v2 {6 ^$ {
- " ①②③
- f7 M7 j8 A+ ~* J - 1■■■
' _/ X0 h+ W+ h5 ]3 q/ _; G8 R0 ` - 2■■■
9 o2 A" t, [7 Q. ~% J - 3■■■* g* g/ X+ ?8 V# c& D
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
4 k+ }- T+ P- S F* t+ y
3 g: ]. G. ^# ?8 L% [- ;;; 棋盘% s C M7 }( \+ u
- (defconst *wechat-5x5-white-chess* 12288 Q3 f7 f# t5 v2 p# l
- " ")
0 J1 @9 d% [1 P( b; e - 9 U* j! T/ x; H- e% [" y
- (defconst *wechat-5x5-black-chess* 9632
% Y0 |% m" R( g4 C! @; G - "■")* T8 U. Z3 u* L) ?0 P: i
- % `, }1 ^* }6 X2 c" m
- (defmacro with-board (&rest body)
1 T9 @: U- j. } - `(with-temp-buffer
) Z% H' D0 m# @. t - (unwind-protect
0 Y: d' P* E* ~% ?: N$ o, E - (progn" U( F! p/ j2 [. H/ [
- (if (session "board")
; R+ R4 r0 p7 Z5 p# h- c - (insert (session "board"))); ~* m% W/ S7 P3 s. b) K
- ,@body)
! C) Y2 D9 g) p! A - (session "board" (buffer-string)))))
7 P8 C' K9 R3 w0 T
' |- q9 Z0 E& u3 \/ O+ P- (defun board-init (size)# ?* }6 t8 I4 l2 n3 N% Z7 L: @& H
- (session "size" size)% E: K2 v) {5 R' K* m- [, T$ t
- (session "step" 0)
5 K- m. C* V0 W% V - (erase-buffer)
, @( K# |2 q; }: K# _) |" j - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
1 b9 K) z* a+ B. X+ m0 E - (dotimes (row size)
1 R& |6 U( i5 `# i$ L - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
! g- h f# s+ G. R- V* A
; J4 }! ]2 @' w' V! t- (defun board-contains-p (y x)+ s F2 S1 ~& N% Q5 N4 h
- (let ((size (session "size")))4 X/ Y1 M4 k4 n9 w4 V3 z
- (and (<= 1 y) (<= y size)( l: b# ^( C1 n3 g, R
- (<= 1 x) (<= x size))))
. Z7 u& b6 r6 ~1 [ \4 Y - ; t y% e$ o' d8 V* a) t& h* r
- (defun board-toggle (y x)
) n1 x0 [+ C# ~: L, | - (when (board-contains-p y x)# n" a( O# x+ J D7 a
- (goto-line (1+ y))
9 ^/ E: e1 @8 j5 V" N1 d - (beginning-of-line)
# w0 J, K$ F7 g% L2 f! U) o - (forward-char x)5 x9 C: b z0 M/ t
- (insert (if (= *wechat-5x5-white-chess* (following-char))6 D* i7 u4 t* J
- *wechat-5x5-black-chess*5 t% Z" W! s/ J& B# |6 d. y' k
- *wechat-5x5-white-chess*))+ S0 L% Y$ {) ~, |$ u# A2 p P, h
- (delete-char 1)))
4 w4 \$ c: T9 O) S+ t
& v o2 P4 m; d. Q2 g- (defun board-put (y x)5 q/ o* U* ~; d5 F
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
6 W2 P# S3 c4 ]0 `1 N - (board-toggle (+ y (first dir))
7 v7 L( E2 r/ ^# s - (+ x (second dir)))))
$ o( G* j c2 k$ O x - ! _# w& s1 ~5 c
- (defun game-over-p ()
! T7 X) x0 D4 @ r8 G" v - (beginning-of-buffer)
% w! D1 Q/ \9 c - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))( B" F# q- y1 s
- ~' W" g. U1 R( `/ ] W' D# ?/ W
- (defun board-show (). t. }2 g2 S, u9 g5 i/ W. G4 j
- (with-board6 }6 S; K/ C8 v6 V: G( e% D0 |
- (concat (buffer-string)+ S+ F. A- t$ P4 s
- (if (game-over-p): q/ I% [0 Q H. [
- (format "共%d步,输入任意内容返回大厅" (session "step"))
+ J/ @' L6 a3 M* f: y, c - (format "第%d步" (1+ (session "step")))))))7 z% J' K/ W0 X% |0 K6 i
3 c6 W) P) _3 P* ^, c) p, \- (defun board-position-parse (cmd)
5 S1 w) d# Y5 Q; @ - (if (= (length cmd) 2)! b& [ ~: o' x
- (list (string-to-int (substring cmd 0 1))
" z6 E$ A) t2 g - (string-to-int (substring cmd 1 2)))
3 @: M. {3 w- P1 d' s - '(0 0)))
/ b; h" o7 J& h' p2 T% F' w
1 `4 x( p. f: Z# P% e1 |5 X& S- ;;; 游戏房间1 y5 {: E# O$ C/ h( B, W
- (defun game-room-init (cmd)
' I7 x, ~. a, p6 H! i9 P - (let* ((middle (string-to-int cmd))
) Q4 i1 r' ^) y- ^' _# u - (size (1- (* 2 middle))))
: p( J' b/ x) F - (with-board+ z& v) g6 Q6 k3 m5 L
- (board-init size)
, s) A* ~( |8 ]- E - (board-put middle middle)))8 a) w( d4 R- |8 y* ^5 X7 g& @
- 'game-room)+ h8 u% e2 ?1 o
- . q. ^* K$ ^, Q& O
- (def-room game-room
6 z$ b! x6 S. C) L6 o, \ - #'board-show( `/ u% l/ g9 {: r$ m
- (t (lambda (cmd), r1 B* R/ w, m+ b8 j! `! S7 f4 @
- (with-board
( D+ H ]; ^6 b - (if (game-over-p)
: r2 S' y d9 t - 'living-room2 |, i+ T- J/ m9 }- J
- (destructuring-bind (y x) (board-position-parse cmd)$ B' ]( N; ]& g) X
- (when (board-contains-p y x)
a' F1 v: R" Y, M5 B - (board-toggle y x)) p" Q$ F6 g# v6 G9 L
- (session "step" (1+ (session "step"))))
0 H5 ]6 E3 k" n# V - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|