wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。/ Z! E0 c5 h+ p8 p2 S1 P) {+ o
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- ; @1 ]" Q4 e5 K: V5 G/ r
- <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;">;; 定义新的游戏地图# Z0 W0 P) t. F' D. J
- (def-map "/game/5x5.el" ; 对外开放的URL; K* E$ j q6 x3 u
- 'tutorial-room-0) ; 默认的入口( O& u& f) X. i( l, n+ w7 @/ u! E6 P; G
- : j# P) J" ?) h* ~4 s [: K
- ;;; 游戏大厅' w. Y# Y! \4 t
- (def-room living-room# e- V+ g- @8 p. n. T7 u
- ;; 进入该房间后的提示语* `' W: N$ I, Y
- "1. 教程; n d8 U, ~7 G$ \# e- |
- 2. 入门(3x3)
6 k9 ^9 v' U% v S5 ~ - 3. 初级(5x5)' K* _" d) R8 C( e8 X5 S
- 4. 中级(7x7)
* u' c# q$ h0 i& G; N - 5. 高级(9x9)( m) n+ A. M5 H8 B" S3 K5 c% C* b
- 0. 关于作者3 M# s% T! D5 t: m3 }0 o0 f$ D V
- 请选择1-5开始新游戏:"
9 p4 \ r( m- } - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名/ D* x2 H5 T0 C5 Q
- ("1" tutorial-room-0)
: h+ F* q7 Y# m) T; G - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配4 p3 k' m* G/ p! U# A
- ; 相应的返回也可以为函数,动态返回房间名
" H, A9 p& i z8 q - (t living-room)) ; 如果条件为t,为永真
5 o, X u8 i$ N w5 N - 4 z, S3 ^3 F7 I2 [+ D* @2 I- \
- ;;; 作者信息
5 _$ t: Q! e; g - (def-room about-room( F7 G9 a: s* X; D& a% W. X
- "作者:redraiment
' w! p4 i' B# _5 e - 微博:http://weibo.com/redraiment
# k! P/ A' k0 t' e( a8 v! a- A - 有任何建议,欢迎在微博或微信上联系redraiment。+ N/ a h- l) m* ?" w
- 请输入任意数字返回游戏大厅。"
0 ?/ L( i) z; m3 g" v - (t living-room))
! u3 n4 }7 D( A6 k$ m! } - 5 y& P# c& l: P
- ;;; 教程
$ R8 w4 p1 [3 Q: A - (defvar *wechat-5x5-tutorial-rooms* 0
" Q% i8 \9 ~5 {$ m' s+ [ - "The number of tutorial rooms")0 A$ l0 O' w7 |. z; _% V* Y1 f
- 4 m3 ]' r" d, z" F
- ;;; 简化教程的定义+ Y7 e" Z* O0 r; i% Y5 ^
- (defun string-last-line (content)
- n- V I3 E0 K }* w/ T - "多行内容组成的字符串中的最后一行"
9 `- }/ d5 H& v - (with-temp-buffer- k5 C2 |, [: k, G7 [) O. l
- (insert content)- c: Q$ y$ z) N
- (buffer-substring (line-beginning-position)+ W5 i# h7 O: i3 y8 `5 b5 I
- (point-max))))0 o7 Z0 H% A& v+ d6 w! p2 v4 L$ A
- 7 u# M" f0 [2 P" y+ p! O: I* `
- (defun def-tutorial-room (prompt) c8 s6 ^! S! j+ T: s+ G# I6 u
- "根据提示语自动生成教程房间。
, S* V- f' X& T - 9 J) M- N$ y5 e5 s; D
- 1. 提取最后一行作为问题;1 q1 z3 u1 p! R' O$ f% p
- 2. 分析问题,获取期望用户输入的内容;
- v8 }) `# K7 q+ d - 3. 定义教程房间和重复提问房间。"0 W: B$ p* D5 X
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))- m( ^ {4 I0 x+ m5 t0 J8 F
- (repeat-room (concat room-name "-repeat"))) F! G' n6 E7 ~- c8 \
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
2 Y: J9 p2 O. I; X' F - (question (string-last-line prompt))
, B/ Q9 w# M; Y& W- i - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
% s+ F* e# D4 p - (match-string 1 question)))
+ |$ h' E- d3 D, c - (doors (if except
9 o$ g$ e; M4 J8 ~) a - `((,except ,(intern next-room))# O3 U! m- P) k- I& z/ f
- ("q" living-room)$ W: U5 Q5 s0 `" ]
- ("Q" living-room)
8 B/ o0 ?& `# h2 q0 D - (t ,(intern repeat-room)))' V, n* l: u, Y- ~& S$ z2 n
- '((t living-room)))))7 x) n# A; @1 z
- (def-room-raw (intern room-name) prompt doors)0 V; d' ~" A5 t, M( a
- (def-room-raw (intern repeat-room) question doors)))
! H) j' ^2 X, H- Q, }
' ?& y: O, ]2 V0 \" `4 [3 A7 J- (defun def-tutorial (&rest prompts)
E4 E' k; v5 x$ Y8 ? - "批量生成教程房间。"
* {; s+ V; d% ]+ L* z+ w+ L6 v - (dolist (prompt prompts)' b0 [8 x# g: ?' E4 M/ b) e
- (def-tutorial-room prompt)))" b. i& T5 `+ B$ I
- 4 v) j! U5 b2 i8 Y! b. \' Z
- (def-tutorial
# @1 Y$ T# Z" T! }" a6 J% s - "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。5 d- x! s3 C, E1 z. Q/ |* D4 f* W
- 1. 教程: T) B( k! I9 [+ a
- 2. 入门(3x3)
4 Z' X7 x$ W1 J2 F, n9 Z - 3. 初级(5x5)( ]- {( w# F* w
- 4. 中级(7x7)
: L/ `7 ` c9 N% Q. T, e - 5. 高级(9x9)
8 h+ n) p/ y. `" F& W - 0. 关于作者* f o. q; T* `4 W/ Z
- 请选择1-5开始新游戏:
. d2 B' p0 J# @9 a. \" c - 您现在正在游戏大厅里。
9 ?; I$ k. [0 E! [; O: n; q3 o- z# X - 请输入“2”进入入门级房间"$ S, Z3 @: W, u& [+ d; {
- " ①②③
" [, j; @/ A2 X$ V" L& o - 1 ■ 4 [0 |/ x/ e- q
- 2■■■
. q9 S5 ^6 p! _) J- C - 3 ■
4 A& n/ G3 n1 s3 s9 J$ C2 j - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
& L+ o6 K! ~. j6 ~$ ~3 u$ M/ d! U& ~( @ - 请输入“22”来关闭第2行第2列的窗户。"
$ v5 j2 s8 E& m$ V - " ①②③
/ M. Y" H0 U$ C- y% ], J b) g. Z' l8 G" R - 1
! D7 `+ t3 j/ {) Q& ? - 2
4 _6 b& |8 ^5 p v - 3 # I- O1 G% P2 B
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。$ j2 l' M2 b# [0 e
- 请输入“11”,试着开启左上角的窗户。"2 X+ d5 o8 x5 ?3 i( s* n
- " ①②③/ H3 Z4 f" j1 W' S! `
- 1■■ / o( F5 Q1 Y6 p4 g7 M* b! [
- 2■
; K* {2 ` ?+ `& J* l) H - 3
7 W8 h1 D6 u$ {& l5 ~6 V, h5 y; y - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
( q6 z* v; e. s- W2 e. E$ h - 请输入“13”开启右上角的窗户。"
0 b! Q+ V$ U+ S- V1 k7 {% p - " ①②③
' d8 C( q4 S- d8 V - 1■ ■
" C+ T- s" z# s5 B0 K! c+ {# ` - 2■ ■* G+ n3 o. R' N
- 3
+ `. ~/ z7 H+ w% C0 i; u2 @2 m - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。2 _8 n+ l# O* b" ~( G) X
- 请输入“31”开启左下角的窗户。"
8 q6 v. y- G8 [$ b& F! o& ? - " ①②③1 M+ _$ P: J5 g0 N" G B& Q1 r
- 1■ ■1 _+ K, f! W5 s
- 2 ■0 O# ]/ P$ C# ?: K
- 3■■ + ]" [9 e# v2 c( J& J
- 此时,总共有5扇窗户被开启了。: ^# b8 [9 g9 H
- 请输入“33”开启右下角的窗户。"( j& f; z! ?2 L$ i/ ^
- " ①②③
1 R( n7 h' m* y0 {: ~& Z9 J - 1■ ■6 e* p+ U' r5 x6 O- N9 i$ J
- 2
) [$ ^ n2 J, ?- l: `7 ` - 3■ ■
* r' O* ^$ E, q* p- y2 r - 现在,只有四个角落的窗户被打开。
0 y" x4 d, V P- N& O E+ N: S - 请输入“22”完成最后一击!") I& _& ^8 u/ E8 f/ C: Y9 r
- " ①②③' O# A4 z* P3 l% n3 C5 J+ v
- 1■■■
/ k0 ~2 o2 |! K# Q7 d - 2■■■
* p# {7 {. s6 [ - 3■■■
" ?" G0 l2 k) {. f8 F9 ^5 j - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")) \! F" F, r/ K. g& M6 Q
- 7 q; ]: S: `$ K. @: J! h
- ;;; 棋盘5 a# K2 F: y4 B [# {! I5 j, W ^
- (defconst *wechat-5x5-white-chess* 12288
1 I9 q% M2 `1 d2 g, G - " ")
( H% F- b. b- d$ }( y3 S6 E0 C7 K
+ G$ O; M5 ]( r. Y- (defconst *wechat-5x5-black-chess* 96320 m7 V; H0 d+ O7 |
- "■")
& p; J2 p% X2 W5 S9 L3 I0 P3 u/ C
( e/ c0 i- D) u9 C! c- (defmacro with-board (&rest body)5 X0 U# V8 W8 M) B
- `(with-temp-buffer8 x9 x8 G0 u1 H' k( p( r# S
- (unwind-protect, t A) N) r0 X" K, ]
- (progn: H) b" M4 o' y/ \5 n# g
- (if (session "board")
$ M- W! I$ ^* S \) w - (insert (session "board")))
- r$ b* J( s0 q6 A' I8 ]( [ - ,@body)5 T% h. ^9 x1 ~8 `; p I4 o' A
- (session "board" (buffer-string)))))) v+ O. L8 D4 T& [, [ q8 m
, N- z9 t/ R% D- (defun board-init (size)
& X, ~- M r4 O3 O - (session "size" size)3 w2 N- H) {* j w: h
- (session "step" 0)
) O, P$ B( ]0 [" [9 F' w - (erase-buffer)
( B* Q8 C/ e* I7 ? - (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
* V; ]: t$ F" O- ? - (dotimes (row size)5 G! [# y# m6 N9 h' F
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
! ], n3 _5 R* S5 b - " a2 z* J* w& w
- (defun board-contains-p (y x)7 L: M) ~% j q8 c/ X! _
- (let ((size (session "size")))3 p( n/ Y4 f6 p: d3 {& T
- (and (<= 1 y) (<= y size)0 h3 b& H5 K. i7 b+ |
- (<= 1 x) (<= x size))))3 b0 G* o" i3 A! O8 F8 T
2 R8 K2 C: U8 \% K; U6 }- (defun board-toggle (y x)
* q# ?3 q" ?% R0 d' s - (when (board-contains-p y x)0 A# ]. t/ Z l
- (goto-line (1+ y))9 i& X6 T4 p4 a/ A
- (beginning-of-line)
: [" L5 _& q* W6 v! i - (forward-char x)
1 u- n7 y: ]- L+ s, t5 D - (insert (if (= *wechat-5x5-white-chess* (following-char))
; h3 {6 _! ~/ J - *wechat-5x5-black-chess*# ?3 z# J2 f5 @
- *wechat-5x5-white-chess*))' {9 p+ P r Z" \5 |1 x# B/ P
- (delete-char 1)))
2 Q! d7 G/ Y( d7 d7 r% G! } - / S, Z7 B8 m r$ W
- (defun board-put (y x)+ K2 l+ }2 J! V, i8 h
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
$ K& Z0 F: L+ F) T - (board-toggle (+ y (first dir))) Z2 O. H* H) S; p) V
- (+ x (second dir)))))6 ?6 f8 }+ a3 ~- X, H
# v3 q) C' P. b5 E6 Z6 r: k0 c- (defun game-over-p ()$ m: E2 s; n4 T; G# F
- (beginning-of-buffer)4 l1 r3 `" y6 |3 k; g+ u& e! x/ Q4 _- I
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
2 b- `6 s. B* }" I4 l - 9 E6 E1 Y8 P# B z
- (defun board-show ()/ v9 O% s. B- g+ r" w6 s# F
- (with-board! T0 \/ r8 ~9 o i# ?5 w
- (concat (buffer-string)! \ M3 E( v' [# {1 l. Z, a
- (if (game-over-p)
, H9 ?! y L z! o: L4 F - (format "共%d步,输入任意内容返回大厅" (session "step"))
: q g( q+ S0 z+ w" o - (format "第%d步" (1+ (session "step")))))))
0 [0 B' U0 x, b+ {! m - 8 ?# q6 h3 r1 o, E' t$ q+ o
- (defun board-position-parse (cmd)7 |( Z* n+ I3 R5 l# L
- (if (= (length cmd) 2)+ N; C, n9 M- m6 J; T) e ^
- (list (string-to-int (substring cmd 0 1))& `! j) D1 K& J
- (string-to-int (substring cmd 1 2)))2 t4 c+ | E. y* s
- '(0 0)))( o& `0 j Y) i! H4 _4 E& |- w4 d/ f
- 2 V) p7 m/ O, |0 c" t. n) Z9 P! i
- ;;; 游戏房间! d. q- o; d8 L5 G3 g
- (defun game-room-init (cmd)( o6 P# m! t) ]
- (let* ((middle (string-to-int cmd))# p# E, k$ S. J$ B
- (size (1- (* 2 middle))))
( s5 C1 W; Y/ K6 L, Q3 l x - (with-board
8 b0 S0 }5 v+ ^5 o: G - (board-init size)
2 x/ g4 P$ M- u - (board-put middle middle)))2 X$ D" J- Y: ^7 e/ |9 b
- 'game-room)
* p" z/ R/ X/ s( G+ C- t; y
) T; W( k; x5 R: P# m- (def-room game-room3 o) S) y, S- c! Z' t# s a( Q
- #'board-show3 E3 z1 p G: w. Y7 k1 I: S7 ^
- (t (lambda (cmd)1 x: t: O) q$ i0 c
- (with-board
) u! c8 X# n1 d- X - (if (game-over-p)( Z/ h, h: T. {$ g$ O5 Y* o9 k
- 'living-room
+ {& I3 r8 @; N6 [9 Y& y/ u0 B$ y# A1 G - (destructuring-bind (y x) (board-position-parse cmd)2 o y+ U. N& x4 y. j
- (when (board-contains-p y x)
: i/ M- Y; E1 e; A7 R7 J5 B - (board-toggle y x)/ u. U) |( e7 a( j
- (session "step" (1+ (session "step"))))
* ?. A3 p* m" {- a7 ] - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|