wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
' ^& _# T+ h' o" X' A% d8 y7 z% Y借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- 6 T5 _2 }; u7 x9 O
- <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;">;; 定义新的游戏地图: O( ]! m$ V/ z& P- k
- (def-map "/game/5x5.el" ; 对外开放的URL8 @8 u6 {2 r$ k
- 'tutorial-room-0) ; 默认的入口
& ]9 i- k2 X* q' D. n3 j7 N$ f
b: `8 D$ c: H- ;;; 游戏大厅
& t( g0 i7 _/ i% N: Q" \ - (def-room living-room; z# M7 S. b& n: x$ g# A
- ;; 进入该房间后的提示语
' ]" Z2 Y6 x! Y4 s, k. J. K7 r - "1. 教程
) G# E7 E/ R+ H m* H - 2. 入门(3x3)8 S( w/ q# c% i4 n5 F0 {
- 3. 初级(5x5)
; z$ [6 j, C+ o6 y - 4. 中级(7x7)
. t; x/ b8 ?& I, a; o - 5. 高级(9x9)' V0 I: L6 ~$ g/ J, {8 E& l
- 0. 关于作者
: @( F1 ?) z' h( }/ R4 B - 请选择1-5开始新游戏:"
, @: f1 K9 ]# M8 i6 G - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
+ o, v3 r8 e8 g8 t, o F$ \# F - ("1" tutorial-room-0)) G H* z) N1 I8 g6 g5 r, m. {' B
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配; X* |4 M8 G+ c) I
- ; 相应的返回也可以为函数,动态返回房间名$ l0 q- [. | g2 x( B
- (t living-room)) ; 如果条件为t,为永真5 J0 d' \- S( ^" P+ i
& W0 s8 o! A: [$ Q! i- ;;; 作者信息% {$ Z' f/ X4 K" s6 Q' K
- (def-room about-room
% U5 v, _5 a9 K5 v8 J% j9 o% L) M - "作者:redraiment
& T5 a6 k& i( L6 B- E8 S5 [ - 微博:http://weibo.com/redraiment
1 W8 l. \7 j" {* Q( A- ]: P0 } - 有任何建议,欢迎在微博或微信上联系redraiment。) I! V$ x& H; J. {) c
- 请输入任意数字返回游戏大厅。"& ?# O( L, B. j
- (t living-room))
' ^4 x. o0 v) }7 o7 a
: c, P6 ~( B) v" @7 b; S/ V/ I; \- ;;; 教程
: R. J- A# e6 F6 Z; Z - (defvar *wechat-5x5-tutorial-rooms* 0
8 ?% H/ j( |, M, v8 k3 f# W0 d - "The number of tutorial rooms")* y3 q+ l' H* {# i8 q6 ]
, g) L5 }4 D. r' L! _2 A- ;;; 简化教程的定义, Q( D% @2 K& j+ B
- (defun string-last-line (content)
/ r2 B' N. \ S7 d. b- G9 N. j( t - "多行内容组成的字符串中的最后一行"# l* G( o9 t) F9 }8 p
- (with-temp-buffer% h: X) E; G- N
- (insert content)
. m, N4 G8 v( Z0 N3 }# \ - (buffer-substring (line-beginning-position)6 ]$ e) q4 x8 I$ K1 {% \- H% u' {
- (point-max))))& M5 q& r! u# h5 y
- , P8 K+ H2 Z8 v+ n+ d
- (defun def-tutorial-room (prompt)
2 Z9 L0 k* ?* d& l5 P+ h# l1 P2 q# D' Y - "根据提示语自动生成教程房间。
+ G" H8 H0 C: | z/ t! a( ? - ' S7 x! E+ u" }8 W! D: `! V
- 1. 提取最后一行作为问题;
], M, `" A/ t% k- h8 G - 2. 分析问题,获取期望用户输入的内容;
/ |0 ]" x5 W6 E5 ?% m0 T; B1 T - 3. 定义教程房间和重复提问房间。"
- `0 f2 ?; L( v9 R- E# H - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))7 @# p8 M' i$ m, X6 s$ Z* Y/ h
- (repeat-room (concat room-name "-repeat"))
9 `6 }$ u/ p1 m1 c! r# [ - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))+ }: s4 q, J8 t C( u9 ]9 l
- (question (string-last-line prompt))
2 l6 h9 T1 o; Z - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
# s% ?1 H! p V2 s# h5 Q - (match-string 1 question)))
) C: {6 Z6 n$ x6 { - (doors (if except, U2 s, d. Y" x T
- `((,except ,(intern next-room))
6 u- c2 A* \9 J) ^5 P - ("q" living-room)
+ ` R6 ~ b* [! e9 m! ~& a - ("Q" living-room)
, |7 H1 n) p6 S' f% \ - (t ,(intern repeat-room)))6 |4 T/ I' B L4 c
- '((t living-room)))))# U6 K d+ p' s/ c, ?0 p
- (def-room-raw (intern room-name) prompt doors)1 T1 I7 N# K" s5 e. u) P
- (def-room-raw (intern repeat-room) question doors)))6 K! t- E& S& `) E
( v# `7 _, ~8 v- (defun def-tutorial (&rest prompts)+ A6 W4 q. C" i, U" f( Q. k3 `" P+ M
- "批量生成教程房间。"
+ |- [. [4 G8 m# _, e' d - (dolist (prompt prompts)1 a- J) {: M; e2 Q4 y
- (def-tutorial-room prompt)))
# I. D$ i8 M# h W- O: u - # E" ^% T: K0 j
- (def-tutorial5 B; @. t9 T! _ C0 G# o# O4 Z
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。" I2 y n( [: t( P
- 1. 教程
6 {. ?9 k1 u' N - 2. 入门(3x3)/ r. K3 u) H/ d- e* \2 U0 F0 Y
- 3. 初级(5x5)9 q( h$ |. ]& w; ]; m5 t% C% X
- 4. 中级(7x7)
9 E6 X) k, Z2 l% L. e - 5. 高级(9x9)( b6 e3 }' L8 x
- 0. 关于作者
+ H# F2 }" q$ X3 W; `, a3 J - 请选择1-5开始新游戏:
# y! L, `5 p9 e& ]5 }$ u - 您现在正在游戏大厅里。 H0 b$ @- K; h& r- B5 m
- 请输入“2”进入入门级房间"
0 |2 E) H6 X K6 i - " ①②③
8 L, h# x$ M- u7 c$ W) B - 1 ■
5 ]1 h* R r2 A5 L; ^; w, L6 ] [6 s - 2■■■: j; D+ y$ Y" Y+ K! @
- 3 ■
) Y5 D- h& ]7 D" e - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!+ M4 H* k% `, W9 R
- 请输入“22”来关闭第2行第2列的窗户。"
$ L) f5 N, S6 m% D; ?$ P - " ①②③3 {5 U2 r, t4 n; G
- 1 2 Q/ U: X* {4 }( p3 N
- 2
# W4 l# l% s; a/ f X - 3 ) V( U8 Y% l! I; \
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
+ y4 z9 k9 [6 l5 ]9 P c% t - 请输入“11”,试着开启左上角的窗户。"
8 N/ w( v* H0 |2 z. B/ R7 W - " ①②③
3 K/ J r a( E7 O$ `6 E - 1■■
& X% l. W, a! K" {+ Q8 N - 2■ : g2 I4 M( _6 Y6 ^0 L7 d# q1 S8 V
- 3
7 E9 @$ \. B' q9 M; T7 l - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
) ?& O+ T7 J3 d. L! a3 x8 o - 请输入“13”开启右上角的窗户。"
3 e" B- V9 H/ n" i - " ①②③
& M. ^" W$ l2 \( E - 1■ ■
7 `6 |, O( C! g' Y9 X - 2■ ■+ x1 `1 k5 B, K2 G$ I
- 3
) C# g# V, O; b6 ?# F - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
& ~$ U( c' p# U/ N1 I+ Y- a4 o - 请输入“31”开启左下角的窗户。"
; x8 l$ A2 z Z( V - " ①②③
; ?: v; [" A3 T! j. m: j* q7 x+ r - 1■ ■
; G! r$ k U- S0 q! w6 B - 2 ■
0 i9 n8 T* d" G) f - 3■■ / o: _2 G4 v7 J* {# X# g
- 此时,总共有5扇窗户被开启了。7 ~7 `9 e7 n$ g# I$ U
- 请输入“33”开启右下角的窗户。"
& U2 [# P, Q2 F% m- l0 ] - " ①②③
; E7 ]$ I+ ]3 B' l& A - 1■ ■
! O( P- F! g# Q1 z - 2
9 G8 W. e+ t+ c! G* B$ m) c- |* N - 3■ ■/ C N4 r; t, ]1 q
- 现在,只有四个角落的窗户被打开。7 M; ]. V8 Y4 H D0 a j0 j
- 请输入“22”完成最后一击!"
: L# l( _/ d; r1 B/ L - " ①②③. J2 t0 k6 I/ z# c3 E, C, A& |& X
- 1■■■
9 g% W! `. x& u0 h - 2■■■4 I" k/ m5 I# P- l
- 3■■■! v- g) N+ [0 `! U1 m
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
- _8 B5 O5 Z, ?4 N9 }+ w - " o) n( n, H5 z G' ]
- ;;; 棋盘* @) [2 p- a7 h8 z; ^ i
- (defconst *wechat-5x5-white-chess* 12288! X4 C2 O1 o4 H+ k
- " ")3 q* @9 k+ `/ b. R L1 |% B2 J
- 6 n: b d+ y$ h
- (defconst *wechat-5x5-black-chess* 9632
, P+ j+ H9 }' Y0 b9 d/ | - "■"); G, E: a. Q, |) C& O& B- V
- $ R; B' J0 {" ^' ~; X
- (defmacro with-board (&rest body)' e: Z) v% R& ^- g9 n: o5 ^
- `(with-temp-buffer2 ~5 m5 w$ D, R) k
- (unwind-protect
. @8 Z0 W5 N8 _! c" y$ V - (progn2 [6 S$ i$ ]* H; l
- (if (session "board")& c9 V( ?" F1 G F" |7 f! N q
- (insert (session "board")))
2 X( U8 [/ }3 L& m% e$ D - ,@body)5 a/ e# ^7 H. D" F0 \7 {
- (session "board" (buffer-string)))))
( \" s, f' L, W Z
- X1 I9 c5 s3 Y- A9 h4 r- (defun board-init (size)
& O- A& h0 R& e; j - (session "size" size)
# K# @5 }' }$ r/ z( w2 N - (session "step" 0). j' W2 E, j6 z! N+ w
- (erase-buffer)/ P1 l: y+ H" L3 I( X8 a
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
# c) [/ V/ G3 W. N' Y - (dotimes (row size)5 T2 w4 ~8 n" p0 `( v
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
: N$ p2 _1 X6 ~ - 8 p: D) n# X- }! B$ t
- (defun board-contains-p (y x)1 z) k- l2 i+ m$ @, E1 D' R. \% ?9 I
- (let ((size (session "size")))
) ^# ?) r2 ?- w3 V- l - (and (<= 1 y) (<= y size)9 `& p2 }, ^+ n" J
- (<= 1 x) (<= x size))))
' e4 r9 p2 v' T i" a5 B. {' J - S9 ~. q: j1 m; x1 B
- (defun board-toggle (y x)! M6 y' ?6 `/ c4 {
- (when (board-contains-p y x)
# [! x9 i' k* ~# q - (goto-line (1+ y))8 l! |9 n- B% L4 @! \8 ?1 ]
- (beginning-of-line)' P$ A8 R5 x; m- x! R, t
- (forward-char x)
$ r9 N/ H7 E/ }+ F: t - (insert (if (= *wechat-5x5-white-chess* (following-char))
0 Z+ b0 ?5 I* ~ - *wechat-5x5-black-chess*' A& R- X4 @$ i1 s$ I
- *wechat-5x5-white-chess*))0 E5 O$ }6 j" ], ?& P1 }
- (delete-char 1)))/ a- J3 K4 J- [$ {- N7 G+ y) `# {
- 5 e# U" y- x$ s- P
- (defun board-put (y x) n& y2 A: f T
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
; M) ~( Z6 [9 M2 m, K4 e0 U - (board-toggle (+ y (first dir))8 f1 q) l k. X: Q8 A! Z$ u
- (+ x (second dir)))))
: P9 ]% |6 W* K; ~# R% _
1 B2 z6 _5 N$ ~- D. I5 B- (defun game-over-p (), k; `4 `; \! `( f% W' Q1 ?, k4 S
- (beginning-of-buffer)
& K# ]9 x4 V) P M - (not (search-forward (string *wechat-5x5-white-chess*) nil t))); e5 N( Z6 }* `% f$ q
, }) G7 d5 h/ u! G% W ] n3 e* H- (defun board-show ()
/ L" C; w8 \' y8 k1 l; X6 l5 X; s, l - (with-board6 @* u( l( P- K9 D5 }1 o
- (concat (buffer-string)+ q, q) N* k4 p$ ?4 X
- (if (game-over-p)+ u- K4 U6 F* ^/ [" N; f) A: K
- (format "共%d步,输入任意内容返回大厅" (session "step")): m. c3 ~1 M$ C; ^
- (format "第%d步" (1+ (session "step"))))))) X$ j# V# L" s- t. S; X
! k# \" i1 P( `- (defun board-position-parse (cmd)& v* d$ }- D- |( U
- (if (= (length cmd) 2)
3 z3 ]# q, l3 K0 }5 Q' T, p$ C" u- G$ F: J - (list (string-to-int (substring cmd 0 1)). o0 o+ L Q8 ^
- (string-to-int (substring cmd 1 2)))
0 O9 \5 `- u1 W0 l' z+ r0 p - '(0 0)))
, U& Z: z: {) Y4 k. ~% j
0 D& c! n6 H! q0 P- n- ;;; 游戏房间4 r9 v6 Y) X# ~6 A H
- (defun game-room-init (cmd)8 W, y, L- O' C% D+ N
- (let* ((middle (string-to-int cmd))+ J r' b0 `: X$ A x
- (size (1- (* 2 middle))))
; @6 e8 ]5 R4 \: V+ I' |3 J* S i$ N$ H - (with-board
+ O9 x- w5 }1 K( B1 b+ T" z2 v' Y - (board-init size)6 \9 ~. t: Y9 t i
- (board-put middle middle)))
8 w4 v, J1 {# K/ d$ S - 'game-room). d6 R: e# H2 h' I
( D7 s+ H8 ]6 Y( c: m- (def-room game-room
0 ]3 u' A) k/ ^' V6 F' G# s- u, f( n - #'board-show) h9 A% w# C/ a4 X' `2 H6 T
- (t (lambda (cmd)
" _+ k* M0 q; a- z. {. e - (with-board
* d) B1 \$ ^7 c0 Z' k& j M" H d - (if (game-over-p)
2 [" E5 E4 G2 u$ E! a8 c* n1 c - 'living-room
: C. d" P( }% Y2 q - (destructuring-bind (y x) (board-position-parse cmd)
- e7 s" k: Y3 k. b- Z - (when (board-contains-p y x)) W; _) L5 ?7 e
- (board-toggle y x)) Q3 V; B3 R$ F) W# g
- (session "step" (1+ (session "step"))))& f5 r( _) Y# l0 K
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|