wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。- E ?* p5 U! O
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
; U* ^7 r8 t7 J+ P6 x0 }4 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;">;; 定义新的游戏地图
( i4 e1 n+ n' M: s9 j- C7 K1 C - (def-map "/game/5x5.el" ; 对外开放的URL0 S$ [' K0 J3 p* B' r: u
- 'tutorial-room-0) ; 默认的入口' j" s* L' q$ f0 o5 B G$ m9 S
- $ m# Q7 n( D, g8 m- `: ~9 A) V4 e2 u
- ;;; 游戏大厅4 O! p1 {% D4 f( y: o
- (def-room living-room( m% [. V7 U3 \& C
- ;; 进入该房间后的提示语! q8 j# b' `* S o
- "1. 教程- B$ w: C! q6 {% f# [7 c
- 2. 入门(3x3)
1 u1 x' U0 F) h( M x! a9 r$ e - 3. 初级(5x5)- C9 U( p" N, D8 V
- 4. 中级(7x7)
- W' p* S9 Q1 V) n' v2 N& } - 5. 高级(9x9)
7 j, F2 e0 T& ~ - 0. 关于作者! O K3 w3 B$ B- K8 t+ Q E1 j' |0 [
- 请选择1-5开始新游戏:"9 o5 F+ h r* I& R' N
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名+ A/ ~$ Q, \$ W/ S
- ("1" tutorial-room-0)7 A: _0 l: t& w- N; A/ _- ]9 y* R( b
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配. M- Q0 w/ f) \1 P$ R7 W( A
- ; 相应的返回也可以为函数,动态返回房间名1 W1 u! j+ K2 |5 i) X7 s
- (t living-room)) ; 如果条件为t,为永真
; k# i) p, R5 w" x - , s# b! u- S$ s0 T4 q
- ;;; 作者信息2 ]% [- q1 D: C% s. p4 [. o
- (def-room about-room x3 l9 k) p+ @/ C, e- y/ z$ Y
- "作者:redraiment
0 n0 ?6 @4 @6 q. [' P - 微博:http://weibo.com/redraiment
. R7 I0 h6 @& V - 有任何建议,欢迎在微博或微信上联系redraiment。
# ~* A: r& `) r+ v1 j- w - 请输入任意数字返回游戏大厅。"
0 E- z8 w D0 Z/ K( w8 I - (t living-room))8 R3 N, @# r! ]+ r' d+ u
- ` }6 P `0 z9 C
- ;;; 教程
9 l6 S4 \; b. B6 T' D - (defvar *wechat-5x5-tutorial-rooms* 0& t4 Z- J. r) t+ a2 {) K
- "The number of tutorial rooms")
( p- P1 T5 F, j0 k$ r - 3 d# A0 L/ k4 O! c1 {; A8 b
- ;;; 简化教程的定义) z5 q* o }( Z' ]3 Y# m6 g+ ?7 D
- (defun string-last-line (content)
; N6 X# ^3 c6 c# H' a8 _/ V - "多行内容组成的字符串中的最后一行"
# k9 N6 M+ Y% O/ I! n6 a, N - (with-temp-buffer
% p4 r, u0 {4 S, L - (insert content)9 v( d6 k- [, J3 s! O
- (buffer-substring (line-beginning-position)7 R6 G6 ~; v7 G5 e' C5 F# s! x
- (point-max))))
2 Y0 z2 q: Y$ J/ h+ m: A
9 l4 b+ j/ t' {/ e/ d- (defun def-tutorial-room (prompt)
* ~" s, z9 H( S- n - "根据提示语自动生成教程房间。8 G* Y2 G$ X; \/ L- ]8 f
- 5 I" d9 O+ h1 d' [6 K# _5 F2 _- z
- 1. 提取最后一行作为问题;
+ b" T( x& Y G: H& t - 2. 分析问题,获取期望用户输入的内容;" {: c5 S9 y% I2 J( I1 m- A
- 3. 定义教程房间和重复提问房间。"* x; [# B, y6 o( V, q0 L Q
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))9 Z; Q/ O x5 C
- (repeat-room (concat room-name "-repeat"))
: O2 A) _2 D0 g - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))" v4 p$ j+ M: ?# x R) J( z# z
- (question (string-last-line prompt))) i( z7 C% d" D
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question); ]$ h5 p" o! e/ s/ y
- (match-string 1 question)))8 @) R$ R' m+ t
- (doors (if except
" N9 A; B" D$ j3 I0 N - `((,except ,(intern next-room))
0 P( _" X$ ]8 a5 o+ o - ("q" living-room)+ n- h4 f2 a/ d/ {+ b) h
- ("Q" living-room)
9 R5 x, `9 Q Z d! \6 R - (t ,(intern repeat-room)))
4 m5 C$ }# `( X( h1 W3 H - '((t living-room)))))
3 U( y8 Q) v+ q) D9 g - (def-room-raw (intern room-name) prompt doors)
9 X1 L4 L, U) b6 Z - (def-room-raw (intern repeat-room) question doors)))
5 z2 |1 C5 A v# L: H, G# D a
& }1 p3 W2 `' E# U* t- (defun def-tutorial (&rest prompts)
, m9 u y4 l& |/ y' p4 V - "批量生成教程房间。"
2 E/ ]# n$ |3 H1 i - (dolist (prompt prompts). H5 }" L2 F. M" C' h
- (def-tutorial-room prompt)))( b2 D0 C2 r- b9 P
- + W0 P- z5 M% F7 `% a8 }
- (def-tutorial. l) C. R$ ^. G! m/ G
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。" w5 E$ I9 H3 C+ @
- 1. 教程
3 V* T; `5 q3 X5 t" N: u - 2. 入门(3x3)2 p- ~/ t, j4 G4 N
- 3. 初级(5x5)
4 F1 z/ t0 A6 I- X - 4. 中级(7x7)( @/ p; n' T2 o" }
- 5. 高级(9x9)
3 R( L6 O3 H) L" v( s) {0 g - 0. 关于作者
) S- i2 y/ a8 q! r5 M# J - 请选择1-5开始新游戏:7 E) J# @# l+ Q/ o `9 H$ \
- 您现在正在游戏大厅里。
; f9 g* H5 i2 N5 y/ }$ W+ g - 请输入“2”进入入门级房间"
4 G2 Z& z) {. ? - " ①②③
, g0 `# v2 ^* ^) ]( P - 1 ■
8 D- W; X. k1 p9 m' E+ n' n2 z8 Q - 2■■■
9 X8 l7 P; @/ o+ N3 E. w8 V - 3 ■ * ~6 j" C7 v3 v2 h( D( q, V6 u
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
( H) ^! P" E W1 l% s3 I - 请输入“22”来关闭第2行第2列的窗户。"
% l3 L5 D; x# J - " ①②③
* Q. G2 N& E g - 1
' h6 y- K4 M) v( J - 2 5 j, o0 m# I+ ~2 k2 Q: T! C. R
- 3
6 Q( l5 z/ q- m2 `( t S - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
" d8 n. ^" Y* i! K7 K& f4 R - 请输入“11”,试着开启左上角的窗户。"
' q t& p6 G/ b, k - " ①②③
* N" H" {3 u A7 h4 n - 1■■
7 l d C4 E' {; {7 w2 { - 2■
7 [5 H7 E" S' S9 x+ l - 3 / w+ L2 n. u+ _$ s2 d
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。* N: T7 Z, T: U, v3 `
- 请输入“13”开启右上角的窗户。"/ X- R; S- E9 P5 g
- " ①②③: U7 H" i& p+ i7 g- y* Y
- 1■ ■4 z/ K2 C2 L8 I* Z
- 2■ ■9 `. v: S8 B! U' y' n! R
- 3
& A& r% T0 N6 i8 I1 x T$ ]& j7 n r - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。& K' e9 ]* V5 e( O
- 请输入“31”开启左下角的窗户。"
& E" u* a7 M+ }9 y2 C G0 z' n - " ①②③+ F% r0 E; u6 U: G! \
- 1■ ■( D3 v" z% s! u J
- 2 ■( U& O4 D8 Y4 l( {8 C- {$ P
- 3■■
3 _( A! v7 }1 U - 此时,总共有5扇窗户被开启了。! I3 h3 ~' ~1 I/ ]0 H9 q
- 请输入“33”开启右下角的窗户。"% q4 U$ D0 n' X: j" y v, Y7 ]
- " ①②③; F* D, o1 D7 ~3 @
- 1■ ■. ?/ Z( c9 \& s" i8 u
- 2
$ z! Q$ l& g/ p- D% k" C, r5 A3 P - 3■ ■# Q6 z4 } |; M* G9 G& F' ]) u
- 现在,只有四个角落的窗户被打开。
+ t0 S: x3 Z4 @( y - 请输入“22”完成最后一击!"' N4 {* u, @) x! f: f# H) _& o5 l0 Q
- " ①②③5 N6 y2 Q( Q) K% k
- 1■■■ V! d8 D$ k! C# B f' F" u4 z/ K
- 2■■■
" E) P& g2 N$ \ - 3■■■
( W" G2 q. ], S# c/ l6 U- M - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")% d( `1 V- x2 i8 W
- 2 p4 x# _7 S" h2 G. O5 w! S% A
- ;;; 棋盘
]" W( \3 q" t0 ~5 ~ - (defconst *wechat-5x5-white-chess* 12288% o1 K2 \& `$ P7 T! m
- " ")& y7 ~: Z' b( k- s+ y. d8 i* S/ |
1 i' ]7 u4 O6 m% _- (defconst *wechat-5x5-black-chess* 96320 B( ^' B, a" L) P6 t
- "■")
! A- g" n& x3 M3 Y1 m1 B
, F# f9 h! M; Q- (defmacro with-board (&rest body)1 f9 r6 {+ E! {8 ]# i
- `(with-temp-buffer
- X% a5 }' [/ p9 `# L8 f, A - (unwind-protect. h0 T1 }; Z9 J2 u9 a
- (progn: O4 a) z! V# {# p5 w, r3 _
- (if (session "board")5 U% N# ]9 D4 w4 r+ b* w
- (insert (session "board")))
% L$ ]5 E4 X! I$ }/ e - ,@body)4 @/ `, v0 D7 h" ~- D
- (session "board" (buffer-string)))))
9 V' y0 a7 w6 Q8 y/ y1 i& ^ - ) i: F9 N; f- |: p: ]
- (defun board-init (size)' e' j, i) M2 A
- (session "size" size)0 D* J" y2 C- v5 t
- (session "step" 0)
4 }' `; w5 R* M# K - (erase-buffer)
( A E( p0 B: @; J' J9 D6 H - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨")); J. o; F+ }7 \% q& a/ `# c
- (dotimes (row size)
/ D8 Z8 J* x' [2 V - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*))))): k7 }/ }9 ?: R: C, o9 @- e
- * T+ R) W# G% Y5 n9 E9 c ^$ r
- (defun board-contains-p (y x)) _! N; d; f U0 `6 V
- (let ((size (session "size")))7 e6 c# x5 R9 H2 Q
- (and (<= 1 y) (<= y size)& G6 t/ @" C' Q; f2 N3 D: B. l
- (<= 1 x) (<= x size))))
7 J( L& m/ b5 ?6 C) _, c
8 L5 u$ @% x! d& @& U3 L8 Z- (defun board-toggle (y x)+ F( a8 u: A- U8 H) b( r9 ?
- (when (board-contains-p y x)
2 |; P, f9 Z b2 h, {' {3 }) f - (goto-line (1+ y))" B+ @3 s" Q' v% `; P0 p
- (beginning-of-line)
& ?( K# W( T' _; C3 y - (forward-char x); t- W9 x* ?) z* M3 X4 o7 G) f
- (insert (if (= *wechat-5x5-white-chess* (following-char))
' | I- t( @& O( J - *wechat-5x5-black-chess*$ T0 L$ @) |+ z
- *wechat-5x5-white-chess*))6 H( Y1 \+ o* Q2 f( s
- (delete-char 1)))
4 {$ o S3 m4 j& q2 j+ w+ [ - ' {% I" w9 |! V9 e' C
- (defun board-put (y x)! G$ Z/ g. l1 @5 Z9 ^' q1 y. _3 ~
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
$ `$ C$ O q! @: i: @' I - (board-toggle (+ y (first dir))
& N* W* g' Z/ l! W: T- ` I - (+ x (second dir)))))
+ p+ |0 a8 d8 ~. Q# R - $ Q; q: t3 v, X6 J) O) j# q6 {
- (defun game-over-p ()) d6 F. Z( g4 L1 K" a0 u6 b
- (beginning-of-buffer)
. R1 Q) j: A& t' g - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))& c0 t# \; }% p. _2 ~+ ], I; N
2 Q/ l$ e( E- y4 Z/ F- (defun board-show ()
- P a) o9 E6 O/ o8 f - (with-board
; F% R$ ?1 X: O7 T3 @: h. e - (concat (buffer-string)
# h9 o! M4 D3 w) m8 h9 E - (if (game-over-p)
; m1 @; J$ c7 ]+ i/ b& b* \6 L - (format "共%d步,输入任意内容返回大厅" (session "step"))
/ ^0 `& C9 |- m( h$ k - (format "第%d步" (1+ (session "step")))))))6 U. Q+ C( q' e) P1 h! P
- " v5 X# ^( o( H ]/ B! G
- (defun board-position-parse (cmd)
, B8 q6 z) `$ `- N# U: j$ b - (if (= (length cmd) 2)
; b i1 {. M! w/ C" h - (list (string-to-int (substring cmd 0 1))
, S3 U1 g# S: B4 ?: }- @ - (string-to-int (substring cmd 1 2)))$ v" Q' M% J8 c2 p
- '(0 0)))+ m. K- e- c' k- `9 K+ R
% ?; {% q) f1 R; W. f- ;;; 游戏房间( V% ?( }$ C- i, {& o8 [) E! ^
- (defun game-room-init (cmd)
9 M+ C3 P I3 z$ {+ ?& l) ~0 V - (let* ((middle (string-to-int cmd))
# C2 N1 Z+ Z5 v& D0 Z# h - (size (1- (* 2 middle))))) B5 ~) d. c8 y+ M$ m# `
- (with-board
4 `5 z& x4 H& f5 T! h/ M - (board-init size)1 R# s1 w9 L8 K# ]" \3 v0 l6 Q
- (board-put middle middle)))
# Z& W* h ~/ n7 W0 l+ ?: f' | - 'game-room)
; |& i/ [+ X9 i$ F - $ M( m8 s" b$ ^8 i1 P4 |; i% S
- (def-room game-room
$ O8 X6 T% Y& D* W7 v7 q6 [; h - #'board-show
3 {( P+ c+ J) j6 u W - (t (lambda (cmd)& _7 `/ u# O# u7 W
- (with-board0 |6 {' ?. ?; T$ E- E8 G8 t! p6 b. B0 ?4 y
- (if (game-over-p)
- R+ d7 Z' x, t- s) L) x - 'living-room6 Q4 a+ J8 Y5 r/ t3 i9 O" Q
- (destructuring-bind (y x) (board-position-parse cmd)# t7 _: `: Z; _" b
- (when (board-contains-p y x)0 W- R6 Z% F" D1 i Q- D" Q
- (board-toggle y x) b) v! q! X2 W* j1 c: `1 h& w
- (session "step" (1+ (session "step")))); b) b! K6 c$ h
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|