wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
: @, a0 v2 H% T" D借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
- g* A# `2 E6 `5 |' B, L- <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;">;; 定义新的游戏地图) V' K; W5 T6 p6 n, \; ^6 O
- (def-map "/game/5x5.el" ; 对外开放的URL. X2 o+ D% T- ^, x$ o
- 'tutorial-room-0) ; 默认的入口6 G! F( h1 K9 @
- + ~/ l x( i. J6 i4 Y9 r+ H
- ;;; 游戏大厅/ x% U- @/ f$ s3 M7 L( `8 @
- (def-room living-room
+ |8 L8 f$ Z, M7 _. z1 @ - ;; 进入该房间后的提示语
; X" p% H4 G5 h) e! e/ f$ l- Y/ ` - "1. 教程$ M5 r, x- D) {" G- F) V' a
- 2. 入门(3x3)
$ z9 W- {' ^5 Y# j" u - 3. 初级(5x5)
5 D/ z0 B% b5 } - 4. 中级(7x7)' s# O% o' d q, ]0 U" Q3 G7 l
- 5. 高级(9x9)
- [5 S4 ^' W/ n8 `: [ - 0. 关于作者
$ R+ A% ?: y. y- \0 { - 请选择1-5开始新游戏:"$ L. M5 K9 i% M m) h
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名/ E6 j8 l0 C* T! h: a
- ("1" tutorial-room-0)
, Q* ]* ]) l; d! \ - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
# d2 h$ j9 c/ Y& y# m - ; 相应的返回也可以为函数,动态返回房间名
7 X' G9 y' P8 r+ e0 v5 o; i. i+ @ - (t living-room)) ; 如果条件为t,为永真6 M2 G5 F: n3 C
$ m7 `6 I b9 H5 Y; z- ;;; 作者信息
6 g8 O& {, P5 E+ Y; R - (def-room about-room+ N- f0 x/ y% C- ^$ `3 F" {: A+ u0 _
- "作者:redraiment5 V$ f; R v5 ^1 e: a
- 微博:http://weibo.com/redraiment: m( R1 V/ { u: m- O T! a
- 有任何建议,欢迎在微博或微信上联系redraiment。
' z/ N0 L2 ?; W# p8 S5 w+ n/ q - 请输入任意数字返回游戏大厅。"0 \/ ~) S; v7 e. r
- (t living-room))6 A0 J/ h& }* v j: P
- 1 k/ J' T0 \& ^. C
- ;;; 教程
; K1 K1 G8 A7 N5 S. ^+ z/ s4 R - (defvar *wechat-5x5-tutorial-rooms* 0
: u) f$ H. G: U% I+ }. D7 e% T - "The number of tutorial rooms")1 {; `0 E+ W% }
- & c4 T+ a# _) N( _6 ]7 l
- ;;; 简化教程的定义
( F; g% S1 d% `4 x+ e - (defun string-last-line (content)
3 k& ]7 Q7 W3 ]0 ~+ l+ a - "多行内容组成的字符串中的最后一行"
; p* N9 {5 L% h( u/ A - (with-temp-buffer
" ?$ }5 P6 E! b" a& ] - (insert content)2 s! Y6 h( S$ m+ x. X9 t: J* e
- (buffer-substring (line-beginning-position)& r* }) D0 g! B
- (point-max))))1 H" D& {: T( s' e) S! M Q4 }
- 8 y; ]" Z* K. a
- (defun def-tutorial-room (prompt)# i) L; g/ `* F- {* G6 |5 F) W
- "根据提示语自动生成教程房间。
0 e9 J( r+ @9 b) ?$ v! y. H. B - ; C1 z: C& ?' q& H1 G1 @
- 1. 提取最后一行作为问题;
7 [2 A1 h, s4 z7 k - 2. 分析问题,获取期望用户输入的内容;' I, O! x0 u8 S; }. W% O
- 3. 定义教程房间和重复提问房间。"
# j; R* `: i4 V- u3 d$ Z) l - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
# [0 C7 N3 ]3 a- u - (repeat-room (concat room-name "-repeat"))3 W* m: j! v6 m+ X, d
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))) n7 ]1 m% d$ G6 |
- (question (string-last-line prompt))
' _& K# t% b5 R5 ^/ R - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)0 h. j; l3 v& P+ M
- (match-string 1 question)))( I4 Y6 h. ~/ o5 E/ x6 |
- (doors (if except
+ N6 h7 E6 n' Z7 k+ E! Z) T# J - `((,except ,(intern next-room)): g0 q/ ^1 H- a" W
- ("q" living-room)
# E, r/ @' R3 e1 r1 j - ("Q" living-room), I" n# u5 E% Q' Q3 |9 c
- (t ,(intern repeat-room)))
/ I2 U: W+ ]; ^9 j$ F4 ?2 c7 L; a( J - '((t living-room)))))" t0 P& }9 c# W: e
- (def-room-raw (intern room-name) prompt doors)* f1 @! Z- Q) }" N3 H
- (def-room-raw (intern repeat-room) question doors)))
' U, u/ o. B! ?2 y9 m* D - ( m$ j8 |# \5 w8 Z7 d
- (defun def-tutorial (&rest prompts)
- @5 x: I9 p7 | - "批量生成教程房间。") M4 ], X7 V d9 {, S7 r4 I
- (dolist (prompt prompts)1 q( s% t$ L7 e
- (def-tutorial-room prompt)))5 x4 e$ k. ?) |& b$ o$ f4 _& Q; m
- * {' w( _8 c3 h% C
- (def-tutorial
% ]0 d! p' m7 V2 k& S) c) T - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。) L& D9 g k3 Z; r' g
- 1. 教程' n1 u; f1 s3 O. ?* ~
- 2. 入门(3x3)3 L8 m# ~% b" J' |( L
- 3. 初级(5x5)+ }; {7 o- Y2 [. F
- 4. 中级(7x7)
6 L. m) \! h/ a7 h# R - 5. 高级(9x9)
% ?! S$ |! w1 u - 0. 关于作者" u6 H) A1 \. I4 ?, d
- 请选择1-5开始新游戏:
0 U/ {6 y5 G# f, s - 您现在正在游戏大厅里。3 v* y2 `$ Q# I, ^3 {1 V
- 请输入“2”进入入门级房间"! h# ~$ V6 p3 b3 I+ z/ M
- " ①②③
+ m' U& L+ Y S+ ?* Q# k; I! P2 g - 1 ■ * Y/ w. r9 N! }1 x$ y
- 2■■■+ W" U3 I# p/ U7 z) G
- 3 ■ + z1 Q- B* `7 o7 S9 @; p. t
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!4 ~8 }: H( N `: u8 h
- 请输入“22”来关闭第2行第2列的窗户。"4 ?: S# x( J; d$ A6 _% I
- " ①②③
# q2 w- Q: G. e W0 k+ G - 1
* r. W8 i3 @! u, n - 2
( ~- h( @( m& m - 3
; c( Z: J' B; A4 U- p7 n! |: ~: i. g - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。; j$ ~8 C' b. h9 W0 l
- 请输入“11”,试着开启左上角的窗户。"* p% }9 ^; Z$ t/ `) r. t# d3 x
- " ①②③9 x, s8 P: p/ h7 \6 y
- 1■■ 1 G) U' _" @5 ]. F6 |6 Y' L/ t
- 2■
: L* x0 y# b6 A Z - 3
% {- b' J# ^8 k- T g4 I - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
: j, Y( I: h9 `3 h - 请输入“13”开启右上角的窗户。"4 l) L1 @# S6 a, }' d
- " ①②③
; \9 D* \" P1 M% u7 u5 l# I - 1■ ■+ C& |$ X# Y( m" d( i7 P/ ~+ G
- 2■ ■" s+ l E# q& }$ h
- 3
# c; @. S' f+ v7 F% D7 m* L9 U1 y! w1 x - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。1 U# R8 C: B8 V: L& e
- 请输入“31”开启左下角的窗户。"; x! ~0 X1 e& Y$ D. F* `3 f: g* Q
- " ①②③
7 U8 _" E# f2 C0 r% E u, s, y) j - 1■ ■
o+ }2 E( X7 |6 w - 2 ■4 ^6 A5 R' \; X; g* l5 v
- 3■■ " Z/ E! `# F* V2 c8 s& u% [2 u2 ?
- 此时,总共有5扇窗户被开启了。
& ^0 \' H! A5 g% M$ E3 j) y8 T& V - 请输入“33”开启右下角的窗户。"
0 ?* O1 p) t, S# u; h8 H/ ~ - " ①②③% E% C% _& v" U G1 d
- 1■ ■0 [" _6 x- b- K+ x/ p2 u8 h6 w
- 2 ( C/ v3 n# |/ F9 K
- 3■ ■9 b+ Z0 G4 s/ J# _( z" R+ m
- 现在,只有四个角落的窗户被打开。
V P z' J9 m& {8 m8 k - 请输入“22”完成最后一击!"
9 m: d- a& z9 o8 y, Y- x& Q$ w - " ①②③
$ v/ X3 {( T3 \- T! I4 y - 1■■■
7 Z! _& q) }- B - 2■■■
3 L+ ~( A1 a. R( X- U4 |, j" O1 w - 3■■■
/ M+ u0 u$ F Y - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!"). \( F3 z, ?" b% ?: |! Y% D8 f
5 V$ g: t8 h$ ~/ \- ;;; 棋盘1 W4 x. y( p% g" m9 f- j3 N* {9 I* `
- (defconst *wechat-5x5-white-chess* 12288
1 }2 e0 D0 H' L& I$ u4 N {' l# Y* [$ l - " ")
; a9 J1 X( b: o* ]( a
; O6 Z$ ?; f* I D& S4 ?5 ^! u- (defconst *wechat-5x5-black-chess* 9632* ]2 N: [* L0 D+ v( v: t8 O! H
- "■"), C, c# w- R+ k4 v2 V# c
1 H8 w: t3 Q- o8 d- (defmacro with-board (&rest body)
# F1 C" q& R& o L/ X- Y _ - `(with-temp-buffer
7 K, L9 D7 G+ H, t% N - (unwind-protect [* }$ X8 T( R! Y. j
- (progn
9 p6 Z' i2 R# O7 n2 s" H) d - (if (session "board")
! P5 M' {/ B, ? - (insert (session "board")))) @7 t( L6 }" U+ y$ M/ m% ^; i/ L
- ,@body)5 [: | p& E g( g1 N
- (session "board" (buffer-string)))))' c% H; q, {1 F6 F# Q' n
- * c- Q7 W( q' R1 p1 M. h" _' z
- (defun board-init (size)1 i' S. o4 x+ C7 ?8 m1 W+ l
- (session "size" size)/ q/ k& x1 P6 Z" z. U
- (session "step" 0)! d% P& @2 B$ F u
- (erase-buffer)
, s9 _0 q, {% U+ Q- _ - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
* x' a2 u! ^& |0 E! ~ - (dotimes (row size)
+ ^3 W+ i& ^6 ^8 u1 l9 V; a - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
3 v6 k3 f" H5 e) q- s. Z - 8 V. r& O2 w7 G! a
- (defun board-contains-p (y x)
, k7 a# K; L- ]/ H* B4 G! @2 t4 j- U - (let ((size (session "size")))
' N7 X+ R3 A) V9 O& e/ x - (and (<= 1 y) (<= y size)
) u' Y( d, i7 ^* m: E! [4 q - (<= 1 x) (<= x size)))); q: o7 T8 ?$ _+ ~
- + V8 n- c J3 Z5 h- n+ v
- (defun board-toggle (y x)9 W0 u6 m" w y7 U7 S% X
- (when (board-contains-p y x)6 Y& ], l2 z. f1 T5 H
- (goto-line (1+ y))
. c" Y) u% C) B - (beginning-of-line)
& C. I" F. D) T& R$ V - (forward-char x)
3 ]% C7 D4 |3 {9 y' O }- o3 c - (insert (if (= *wechat-5x5-white-chess* (following-char))8 D9 X- P3 J: Z. H$ y6 y8 S7 W$ r
- *wechat-5x5-black-chess*# U" [. ^# K! {3 _, s w% n8 @/ ?
- *wechat-5x5-white-chess*))& J0 m& {$ ~8 D/ l" Z
- (delete-char 1))), I3 u) r, I2 K
+ Z5 \0 ?' I9 T- b' a' }4 d4 Z5 s- (defun board-put (y x)
+ C E: b% y( q4 w, |6 w - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0))). o V' W+ J% z2 {6 F
- (board-toggle (+ y (first dir))
. Z1 e' a% b: W5 M- |7 ^6 b - (+ x (second dir)))))/ J6 F3 W3 h7 l. ?: o Z# [) F/ t4 G0 Y
, ^5 ^- A+ i) \/ E8 d, t- (defun game-over-p ()
7 `; P p! P6 o9 s4 a2 D; k, u - (beginning-of-buffer)0 x% y) Z# t! `/ V2 `
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
, p, z' Q# B% R - 1 A C7 ^! d3 h; M
- (defun board-show ()1 P0 N. E5 h7 M! t
- (with-board o! r% ]( \+ f# n0 P
- (concat (buffer-string)
. E. k' U0 p. d+ i: m/ E - (if (game-over-p)
) L2 C1 \3 k" {1 N - (format "共%d步,输入任意内容返回大厅" (session "step"))
. t1 A' X" C- d - (format "第%d步" (1+ (session "step")))))))
" p. v8 \! D0 o2 {9 Z) y/ p
: Z$ a7 A6 K# W- (defun board-position-parse (cmd)
. t) a+ G, ]$ d. ^ - (if (= (length cmd) 2)
) V, m8 T' o! q, G- G - (list (string-to-int (substring cmd 0 1))
: P6 i) N- e/ c6 P Z* |/ x6 q - (string-to-int (substring cmd 1 2)))4 ~9 K( y' L/ f( F: G9 o- _/ b
- '(0 0)))5 L- ]$ k4 @: _3 a# H
2 i. m9 V- V# x+ b Y4 u- ;;; 游戏房间
7 O7 A3 `( p o: q7 D3 v! ~ - (defun game-room-init (cmd)# t+ ?1 h. H5 C" C: g3 U$ K
- (let* ((middle (string-to-int cmd))
3 G' H, T+ p6 |0 L/ J - (size (1- (* 2 middle))))5 ?& Q4 E% ?+ A& |8 K& P
- (with-board/ a# x! W7 ]: ?
- (board-init size)
& |. S: r ?: D - (board-put middle middle)))# k6 T' W9 N; X5 \
- 'game-room)) ]" X# ?2 h/ R1 d
" m) Z6 y! D, c5 B, I& ^- u$ Y- (def-room game-room
( M7 Q" m# j8 U% f - #'board-show
, d) d4 j( N9 ~ V - (t (lambda (cmd). \6 Y) b* ?- W5 g O, W* V
- (with-board
: n$ T5 S1 b4 Z8 G: ]1 } - (if (game-over-p)6 v; |/ Q& W. p& v! C1 |+ G) j! i
- 'living-room4 u4 A, w' l& k/ d
- (destructuring-bind (y x) (board-position-parse cmd)
2 D, s: a0 H+ D1 W8 }( W - (when (board-contains-p y x)
& G5 r5 [, w- r* c4 Z - (board-toggle y x)( p( Q9 Z" p" `% T
- (session "step" (1+ (session "step")))): |# r/ L' \2 i; s: i7 a
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|