wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。% z6 p9 k7 X% k
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
0 [! B/ Q; Q% b4 \5 c2 @- M- <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;">;; 定义新的游戏地图
d# Q- V2 m2 X% D+ H4 F - (def-map "/game/5x5.el" ; 对外开放的URL
, k' T' W+ o6 Z) d3 @6 G - 'tutorial-room-0) ; 默认的入口* _; |/ i" Q2 S, w. ~/ K1 Q- b
- S/ }5 [& A }8 K- P- ;;; 游戏大厅
: \; h$ i J* Y - (def-room living-room+ I. A& l; i+ j) P& y' V
- ;; 进入该房间后的提示语* P4 W6 b- m5 a j: _$ }& {5 Y$ k
- "1. 教程
0 f" O9 o3 X2 [ - 2. 入门(3x3)
# u, h1 _+ ~5 w9 x - 3. 初级(5x5)
. U+ A/ p& V2 z# }6 @$ b - 4. 中级(7x7)& \0 c. p2 I+ s0 k) X
- 5. 高级(9x9)! J2 ^& g* p( j# X; F
- 0. 关于作者
' r7 Z( M( @/ U1 E6 { - 请选择1-5开始新游戏:"
4 w( s& l/ W9 `& d! h - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名; r+ W R1 A- _- T+ Y
- ("1" tutorial-room-0)( o' c* i" Z$ ]- y2 f
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
1 M5 Q* T, U+ L+ e: I0 b7 L! T - ; 相应的返回也可以为函数,动态返回房间名. X, T6 M( h3 R
- (t living-room)) ; 如果条件为t,为永真
' D: {, S8 I ~# P- {/ l U' S - ; y" ~( t, h* [$ Z) ?0 Q; n: X6 `
- ;;; 作者信息
1 @' G0 u7 T( N0 K, c% h u - (def-room about-room
- r; T' a1 e% C7 n. l( ?1 h - "作者:redraiment
/ Z+ W* A, ~/ C4 N - 微博:http://weibo.com/redraiment
5 g) h; j3 |7 t2 M - 有任何建议,欢迎在微博或微信上联系redraiment。4 O- e2 J9 l `$ x
- 请输入任意数字返回游戏大厅。"
f" r0 F1 G7 p/ w0 U: i; y - (t living-room))+ b3 j6 ^% ^" a# z9 Z3 o& D
6 V/ J4 C, g; D% f3 p- ;;; 教程
& @6 d- i% T$ F& m2 B2 l) X" t - (defvar *wechat-5x5-tutorial-rooms* 0
: Z5 t! c l, v: a! _ - "The number of tutorial rooms")+ W. H8 c1 d$ h+ y/ x- g
% W2 l: @: n5 p E( N: V- ;;; 简化教程的定义7 Z% c, e' z4 b# }5 c1 w
- (defun string-last-line (content)
0 S8 v ^( c! j2 o - "多行内容组成的字符串中的最后一行"
2 B$ Q/ M( A) H, G0 } - (with-temp-buffer
. i5 \4 v+ v3 P8 r3 [. f - (insert content)4 d1 E" a% J7 `0 {1 |) ^# b% |- m
- (buffer-substring (line-beginning-position)6 k% J' s. T7 g. L$ H: X5 T
- (point-max))))
* q; Q, G1 }# ` \$ y: b" G0 m - & q* c3 [. f: M
- (defun def-tutorial-room (prompt)' B2 R9 y! N& l2 p! ?* {0 C
- "根据提示语自动生成教程房间。
; z8 I& v4 U( Q' X, p/ y6 I9 F - 1 D, O5 j+ G; S/ f) z" W
- 1. 提取最后一行作为问题;
D6 I i; M: \# q0 l - 2. 分析问题,获取期望用户输入的内容;7 z q h& P1 e( u! X/ V( h) Q
- 3. 定义教程房间和重复提问房间。"
5 Q( C' S' k, n - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))6 X) {) [3 E+ g8 E# Y0 _" m
- (repeat-room (concat room-name "-repeat"))
- \$ [4 ?* j4 s8 b {2 [ - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
( t0 U4 K+ ?& g4 e% U; O) V - (question (string-last-line prompt))
8 J4 K6 J# e$ i5 \6 F' Q - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
" S% E" S" S& n* ^, f - (match-string 1 question)))
7 p( D- c {8 u: R, s1 _+ s5 @; q& W - (doors (if except) {; N) P1 G, J8 E9 i+ Z
- `((,except ,(intern next-room))
- O3 R# \; f8 v" n - ("q" living-room)
& ?/ ?! I! H$ f. {3 q - ("Q" living-room)
" I% c: [6 d! r7 e; L n - (t ,(intern repeat-room)))
2 t m& V1 j1 Q - '((t living-room)))))
) ?3 v$ k) g! ~, x2 | - (def-room-raw (intern room-name) prompt doors); K) T' K3 E! Q' V V
- (def-room-raw (intern repeat-room) question doors)))
2 ^ N$ c& g( X, a( ~* z, M - # M' E+ j+ T/ {* r9 [( S4 P
- (defun def-tutorial (&rest prompts)( _' A% W: d% R2 u ~) Z8 U" g: I
- "批量生成教程房间。"
- P% ^) C6 }/ D3 r9 K1 h6 q - (dolist (prompt prompts)
- J0 I' P8 ?" L9 t - (def-tutorial-room prompt)))$ l: e- d& [6 L6 C6 Q
- ) S% i* w7 ] G7 x7 F3 i
- (def-tutorial- O; B: C, E% M
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。" ^+ |' w, N1 K7 }3 e6 f7 J
- 1. 教程
& j2 q7 o" ~3 K/ @ - 2. 入门(3x3)
8 D4 e/ N" m' ?. { - 3. 初级(5x5)' ?; C/ j# r/ @- |! ]; J6 i7 Q2 R
- 4. 中级(7x7)
: n5 n% H, s0 b$ _" _/ |! F3 e6 O - 5. 高级(9x9)
U8 Y ]( c+ a - 0. 关于作者
+ ^* ~3 h% e/ I - 请选择1-5开始新游戏:
+ S: m% T$ G0 t) T2 A6 I6 ]* X - 您现在正在游戏大厅里。
) E+ G! C- L5 o2 }6 y4 |# a - 请输入“2”进入入门级房间"
6 B; p1 r! l* { - " ①②③( h- n9 _! N: T c. w4 H
- 1 ■ & W4 Z* `, w* S! R
- 2■■■
0 ~/ D& T5 D" e+ T - 3 ■
2 B* z1 l o4 }/ T+ m - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
, w: G: K9 |& v& k4 [. Y+ S5 P - 请输入“22”来关闭第2行第2列的窗户。"4 x2 H3 f- l) E# K
- " ①②③
6 L3 t7 y/ @( y" S - 1
( _" P: X. e8 W) B! o - 2 6 g* b' c4 g' v7 w2 P
- 3
9 y) |& c4 L1 Y' T - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
K' D% ~* Z9 N! { - 请输入“11”,试着开启左上角的窗户。"
7 m0 P- J' T1 @3 {- a; e4 f - " ①②③( `5 G6 ^3 i+ P0 h z
- 1■■
! T; R% Q4 M7 X5 d" J - 2■
) G. H3 m2 D7 S - 3
8 U& B; R* @' d+ y - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。. u& ?* f: H9 b& s. c* n
- 请输入“13”开启右上角的窗户。"
+ ]* F* {! Y1 j1 _$ x0 L/ O - " ①②③
3 @4 Z% Z$ ] U" \) y - 1■ ■
3 r5 M) T0 }9 i/ y- K - 2■ ■
/ L0 J4 P, v/ V9 ]' L0 D - 3 " i" E3 b k# Z7 I
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。, _2 x4 b: d( Q
- 请输入“31”开启左下角的窗户。"
& T: e7 Q i, Z; c! o5 Y, S - " ①②③3 Z% E3 A5 ]( Y, O! q# S
- 1■ ■" @5 s9 r2 N! G/ b
- 2 ■
5 a7 U$ g6 D: g" N1 r; E3 B - 3■■
! Z6 e% Z7 a# A' m4 L - 此时,总共有5扇窗户被开启了。$ J+ z. |% {1 M D* `0 m% X" n0 g L
- 请输入“33”开启右下角的窗户。"
, \3 ?& } b% e - " ①②③
H5 B- J" t% l3 p0 _ s H& P/ p - 1■ ■
, l; _, ~* j/ i* n6 j - 2
6 C. }) T9 f6 k$ B - 3■ ■
( W6 P2 n* ?3 m, V7 Q0 h - 现在,只有四个角落的窗户被打开。
, K1 \# i: m( Q& b - 请输入“22”完成最后一击!"
( h5 t! ?8 T B' g' \. Y - " ①②③& H! i2 k, j+ y; m
- 1■■■
* y4 ^2 i4 _1 N# N$ U7 [: r9 A$ I) F - 2■■■
4 S" e" K7 e$ E% l - 3■■■3 @3 k# k! \! d) ^4 c
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")7 [# B) _( z1 Q: u1 x
- 3 N% Y2 l8 w' l) Z6 b2 p" _
- ;;; 棋盘9 y) m/ {* g8 o9 U! |7 y( S1 k
- (defconst *wechat-5x5-white-chess* 12288* ^7 ]9 a! ]7 u) I0 }# c9 A
- " ")
5 g g% A/ E! F" u6 _( |1 ^7 u' n- d - 7 |! _ t* f; T! P1 M1 E/ x
- (defconst *wechat-5x5-black-chess* 9632
" O4 J1 J) r3 S: L! j) P$ f, }4 b - "■")* b& m1 D' ?2 }: l9 p/ H7 }
! R6 ] Z8 T8 J- l' o; W- (defmacro with-board (&rest body)0 E2 d5 I& |) ^9 \& ?: J
- `(with-temp-buffer# V5 ?( ^& [1 |( J' Y
- (unwind-protect
" a( y P0 n$ q4 b* N, n - (progn' L+ o7 Q6 ~8 m/ q; V+ T" R) K
- (if (session "board")- g& ~$ A" u0 m' z% R
- (insert (session "board")))
" }) y& s3 H+ x1 j - ,@body)* A. @6 }7 M6 u0 \3 j; I/ W
- (session "board" (buffer-string)))))! t1 N, M- e5 p' D7 T* f
- . X& P3 V+ Q- Z4 m( m6 _
- (defun board-init (size)8 E/ w+ ~; T! C n+ C# N
- (session "size" size)" x7 d, _1 ?: {1 {9 V
- (session "step" 0)
, X5 ]5 q Q( t1 M - (erase-buffer)
7 A; f1 @! o/ e8 } - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
, N( j4 L& F* d9 I$ V- @4 p - (dotimes (row size)" z4 R9 P3 |3 F# b4 t7 B6 M
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*))))). m" W) ]+ V# B7 W. {% u5 I5 j
3 W+ H& p; G! v3 m: P% J- (defun board-contains-p (y x)
0 ^1 N- I8 g0 E2 F4 S- r1 h! z - (let ((size (session "size")))
3 Q, h) ^% v8 | [! L& A* ` - (and (<= 1 y) (<= y size)
- K2 P1 S: f( g9 j* {6 Z1 f - (<= 1 x) (<= x size))))
" d2 n$ O1 J7 p' n - 8 V* P% ], B3 z, @
- (defun board-toggle (y x)8 e1 f* I( K, b
- (when (board-contains-p y x)+ e8 |$ R% m; E9 v h5 k
- (goto-line (1+ y))
$ B w- ?( \9 K# c ]* [ - (beginning-of-line)
d/ N3 N& S( P4 M- q3 \# { - (forward-char x)
" M% t0 ?6 W: c; b: X - (insert (if (= *wechat-5x5-white-chess* (following-char))
, C O9 {0 D6 W% ~* @ - *wechat-5x5-black-chess*) w1 b! w) C8 O
- *wechat-5x5-white-chess*)), h1 b8 C) f; @ [+ j) u+ z
- (delete-char 1)))) {% @7 C1 Q5 N8 @# M+ u6 R: ~
e; S7 [' E: s Y: x- (defun board-put (y x)
1 j* l+ d: p/ v2 K - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
& o6 d( z* E0 `5 K3 ?5 l$ p - (board-toggle (+ y (first dir)). ?* y0 c( A* t' W' U
- (+ x (second dir)))))4 y% p5 G0 b) y% K6 L" }% L* X
9 I# X* r4 c; f7 m4 N/ }9 N) y' [- (defun game-over-p ()
2 h, |, Y( t2 r5 h1 T$ a - (beginning-of-buffer)/ g$ V0 U6 {- _# }, {7 p2 Z
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))$ n# e, g. k% y+ Y
- 8 t# g+ E4 P4 g6 c4 H/ h! l
- (defun board-show (): u9 T$ H4 p& e. w
- (with-board
( ?% Y! U' Z1 ^ - (concat (buffer-string)
2 L9 R- A+ S' Z( D9 S @5 _ - (if (game-over-p)# X6 Z; ~; @( u, y# M6 @
- (format "共%d步,输入任意内容返回大厅" (session "step"))& v5 W, C: T4 G4 r0 n$ `9 Y0 C' V6 R
- (format "第%d步" (1+ (session "step")))))))8 ^1 i. Z% w& o2 h4 X
7 X* O! X6 J! _/ g* L- l( O$ h$ u0 T- (defun board-position-parse (cmd)
- C J2 c" p( i4 M. o6 W6 f& G p' v - (if (= (length cmd) 2)/ z# [& V J5 |3 Z" Y
- (list (string-to-int (substring cmd 0 1))
2 L- f! O+ n8 ]8 D - (string-to-int (substring cmd 1 2)))
" G% }3 y' t1 m3 _7 N: }- S - '(0 0)))
- E& O# l9 O( Q5 l/ y8 w( n
5 p9 t# l! k2 X( F- ;;; 游戏房间
; t1 c! x* Y7 X9 _9 | - (defun game-room-init (cmd)9 _0 s6 v: v* H9 I) ~; i
- (let* ((middle (string-to-int cmd))
( Z# Y$ B/ D" h8 d - (size (1- (* 2 middle))))2 K% f" ~7 S: {4 Q# \" B: m
- (with-board# B7 `1 z! I, r8 K
- (board-init size)
9 k* v; x9 i& I( O" Y: I1 B - (board-put middle middle)))
9 W9 j* p) G) W$ A - 'game-room)2 Z3 Y6 F- o4 R
- : U1 W* B$ {' N/ P: K; X4 j4 b
- (def-room game-room A' v" y* T# v- W' `2 W
- #'board-show5 ?1 o6 U+ P& Y$ r( ^
- (t (lambda (cmd)
# D+ `" Z" A. o% I6 b - (with-board4 _8 L( S3 q+ U+ b$ J$ w
- (if (game-over-p)
( u5 a7 |. G- B3 ~( `% k: ]+ ~ - 'living-room/ i& J G& R) q* D8 c9 d" u
- (destructuring-bind (y x) (board-position-parse cmd)
% L- h. ^1 \0 _5 s - (when (board-contains-p y x)9 O g! ]$ D0 \- e
- (board-toggle y x)
- r) _3 s8 `6 K, j, q - (session "step" (1+ (session "step"))))( e3 @0 @' k% E6 Q' g6 `' w3 n4 i
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|