wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
+ r& u B, e4 o* L借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
: b$ j' b" K8 m, W, 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;">;; 定义新的游戏地图, n u2 }$ c1 u0 f9 Q: `
- (def-map "/game/5x5.el" ; 对外开放的URL
. c& O2 @& y. ~! Y7 ? - 'tutorial-room-0) ; 默认的入口
; e- F! p% t/ V6 C2 N - ( t0 G# K" Y2 h2 k. u, O% k
- ;;; 游戏大厅! q! `4 e+ W- F- q
- (def-room living-room
- a) w0 {( d1 t5 G, A - ;; 进入该房间后的提示语# Z. {1 n8 h3 w/ X
- "1. 教程% I1 f5 R. I8 d8 k9 N @/ n
- 2. 入门(3x3)/ d0 x A) Q- t% C
- 3. 初级(5x5)
' x9 s/ p* {2 o, o - 4. 中级(7x7)
6 ~/ m y4 m/ ]2 j- | - 5. 高级(9x9)9 n* t* d& r# \. r$ k( e
- 0. 关于作者% v' I5 y9 [) V+ p9 i
- 请选择1-5开始新游戏:"
|& x! p" n0 C# O; z - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名! L2 I4 w' {4 }7 B; ~
- ("1" tutorial-room-0)
& F$ S _9 s' p2 g2 r! @; X D - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配8 x. |( _* u# V$ x
- ; 相应的返回也可以为函数,动态返回房间名4 |2 c. k% g2 t; T* I! L
- (t living-room)) ; 如果条件为t,为永真9 @1 D, S" X9 o8 _( Q) J4 I6 R$ \
( `& E7 h J/ S' {' n/ n K4 ?+ X1 j- ;;; 作者信息
z/ s3 y4 A! W8 F - (def-room about-room" h6 e6 D. j I8 N3 |+ U, x
- "作者:redraiment6 e: ~7 `* ]( y8 [
- 微博:http://weibo.com/redraiment
+ p( n* u" u9 Z5 Y% J - 有任何建议,欢迎在微博或微信上联系redraiment。* m& D% Z! V% R* B# l: J, D
- 请输入任意数字返回游戏大厅。"' u x) n& u) k/ t5 U
- (t living-room))
7 w. r9 G1 \2 N/ C- Z# W6 z# h, Z - ; s1 I- S( N0 I: [' c0 `9 ?
- ;;; 教程0 p1 f2 |* R5 z& F5 j$ j
- (defvar *wechat-5x5-tutorial-rooms* 0! V6 s* }( U* l
- "The number of tutorial rooms")$ [: u+ t: t" S& y0 a5 {
- # J( K* U, F2 I. y
- ;;; 简化教程的定义
7 Y6 R, b3 b1 ?" S8 V `7 E/ {9 W - (defun string-last-line (content)
$ X T+ C3 A8 }, `6 f% a - "多行内容组成的字符串中的最后一行"
2 l. v! E/ ]0 b2 @: M1 I0 j" m4 Q - (with-temp-buffer5 Q6 ?. Z# w& P. ~3 P
- (insert content)
7 C% t7 h i7 x) W3 C5 ~ - (buffer-substring (line-beginning-position)
8 @1 M7 k) A* t( j( m, J# k - (point-max))))
/ ~5 Q0 |1 A! t$ Z& W) m% a2 G
! q) q' U9 X" `' [- (defun def-tutorial-room (prompt)3 L. s3 D' [5 j
- "根据提示语自动生成教程房间。
* }* Y7 Q+ ]9 U# |1 p9 R" U - * v& d9 t/ U* O: p/ {
- 1. 提取最后一行作为问题;- x* T. m; }; R, B4 `- z% w( r" \
- 2. 分析问题,获取期望用户输入的内容;
, d* ] A' E* M8 k- U- C& J" ^ - 3. 定义教程房间和重复提问房间。"8 `5 N7 }9 {& J. s, m% X5 u
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))8 S+ Z/ S) o# A
- (repeat-room (concat room-name "-repeat"))
0 Q" w5 J* P B' A( X5 X - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
. W+ z- _( G- E, T0 E - (question (string-last-line prompt))& G. ?" w$ Q; w* h0 f: R! W2 s
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
; |$ ]# Z9 S# _$ ^' S, q- c - (match-string 1 question)))
! v) a# X) i% d* v r6 G - (doors (if except
- i7 c" L V' j/ M - `((,except ,(intern next-room))
" J7 @3 n1 k d8 d - ("q" living-room)7 r8 D3 j" E$ {, t: L/ ^
- ("Q" living-room)( i1 c$ B* u7 C8 ^( ^
- (t ,(intern repeat-room)))7 R8 z( V7 I9 z$ M
- '((t living-room)))))
( `. @8 C) z& s+ c4 h2 Q" N - (def-room-raw (intern room-name) prompt doors)
) F2 y: Y/ ?. g+ @3 k8 x - (def-room-raw (intern repeat-room) question doors)))
" b: {/ O1 t3 T e$ n
% Q0 `! c# k0 }! T7 y R- (defun def-tutorial (&rest prompts)5 M9 r' A. W `. D# }
- "批量生成教程房间。"* _5 u& b& I) Q: H* h
- (dolist (prompt prompts)
8 S( B( J! k# J - (def-tutorial-room prompt)))
# I) V" \& [5 K0 ^. P/ v. F) _4 T" {$ U - . f. i! K1 t/ |0 G
- (def-tutorial& Q- L l8 y9 b, v2 z
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
6 l: K- M0 u; s8 p' e& u" | - 1. 教程7 S6 ^1 f T9 v, l
- 2. 入门(3x3)- W3 t' m: n g7 G8 q5 S, B
- 3. 初级(5x5)
3 D$ j( u& y, n$ M - 4. 中级(7x7)
6 `7 f% O2 g, N# x9 l1 A X - 5. 高级(9x9)/ @- Y0 N# S1 W
- 0. 关于作者1 A$ C7 G& Z* O: g r0 Y
- 请选择1-5开始新游戏:
( `& C8 j* I7 u# ?" [ - 您现在正在游戏大厅里。, B) o, U& n# f4 `: k3 w
- 请输入“2”进入入门级房间" I$ l! {0 d9 i' z0 E
- " ①②③
7 ^8 q# [ Q+ V+ B1 N - 1 ■
4 E* \! c% E5 t - 2■■■0 D9 P3 B) I0 Y! u
- 3 ■ 0 u7 n: a% K/ b: m
- 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
~% y7 |6 _1 x1 ~- r- S - 请输入“22”来关闭第2行第2列的窗户。"2 Y- Q/ Z2 ]6 |$ e$ w( e
- " ①②③
" H k1 S0 l0 [. L/ Z - 1
Z) R$ H+ H( ]" L8 }& w - 2
# K% T3 N j: x9 I1 w - 3 % {2 G' D5 S5 a3 w3 w
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。1 w1 ?/ T* A1 C- ]* n' K9 F
- 请输入“11”,试着开启左上角的窗户。"
+ C5 [) `9 ]- `7 C4 j - " ①②③$ t B$ O8 {1 ?7 c7 C) I0 u
- 1■■ 8 l( h* J5 \( b, {! j
- 2■ , A Q( ^9 H3 h: `
- 3
8 N" i9 x9 m1 g' f- { - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
% N6 q2 @7 o* d7 K" g - 请输入“13”开启右上角的窗户。"+ W* W% f! {# A5 K' x9 u1 S# H d
- " ①②③
1 |- }! ~" h+ o, d% _% n - 1■ ■
! X S2 P* E ~ - 2■ ■
. k. i1 I7 w- O9 d+ A" ~ - 3 6 }' h& c- ]( z+ G; d) Q) W
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。, p, F; j# I$ e4 F) {5 I
- 请输入“31”开启左下角的窗户。"% c; k# p( }2 K% |3 a v
- " ①②③* V' L9 ~. R6 f3 [" I% ?
- 1■ ■( R3 ?( h8 g/ m' |, u: G
- 2 ■2 [6 e i6 D% Y$ I
- 3■■
) C( {/ ]/ d( S$ Z2 N$ S+ |* U n3 I - 此时,总共有5扇窗户被开启了。8 d ^+ n: {: H; S
- 请输入“33”开启右下角的窗户。"8 r% Q- o& _1 [2 w' d1 z! G
- " ①②③* I+ [9 Z" t3 [. n% c6 U
- 1■ ■
* x) z" y! [1 H& k4 n - 2 0 \' H; Y4 m R2 G2 E O
- 3■ ■
. I: S2 Q& H/ c4 } - 现在,只有四个角落的窗户被打开。
+ W1 u4 O& J8 k - 请输入“22”完成最后一击!"6 @4 s' N. p. G, D% t
- " ①②③9 g+ B) Z) [; A' f3 P! s* R( d
- 1■■■
( U' U a- t+ o! t4 g - 2■■■/ K% U6 ?3 s' A+ C9 x# L- z
- 3■■■
+ e2 z6 h- j: D% s - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
1 P Z, k) D0 ^( t9 \ - 7 |, L$ y7 Y n0 w$ I. M
- ;;; 棋盘
1 i$ C$ ]' E& c6 X; f& f - (defconst *wechat-5x5-white-chess* 12288; Y0 e, ~; J% j5 M& D& h
- " ")
: u& g- k3 Z- ~, B" T - 1 U7 B, w: U j7 O3 d+ ~) Y4 u0 }
- (defconst *wechat-5x5-black-chess* 9632. o3 N0 r0 s L$ a+ b1 Q$ V
- "■")* M" @0 i$ d/ P$ D
- ( y$ a0 Q6 y% Y2 \4 F( I- Q% H# D/ B
- (defmacro with-board (&rest body)! k% o0 ]2 v6 ~2 U" s3 m* ?
- `(with-temp-buffer: B& s# `0 W& p- _
- (unwind-protect
4 v' H }0 Y+ M& T J( Y - (progn
" I+ E( F5 R9 S7 K1 { - (if (session "board")6 \' i! x! u+ L+ t; ?; j
- (insert (session "board")))/ a- @& c ]5 Y4 u' M
- ,@body)3 @; [# _& V. O! v
- (session "board" (buffer-string)))))# }% L0 u, U; n# W4 O1 ]
- : Q$ W1 C7 E0 U& W H' Q, ]" D& D' |
- (defun board-init (size)
6 }1 t' B2 T: ^! H - (session "size" size)
( R" L- U4 b5 g7 X- z - (session "step" 0)
/ T2 _9 z* c3 U0 J5 r - (erase-buffer)
3 H1 o, s' u1 ]5 L6 V - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
% a$ P; _; l4 \7 X4 \' }% o0 z - (dotimes (row size)6 F+ _* B7 a5 j
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))0 G4 X- o& N6 [# ?; D
- * W+ e- W! t' W4 u$ [
- (defun board-contains-p (y x)& [% @8 V1 l9 e8 ^; R
- (let ((size (session "size"))) ]) P! y* x8 ~; p" x+ G8 V: |! f* a
- (and (<= 1 y) (<= y size): p% d$ s0 U+ r$ l. V* B7 \
- (<= 1 x) (<= x size))))5 H* [* R7 C- \: U! R6 Y
- % H% ]5 e8 G8 U1 _
- (defun board-toggle (y x)
% q1 o: C5 Y k; {9 U - (when (board-contains-p y x)* [3 F; G; ~' B5 \5 s2 c+ [* k
- (goto-line (1+ y))
- g r+ q! g( P1 b% W - (beginning-of-line)
% _) \7 q2 {( ?5 d0 l4 L* B - (forward-char x)
6 ]; r( t3 \. L1 e - (insert (if (= *wechat-5x5-white-chess* (following-char))2 ?7 q$ t6 Y$ e" d6 h# J6 ]! g
- *wechat-5x5-black-chess*
/ I( U3 L: ]2 [8 g3 }" o; d5 N2 Z5 ^ - *wechat-5x5-white-chess*))" j& D, [2 v/ ~: }1 O
- (delete-char 1)))
3 {1 j+ ?! X/ R8 z
9 h6 q: X5 M; s; n' W! L, J- (defun board-put (y x), H4 I$ k5 P" c0 d* b, w- K
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0))). A4 n! z2 f- C2 F; s! N8 ~# g
- (board-toggle (+ y (first dir))
. j1 M7 p/ L/ s - (+ x (second dir)))))
( P( C* W0 r4 u) h5 z - ) a" R( d7 v4 @+ u
- (defun game-over-p ()
& b* ~6 a* F8 I7 m - (beginning-of-buffer)0 C6 e3 r1 @' N5 H, R
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))" L% u! o; ^. r6 t& a1 j' E
- % o3 n9 _: W; b# y: A" g6 z2 v- ]
- (defun board-show (); B. W' ?1 Y9 N0 {5 P8 n3 f+ U; `
- (with-board8 U3 j$ U5 S" v1 j: C, m/ @+ M
- (concat (buffer-string)
! h7 ?& T5 Y& A" N7 x* w& } - (if (game-over-p)4 m7 m5 l7 X" ^4 e$ ]& C& {
- (format "共%d步,输入任意内容返回大厅" (session "step"))
; t* d/ A* l: O1 _7 [ - (format "第%d步" (1+ (session "step")))))))
. `8 D: | V2 k7 C! T- S p2 @; {' {! e
' Q. {0 `6 U! t- (defun board-position-parse (cmd)7 Z, S5 O+ a$ k/ M2 A1 A# E
- (if (= (length cmd) 2)+ `* H! \" E$ L( \4 t7 |* t1 ]+ k
- (list (string-to-int (substring cmd 0 1))
6 } n" _7 j- f' N2 ~$ J9 n) b" `4 W - (string-to-int (substring cmd 1 2)))
. ~3 Q( x: L9 F/ q - '(0 0)))) P" {; i' G+ I! `5 R+ s0 h* x
0 i. h: F2 D+ N9 E7 B! `- ;;; 游戏房间" A' O* u' r* U3 o O3 ^
- (defun game-room-init (cmd)
4 u8 i, q5 W9 U* j4 l6 h4 A6 j& I - (let* ((middle (string-to-int cmd))
" H2 C. c7 _! d* k5 r/ t - (size (1- (* 2 middle))))# }# r) c1 w! O2 L: H k
- (with-board
7 W3 {2 n1 i( G" C4 r5 p8 I% T6 O - (board-init size)
9 @+ ?9 N* n% z9 K - (board-put middle middle)))0 }: d# V( w+ W" M0 w+ s
- 'game-room)
- W# C% ~$ C9 e: A4 [7 Q! n$ I - # R" f$ W* Q3 s# N0 c
- (def-room game-room
5 S+ G! u3 }9 f( o/ \: B7 i7 I% { - #'board-show" [- i% e, {. Y/ u) `
- (t (lambda (cmd)
6 \' ~& U9 g1 I e) u - (with-board: A; } A) Q: T0 U+ K; P
- (if (game-over-p)$ j- Q* g, d k- H4 B& B) C! f
- 'living-room
. n/ x! _, e8 D% j2 a2 ?- r - (destructuring-bind (y x) (board-position-parse cmd)
* z1 J3 I' g0 O# R2 D$ C - (when (board-contains-p y x)
( G1 L; E% }4 C/ D9 }& \7 h - (board-toggle y x)
, y/ c9 i/ Q' A. v" k+ m6 l% h1 Y - (session "step" (1+ (session "step"))))
' A; H+ J* K. S - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|