wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
" u/ ^, h& y2 l& y4 T! Z4 ^& O借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- 4 B1 c0 n! ^" x7 e
- <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;">;; 定义新的游戏地图# e3 c1 n: U( d4 Y% W/ |) R" J6 G
- (def-map "/game/5x5.el" ; 对外开放的URL& Z$ t$ i5 ? v- s& G
- 'tutorial-room-0) ; 默认的入口
4 K9 E$ u- Q: P" z - ! X7 N& s3 \. m+ a( R
- ;;; 游戏大厅" `2 L8 ]1 R1 b4 W
- (def-room living-room7 R: s3 z" @% R; y0 Y) x
- ;; 进入该房间后的提示语3 X: k v) I7 b) l3 y% v! |1 v
- "1. 教程
# k- @" V, m5 Y, T8 B: i$ s) k; t% _ - 2. 入门(3x3)
& ?3 {4 L/ c0 [5 J; Z" M' J# r - 3. 初级(5x5)# c2 A! L: _3 C8 ]
- 4. 中级(7x7)% m9 X# C3 O; ~' Y0 m! g
- 5. 高级(9x9)
; c! T" K: X' L! w - 0. 关于作者
, O w1 z2 Y; _+ D) W - 请选择1-5开始新游戏:"
& k4 J* k3 L0 y( |& t - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名( l8 J0 f0 @6 `$ O4 q( Y
- ("1" tutorial-room-0)! W- x2 m8 Y2 l2 g. ?# h$ R
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
$ N7 {. ]! T2 ~1 D - ; 相应的返回也可以为函数,动态返回房间名2 q1 y; s1 n5 R' C1 @; |
- (t living-room)) ; 如果条件为t,为永真
* F# j+ w6 B4 o) ~5 I5 G
: `" G, S- z+ V' h( S/ w- ;;; 作者信息- @, L T1 d; W0 _
- (def-room about-room
! y2 x/ K2 t8 K2 F. _+ T - "作者:redraiment0 S: S1 R8 d9 y; S6 J
- 微博:http://weibo.com/redraiment
8 v- h$ h1 K9 i- J2 V1 L+ |) I/ Y& O - 有任何建议,欢迎在微博或微信上联系redraiment。) k6 k* g) J0 E2 o9 f! @
- 请输入任意数字返回游戏大厅。"
9 R4 T6 p: ^6 U3 H - (t living-room))
- ~2 \; f) D) |# Q" G - 7 S# F5 w: Y( O" Y6 g! e- P) S
- ;;; 教程
" W% Q' Z# b" a% i - (defvar *wechat-5x5-tutorial-rooms* 0
0 _6 _! c3 D+ i - "The number of tutorial rooms")1 @) I( @* K& w, t
- # e, e# V# K- z( r9 N' {
- ;;; 简化教程的定义
6 q$ D6 W% x8 [# H. ] - (defun string-last-line (content)
6 j+ {- B% i; o. O; c - "多行内容组成的字符串中的最后一行"9 H7 E R! b/ X: w' H, e: F
- (with-temp-buffer
0 Q3 I$ N \ y3 g - (insert content)9 J0 }) K/ r6 G X9 G
- (buffer-substring (line-beginning-position)4 N7 Z3 @, q9 n( j7 @
- (point-max))))" ~8 v }; a2 F& y& [8 y
$ @: j. F% {/ ~5 g- (defun def-tutorial-room (prompt)* T2 k; i% S4 E# U' }
- "根据提示语自动生成教程房间。& M! E9 c+ ?) ]) ]+ j4 d6 |
+ t6 E( Y, C( V% H% c( \$ S3 Z- 1. 提取最后一行作为问题;
& y& s9 }& m- w& D) m4 ?) e6 j2 U; w - 2. 分析问题,获取期望用户输入的内容;
' v0 p1 ]) r/ g! Q4 D# r - 3. 定义教程房间和重复提问房间。"
E. _$ h. s' a/ D4 a - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
0 x9 u% ~, Z) K( u/ i - (repeat-room (concat room-name "-repeat"))
, ^/ H" e) ~ u; ~$ t* E. a - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))# H L- x2 @" D- P$ W3 \
- (question (string-last-line prompt))+ u0 |. }; B5 w V$ B5 {
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
& M A. t- Y: A& O9 h* u - (match-string 1 question)))
& s$ G+ M2 r1 p6 h - (doors (if except$ i* e& Q7 @2 l5 B
- `((,except ,(intern next-room))
5 E, U& A& v& m" k - ("q" living-room): S7 x5 }# ]$ m- C: f) V3 V
- ("Q" living-room) N8 h o" b$ l1 W9 i9 @
- (t ,(intern repeat-room)))7 ~: p$ W1 P" p8 b
- '((t living-room)))))
q" J, b8 q, ]2 G0 N$ ] - (def-room-raw (intern room-name) prompt doors)9 Z0 }5 Q" j- _
- (def-room-raw (intern repeat-room) question doors)))
! g* S/ g- w. h; C8 H9 f - 1 F! a9 y7 n- k) [9 b9 K
- (defun def-tutorial (&rest prompts)0 _0 Y" i# }7 t" a. ?0 i
- "批量生成教程房间。"
1 M3 W# C$ ^' N2 D - (dolist (prompt prompts)
0 C1 k1 S. [1 W$ M - (def-tutorial-room prompt)))) [$ e: t$ H$ j9 R3 z
- ( E" @% f& l8 C% ^
- (def-tutorial
e Z) H, J! x - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
$ f% O# m7 e9 z - 1. 教程* }% S, I6 Z7 D4 F
- 2. 入门(3x3). ^# S7 h S" d7 |+ J* v
- 3. 初级(5x5)
- F, _/ F3 o# t( C$ A - 4. 中级(7x7)! J! j; @1 a: N
- 5. 高级(9x9)3 j( w7 Z' B$ Q
- 0. 关于作者7 F+ _: }; d- `' m( A+ X' N7 J7 y# x
- 请选择1-5开始新游戏:+ X9 Z- Q( ]! v2 a: k* r
- 您现在正在游戏大厅里。6 `2 p2 W% S. u9 K$ I: n, o
- 请输入“2”进入入门级房间"! k7 u6 k' x& H; i# p' ]2 g9 y7 S
- " ①②③
# R, \; F8 h; ]& k4 T) e - 1 ■
8 ?- g$ W* E. D( d$ O - 2■■■
/ M5 G: L! a+ x% n; } - 3 ■ . S: g2 b a3 @+ G9 V
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户! i6 D3 w9 n0 M# i% ]
- 请输入“22”来关闭第2行第2列的窗户。"
6 t% {0 {) O( I- O( o# V, h8 E* G) G - " ①②③
( [1 ~/ Y! ?# }# L) U - 1
9 _3 C$ Q9 \/ x, W0 i - 2
4 w$ _ Z c* d - 3
" w7 E" y" ^/ I7 ^4 h* o - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。3 j' Y( f! d }
- 请输入“11”,试着开启左上角的窗户。", U* w" @" H. W" c$ I6 m
- " ①②③- z4 n4 M( V# N- o1 w
- 1■■ ' f9 A% [! h) e
- 2■
( G- f& y8 E3 ~* n - 3
$ d4 s* S2 v$ o" Q; n | - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。" i8 }' ?0 r* h9 h( m, ^& m: `) u% c) E
- 请输入“13”开启右上角的窗户。"
1 X9 n# v \* @9 u7 ^) p5 R4 } - " ①②③( S3 L1 E" V K1 c3 F& |, O0 r
- 1■ ■
4 N9 R' }4 X6 M - 2■ ■+ j, J5 H3 o& ~* @
- 3 / a$ q7 Z! V, x0 C: z j' E3 S
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
% C" A# y2 O$ O3 W4 ~ q - 请输入“31”开启左下角的窗户。"6 O; r- X F$ L* |
- " ①②③5 X2 D9 [# O8 g$ |. m. X
- 1■ ■) ?$ q% C9 i* Q: |, o5 ~
- 2 ■
- H, k, f; Z9 I+ s6 J$ U! ` - 3■■ 6 j: O( A. r& f3 X
- 此时,总共有5扇窗户被开启了。
2 N5 f1 w E' k: n" u - 请输入“33”开启右下角的窗户。"5 `& L, a1 }( k! V' M) x2 E+ C0 u' o
- " ①②③
& M9 W( W; `1 H6 d. O - 1■ ■
4 m( O. a# L0 v0 F$ ? - 2 ' L+ H2 {' t. `! F6 ?
- 3■ ■
# L+ J$ F _+ K) b7 X7 i - 现在,只有四个角落的窗户被打开。6 ^* a1 d2 u4 X D' M( Q5 s: {$ I
- 请输入“22”完成最后一击!"+ g. p) v2 d3 u( t$ N
- " ①②③2 h W* U2 ~- G, g+ g
- 1■■■9 ]0 C% {8 `6 ]( K
- 2■■■
+ o3 |; I5 {6 B9 F$ H - 3■■■- ]7 P' _/ B! R& \3 M1 B
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")8 |# ?/ z( x7 y1 X1 w# v
- 1 I4 A \3 \' u& e; G& a4 {) g1 ]
- ;;; 棋盘. q( `; J2 U8 k2 E/ [2 B
- (defconst *wechat-5x5-white-chess* 12288
7 Y% y; @% B7 m1 S - " ")
. i. Z6 b9 T' G! A - : Y1 {. Q# D% v3 p* g) r* O# Y7 _
- (defconst *wechat-5x5-black-chess* 9632
2 j, v6 i- Z v& g# A - "■")# d) _8 S# y3 M# n8 R1 ^3 a
- . a% B. I% Y1 B3 \% l& k
- (defmacro with-board (&rest body)# J7 N; x d# c& F
- `(with-temp-buffer
b' D6 p0 j3 f: x7 ~ - (unwind-protect
4 b0 ` Z6 X d2 t, |: I% X - (progn
5 J% A* L# p% f/ `, D% e4 P - (if (session "board")$ R5 y2 G& S- j5 {7 U
- (insert (session "board")))
$ _) ]* ^% `) z9 h - ,@body)$ {: u8 M& Z' _
- (session "board" (buffer-string))))): ~% I- x$ j# _/ I9 ^
5 X# j! @) R8 C! `* N- (defun board-init (size), L( Y. W" x9 h
- (session "size" size)+ T7 @1 G$ X( z! K1 h7 I! Y
- (session "step" 0)4 h, z0 t3 V5 G D: _* x2 N
- (erase-buffer)
; H4 `5 o L1 J; B. V" h a - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))7 U z) U% g% ~) H! g
- (dotimes (row size)7 Y* u: W/ h0 K; _* k$ a# z
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
7 ~# t1 Q! s' ]; K; h1 W- g
6 M5 i( I7 }" p( C$ ]- (defun board-contains-p (y x)4 j: r5 [" N$ ]/ r1 ]; d0 R0 s
- (let ((size (session "size")))
; p# L% n$ G+ I7 N9 N% w - (and (<= 1 y) (<= y size)
) ?* e; Z/ W( {' P4 k4 ~( ~ - (<= 1 x) (<= x size))))
; h4 M: K2 Z5 P9 ]
5 @+ B F+ i/ E# A3 Q- (defun board-toggle (y x)
7 x) r5 H/ C' z s$ H - (when (board-contains-p y x)
7 X7 K+ b# r* b* ~. ]8 w - (goto-line (1+ y))
$ M4 g3 i$ x2 M! Q- I% { - (beginning-of-line)
Q( V3 _( ^# p% Q) k; d8 S/ f - (forward-char x)
, N; s H: Y; ] - (insert (if (= *wechat-5x5-white-chess* (following-char))
3 |6 w1 J( R7 M0 k7 Y - *wechat-5x5-black-chess*
! N; N/ _4 c, K) M0 G- y6 g( i - *wechat-5x5-white-chess*)) _3 A! I k1 Q/ e6 a
- (delete-char 1)))7 N2 N" p. w y# @' l) E, Y
7 Z$ _/ t6 Z6 t( [: ~- (defun board-put (y x)
4 O7 P" V$ _" |0 l8 h - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
6 v- w, v5 b2 M3 r - (board-toggle (+ y (first dir))
2 P+ s# S- v$ S5 h3 u - (+ x (second dir)))))
& Z2 r1 i8 ~7 D$ l' K
: A6 t+ B& g: \, |- (defun game-over-p ()7 i3 E5 ^4 }: a7 c
- (beginning-of-buffer)
+ ]1 k+ |* `/ C - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
" W. n0 k- v8 c l8 M. | a+ g
, A5 Q6 p" O; u7 K- (defun board-show ()) o- Y. [) C# ^0 Z
- (with-board
' c) r: i" [2 P+ b0 } - (concat (buffer-string)9 {, g2 i4 X) j. Y& P, L
- (if (game-over-p)
4 F8 n, H- r& a" M - (format "共%d步,输入任意内容返回大厅" (session "step"))0 A8 W6 I7 g+ ^' h! D
- (format "第%d步" (1+ (session "step"))))))) L& @ p$ y' w! m, L/ Z5 ?
- ! b2 i1 N- Q3 _& g' h/ _6 u7 w
- (defun board-position-parse (cmd)
2 b3 n0 [) v, K8 {5 l - (if (= (length cmd) 2)
4 x7 O; @2 I: ?) \2 ~% M- q: e( l& E - (list (string-to-int (substring cmd 0 1))+ V6 a4 H7 d2 j8 n
- (string-to-int (substring cmd 1 2)))
- P6 w9 J( y8 m1 _( p# ^ - '(0 0)))" ?9 @1 u% \) I8 Y" m6 Q
0 h: C( V' Z* b: r! ~- ;;; 游戏房间
" ]& O+ c; |6 B8 q - (defun game-room-init (cmd)3 d+ N9 n. d J8 m! s
- (let* ((middle (string-to-int cmd))
. ?' B7 p$ b$ I* b8 r - (size (1- (* 2 middle))))
! r& `1 u' n4 ` - (with-board" S6 t3 b- C( e# x, B
- (board-init size)! v9 y& V% X# ~6 f9 [$ ], m
- (board-put middle middle)))' ?) S4 U7 D4 f/ h$ S# c
- 'game-room)
" F2 h# Q& _' [/ E; y2 H2 S
6 E, i3 \- E8 l2 j% t- (def-room game-room( _! n& u) N" _
- #'board-show
% W" J# q' L* N9 g - (t (lambda (cmd)
) A- Q# O+ m, A9 l7 r( d - (with-board* Q6 i0 W3 N0 x0 H
- (if (game-over-p)
) o; L4 b1 C% E - 'living-room- F5 X( G6 g# d3 q5 `; f. K, r
- (destructuring-bind (y x) (board-position-parse cmd)4 B4 T: a: _7 c% f% i
- (when (board-contains-p y x)
4 U/ ?1 p, w8 W8 k - (board-toggle y x). k# P% v+ K6 s- P
- (session "step" (1+ (session "step"))))
: ~1 S7 Z2 }# }; _ - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|