wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。' M- d0 g& D! v2 M
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
2 m/ M( a! }! h- <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;">;; 定义新的游戏地图3 l4 e, C7 T1 C: d- @
- (def-map "/game/5x5.el" ; 对外开放的URL7 d; s7 D/ z* ]$ U
- 'tutorial-room-0) ; 默认的入口# x0 t( m& m$ l/ W8 l: r' P
) T3 v1 Z" v, k b9 W( F! N- ;;; 游戏大厅
4 I# V, V; G7 Y2 T - (def-room living-room/ m0 ~' _0 c7 T3 V+ Q1 C
- ;; 进入该房间后的提示语
& j, w' B5 v, s+ T4 w( ?% D/ Y - "1. 教程
, B6 M' P% R& n+ V0 Q" q - 2. 入门(3x3)5 K( W' q9 h8 _" j7 R+ h8 \
- 3. 初级(5x5), t: V6 T2 n0 A" T, o
- 4. 中级(7x7)
# o+ L) F5 o4 b' h6 L - 5. 高级(9x9)6 z# @5 `0 D q, |+ \# l
- 0. 关于作者
( `: ?! Y4 E! i/ |9 O/ `. `$ K - 请选择1-5开始新游戏:"
8 n5 h+ w3 n- I- O$ c; U - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名! h+ r( N( n1 H8 A& S" t
- ("1" tutorial-room-0)
' e4 _8 M. v4 K% I" b ]5 c - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
9 i# {& P/ u$ x+ m: Z0 D& Q/ X7 z - ; 相应的返回也可以为函数,动态返回房间名
; x9 ]9 k4 \. S& f0 | - (t living-room)) ; 如果条件为t,为永真5 Q& x4 |6 V# o8 J4 o. N
- ; i4 x7 ~$ ]' X1 `* o: d% n. O3 {( z
- ;;; 作者信息
9 \! E( @7 v' O( `/ n* C - (def-room about-room
6 f) q# e- x7 j6 k - "作者:redraiment+ I- x+ r) e4 f3 F5 P0 S
- 微博:http://weibo.com/redraiment
/ @2 U& X& v: z7 n% S, w - 有任何建议,欢迎在微博或微信上联系redraiment。
) L9 d& H1 ?% K# N - 请输入任意数字返回游戏大厅。"
: h2 ]5 {3 S2 v3 s) Q) M/ e9 Y - (t living-room))
- f3 {9 A0 Y+ Z: o" I
' q$ `* I) a- h2 ^* U9 B3 h- ;;; 教程4 q$ f5 w! g8 w4 S$ R; J9 _
- (defvar *wechat-5x5-tutorial-rooms* 0
! S% Q( ^- u6 u& O' z% F - "The number of tutorial rooms") d0 i+ n) j( j% B4 ]1 V' t% i8 }
& J* p! r* j0 k9 x( f& `% s- ;;; 简化教程的定义
- u4 O! K1 I& h - (defun string-last-line (content)8 J8 Q& P8 o- ]/ H
- "多行内容组成的字符串中的最后一行"
4 @ ~0 A9 H2 G4 Q - (with-temp-buffer
; M0 b, W$ i+ ^5 X# s- y - (insert content)
2 o/ W# n9 c0 d8 X# f' L! M - (buffer-substring (line-beginning-position)! r$ u3 M7 F; X; ` M
- (point-max))))
+ P1 B8 ~1 J; s4 M0 G
. V# R1 k5 U8 g2 ], j+ Q% r- (defun def-tutorial-room (prompt)8 P( J: X# B+ D: v/ W R9 C, i5 N
- "根据提示语自动生成教程房间。
# Q9 J+ `$ [- V- ~
; d9 O1 Z; E; X+ L8 W7 D- 1. 提取最后一行作为问题;7 |: x, r" a$ Q; [; z% N, W% |
- 2. 分析问题,获取期望用户输入的内容;
# Q/ F: v) b( T/ g0 B7 F - 3. 定义教程房间和重复提问房间。"
* L0 j# M+ d( D4 y2 O' Z" E - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))' s; X# u) r7 q2 R+ Z" f1 ~6 j: W
- (repeat-room (concat room-name "-repeat"))' R9 _) F0 X, Y* R& r8 k# n
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
/ ]2 f# V! c7 Z) A9 d6 ^, s: J& m - (question (string-last-line prompt))
! b3 P# a/ _# N - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)/ c, s% T1 f2 D( q
- (match-string 1 question)))1 w c4 S2 H8 _. V7 ]
- (doors (if except
) j0 I4 x8 r4 g0 m' l1 C c - `((,except ,(intern next-room)); J* R# ~+ n1 q7 p8 {
- ("q" living-room)& d" d4 w9 v& k, K* \8 x
- ("Q" living-room)
" X4 I. \# d s; k7 ]( I - (t ,(intern repeat-room)))
: V( X1 x2 Z8 B9 m' r4 v) b: B6 D - '((t living-room)))))
( w% M' Q3 @: F - (def-room-raw (intern room-name) prompt doors)
" T4 J# u' M; x% l$ P$ D - (def-room-raw (intern repeat-room) question doors)))' K! l8 ^* I: h* s, [+ g# K
- 9 Z( O; W4 i1 ~/ o; S( h% P
- (defun def-tutorial (&rest prompts)
9 R/ g' k3 F, O/ E - "批量生成教程房间。"; p3 `$ J: N- `: h$ G( o
- (dolist (prompt prompts)( _2 k4 J2 ^5 w( Z6 Z
- (def-tutorial-room prompt)))
7 z, A; ~4 V6 J8 D+ t& ]4 o t6 ?
c3 h1 }+ T( ~, q! Z- o- (def-tutorial& M3 M8 E; I( V( z; ^% v0 ?% N
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
/ Q' y7 W" G! c( O$ K - 1. 教程
3 U, M, @8 a# k* |- } - 2. 入门(3x3)
* L4 X2 h2 l S: ^* P% \ - 3. 初级(5x5)* H4 y4 O3 p" O
- 4. 中级(7x7)
6 @$ S0 t; F4 q, r - 5. 高级(9x9): f. t1 F/ A, j! I6 h$ [, {' I
- 0. 关于作者0 v }2 O' M. R6 P
- 请选择1-5开始新游戏:, w( ?7 k' n8 x2 i: e3 \
- 您现在正在游戏大厅里。5 Z5 e0 w) }+ r' t( n5 P5 ^
- 请输入“2”进入入门级房间") D" `* k. k; @5 ], d3 j
- " ①②③
" o0 b; P2 \8 O8 r- \ - 1 ■
" e! P4 g2 g8 ^! \ | - 2■■■8 F1 W; g. C# u$ v9 v% d6 k h
- 3 ■
+ a/ f' y, V. { - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!% T- R' u; m5 g; R) R$ n
- 请输入“22”来关闭第2行第2列的窗户。"
8 s1 g! w0 \& q; F( c: |+ ^ - " ①②③
, H/ U" }( I/ B" n# y/ i3 \. ~9 y+ D - 1 ( ]4 o* X& i. b* l
- 2 4 d+ g5 v8 E* m! Z, U
- 3
3 ^8 N8 v7 \4 U+ t7 w/ m0 J - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。6 v* a! M( I# M" V/ X- \( m
- 请输入“11”,试着开启左上角的窗户。"8 H7 b2 ]1 B# d N$ w6 _ Q5 t
- " ①②③
9 J: t5 x& Q* A# F/ {, m - 1■■ * O7 V3 i: g* C5 E S' I
- 2■
: v9 m# P! \ t4 B* C. @0 T* c7 M* N - 3 R7 B9 b& d0 G! Q* N
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
) U0 d( ?3 s; D) t8 f - 请输入“13”开启右上角的窗户。": t2 {; A4 b- \# w# q
- " ①②③
7 A' j* k6 } c# s. q, C: o3 w - 1■ ■0 \) u, t# d9 L* s w
- 2■ ■
! b+ J; a. p4 _' q - 3 , _0 b! Q9 ^0 t
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
% J0 F; L# V" H3 ?! B/ P - 请输入“31”开启左下角的窗户。"
2 j4 N( |# }: c2 a+ \+ A - " ①②③6 u/ @" T e' {) K6 C+ z6 w9 F! j
- 1■ ■0 C4 K. `0 H% B5 U3 y2 s3 m( X' a
- 2 ■
6 D2 _% O( W z' l9 Q; M- `8 t* ~ - 3■■ % B+ A, s% s7 v# T$ {/ {
- 此时,总共有5扇窗户被开启了。2 k$ r6 x. h) K5 ^" r7 @
- 请输入“33”开启右下角的窗户。"
0 [9 ~1 V3 _( ^& m - " ①②③: L$ P+ g5 ^$ A+ w3 m
- 1■ ■/ v ^" @* ^- Q1 v# M6 z+ T- ~+ P
- 2
$ d8 i4 r$ Q5 A2 t - 3■ ■8 i' ~8 J% ^) e" N, n$ I
- 现在,只有四个角落的窗户被打开。
- s7 }* x) ^3 B3 S2 y( o* y( v$ V - 请输入“22”完成最后一击!". N6 Y7 V. I- z
- " ①②③9 _1 M2 M+ O* Q' y6 i
- 1■■■
" {& Y( A4 \0 ^& ~" o3 _0 | - 2■■■
' h* `9 t) R' B: }7 T - 3■■■
) |4 V% @5 @/ b# R" J* L Z - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")% Z h6 w. G0 ~5 K
) m# u; }2 s; w5 l6 k, ~- ;;; 棋盘9 w: C) k$ d) q. X. ^ q
- (defconst *wechat-5x5-white-chess* 12288
! ^, i5 q& m: i/ e8 \ - " ")
2 H4 h/ x$ I/ \
6 T; D5 P5 P, X2 N( c) T& j- (defconst *wechat-5x5-black-chess* 9632
2 B% ?7 P: C: f. Y9 H - "■")# i0 N8 t! b8 T$ O! g
- : d9 b" E" I$ e& m6 J4 k: T$ f
- (defmacro with-board (&rest body)
! w$ n& |. r9 Z* j% k' f - `(with-temp-buffer" ^1 E. w, N2 n' ^
- (unwind-protect
/ N2 `, d' m% h$ f6 I0 N h% ]; Y - (progn
& h: p. x0 ]4 _ - (if (session "board")% v' f' _4 v4 w% W; v# W* ]' @
- (insert (session "board")))( |2 ]/ L+ F7 n% A; @
- ,@body)4 h9 G% ]" f2 z4 P& Z _
- (session "board" (buffer-string)))))
. F6 s$ A2 r9 V/ a9 X* o: @- @4 L - - o8 J) I2 i1 x! x
- (defun board-init (size)
" u* d& B- h. o$ M: y7 S - (session "size" size)
9 u8 r# v* z' _+ N* n* p s* M2 R2 s1 b - (session "step" 0)7 V8 V+ Y% a+ D8 `; N
- (erase-buffer)
) _8 ?6 L' c0 Z% _9 D; F/ H - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
+ b4 r0 O3 `1 W6 I" Z& k - (dotimes (row size)
* N" }: ^- ?- B5 n( k V8 L - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
* q- ?8 i9 v' `& \: Y
( {9 Q; X! K( `) Y: z. W- (defun board-contains-p (y x)
8 j$ V. Z6 }; @5 Y - (let ((size (session "size")))# p2 Z+ j! h4 O% _3 `3 c
- (and (<= 1 y) (<= y size)2 J& {( p8 q c/ j& W
- (<= 1 x) (<= x size)))), U1 L! M; p& |) j3 G2 Y# e8 R" u
- ) ? [% J3 {* J) _+ b" y( t
- (defun board-toggle (y x)( P7 y0 @+ d. I0 j
- (when (board-contains-p y x)
7 h: x9 {- {4 z - (goto-line (1+ y))
2 M" k6 E: q, B3 N: j A" V - (beginning-of-line)
, N5 X8 B& \3 I" r$ j - (forward-char x)
5 R6 V6 N: _! }5 j0 h - (insert (if (= *wechat-5x5-white-chess* (following-char))
, _" X. s" x, ^$ U; n+ M - *wechat-5x5-black-chess** N) ]8 E- g9 ]6 ~( g5 Q
- *wechat-5x5-white-chess*))
" j/ `! ^/ D% D0 y - (delete-char 1)))
4 ?6 p+ Y# E2 c' [2 R+ Q
, T8 B& i ?- o" a$ W- (defun board-put (y x)8 C& _# `5 P# O; X
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))# n+ @1 F: t# @& H5 Z
- (board-toggle (+ y (first dir))
% a0 R+ W) Q6 `4 P0 ^ - (+ x (second dir)))))
q+ L& I$ M6 M# U - & u! ]: `7 C ^
- (defun game-over-p ()
! }" S I6 V) @3 X - (beginning-of-buffer)& y, |0 r/ E' A* U1 v3 u
- (not (search-forward (string *wechat-5x5-white-chess*) nil t))). M$ `4 z) n6 a8 x
7 D, |, g. d% }! e6 C, L- (defun board-show ()
$ |3 E# ` E! q: k - (with-board
8 x3 H, o' k. U5 h - (concat (buffer-string)
& F6 c: ^- o# C& M - (if (game-over-p)
) D0 K" p9 i5 O, z/ g) T% n - (format "共%d步,输入任意内容返回大厅" (session "step"))
8 }2 @5 i& ] y0 r: f" B; A6 | - (format "第%d步" (1+ (session "step")))))))
: t4 y" o- E, d! ?% X. R, t
3 N' G6 J+ R/ Q ^$ @$ a( W2 U- (defun board-position-parse (cmd)( p, m. ^; U$ z2 z) h2 G" a
- (if (= (length cmd) 2), X: j8 R, A5 D0 f# v
- (list (string-to-int (substring cmd 0 1)), K1 g) I( M0 L/ C7 ]; ]- q6 v
- (string-to-int (substring cmd 1 2)))
* d5 T P, l% B H! s) }$ v/ L - '(0 0)))' G2 I* \* k1 v: i% V7 {
- # f$ o/ f8 w& k( ]) j
- ;;; 游戏房间
8 c) e0 a& k, U, P, _ - (defun game-room-init (cmd)
* C; s9 H p/ a3 s7 G - (let* ((middle (string-to-int cmd))% ]; Q$ K4 p, O. T. }. \
- (size (1- (* 2 middle))))
/ V& {( B# V i% o3 f6 X- o- H - (with-board
( y1 S" M9 g5 d% R4 } - (board-init size)+ _: e5 L) s* V8 ^0 B R
- (board-put middle middle)))
* ]- M) T y) @0 x' e$ ^ - 'game-room)3 ?/ v. V! ~# p4 J: s
6 D# J5 _2 c) t4 I' n; x- (def-room game-room; \, {, p7 ^3 ]+ Y) ^2 F4 y( h. |4 X
- #'board-show
, @& o! D% N0 f) V/ M p - (t (lambda (cmd)
8 |, s+ h8 C* @7 U - (with-board
- d5 f; J C0 ]$ @0 E' J - (if (game-over-p)
/ P% p# |! V: v' o/ y - 'living-room2 J! F+ j* _8 C: M
- (destructuring-bind (y x) (board-position-parse cmd)
8 J6 l/ t1 H2 C! H - (when (board-contains-p y x)
1 l$ V! ~6 @1 r+ p5 x - (board-toggle y x)
2 s& M6 D4 B4 g' L* [ g6 t! u - (session "step" (1+ (session "step"))))7 l, m% M' j2 x0 I
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|