wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。! T5 a7 l. K: A: j" ]4 ]! r
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。- ) p$ m- O1 ?- f9 `2 g& P
- <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;">;; 定义新的游戏地图
@/ O7 ^1 }; K- A+ o+ ?; e# S" p - (def-map "/game/5x5.el" ; 对外开放的URL7 n0 m* J+ o( b! y/ K6 {
- 'tutorial-room-0) ; 默认的入口5 t6 f# T. p/ P; v, t) g
- , l* Z) [' J- `( l n
- ;;; 游戏大厅
, |* t" y1 Y3 e- Y& i2 k - (def-room living-room
4 k( i1 C. e1 m) T5 n% Y7 w ? - ;; 进入该房间后的提示语
' k( h# _- Y, L4 Q5 v$ D - "1. 教程
3 {/ i; G2 \- @- t9 N - 2. 入门(3x3). E* C5 C( L" f7 c. k/ b2 L/ D" F
- 3. 初级(5x5). H+ W9 u6 i$ g6 P! V5 Y7 M/ q) T
- 4. 中级(7x7)0 H9 y# ~+ ?9 J1 M
- 5. 高级(9x9)5 h" w' q' I: H2 z
- 0. 关于作者
D6 V9 n: c- j - 请选择1-5开始新游戏:"; B" A8 b+ F* I% S* ]
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
) k% ~3 d% k! _7 c6 _- x - ("1" tutorial-room-0)
8 ^: ?5 L$ p" A# U" X4 R - ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配' Y" l+ y- f/ M: w0 W8 s2 p
- ; 相应的返回也可以为函数,动态返回房间名
# @4 Q' W/ }: G1 ]' F U - (t living-room)) ; 如果条件为t,为永真
' J$ V2 ^6 S/ O8 @+ B - " x; Q+ u Q5 i7 L; s1 b
- ;;; 作者信息
j4 e# i/ m0 R3 g, Q - (def-room about-room
3 ~9 Z) |0 ~" p" C - "作者:redraiment, j3 f' R+ M1 M8 L
- 微博:http://weibo.com/redraiment) {; G& q; h& h
- 有任何建议,欢迎在微博或微信上联系redraiment。: ?3 H% W& V' y2 Y5 v7 T/ `
- 请输入任意数字返回游戏大厅。"
$ D5 g. a! l5 w" Q2 l - (t living-room))0 S; m+ ?8 z* Z. A
6 X7 r8 l( K: E4 n1 n" ]- ;;; 教程! Y4 i# A$ t4 ]6 b0 w
- (defvar *wechat-5x5-tutorial-rooms* 0
+ |7 g( f/ \' H' k h$ @4 x9 ~ - "The number of tutorial rooms")
$ o! I9 u0 k* h6 P
5 S+ j1 k1 Q5 a& l0 C5 }" Q7 i- ;;; 简化教程的定义
( t0 }% y8 J: G" B x# x - (defun string-last-line (content)
8 z7 C( ~# B7 T) P3 d7 Q - "多行内容组成的字符串中的最后一行"2 M* b: Q9 y! |3 }
- (with-temp-buffer
# g9 j3 u' s% m( U: V" v8 [ - (insert content)
x5 u$ Q, [% }4 @ |4 G - (buffer-substring (line-beginning-position)2 |2 _) {) {8 L
- (point-max))))
% D" x' v: [9 [
1 S* O/ |8 L7 R- (defun def-tutorial-room (prompt) q; w/ `4 J7 a9 E# [
- "根据提示语自动生成教程房间。9 F. }! n1 F% t, M& h x' {( O
- ( N, U; e Z! ]' J
- 1. 提取最后一行作为问题;
$ @1 g2 N$ M, U' r - 2. 分析问题,获取期望用户输入的内容;! U1 @( N' y @! c9 \" x* ]
- 3. 定义教程房间和重复提问房间。"
" k$ `$ E2 U: E% { - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))* k; a2 i1 E6 u2 e
- (repeat-room (concat room-name "-repeat")) m1 {( d) t O# O" h! J( C3 r6 F1 Q
- (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
$ e# X B( {* |- U( D+ U1 a: k - (question (string-last-line prompt))0 e" w: n: `& B4 i6 B" q
- (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)- p, f9 `5 W4 e a4 [/ _" B6 z$ M
- (match-string 1 question)))
6 J; E6 m2 {& {& Y& P' |% P - (doors (if except5 ?: r/ M1 I5 ?7 d8 T. d5 y* q
- `((,except ,(intern next-room))& j# D/ y7 v. z" B! g; |9 O) Y( U5 [
- ("q" living-room)/ @3 O' c9 Z8 O- N* j
- ("Q" living-room)
# `" p/ Q* `4 f S4 d& [ - (t ,(intern repeat-room)))0 I1 k! |- ?) q3 {- f: z
- '((t living-room)))))( A& f, b4 e6 l0 _- @9 Q. I
- (def-room-raw (intern room-name) prompt doors)
- |& v! m8 k: H9 T! d2 `0 k f - (def-room-raw (intern repeat-room) question doors)))+ X' M9 l6 i# Z) f4 Z L! U
9 Q C* R; B: ^' T L3 G# l' j- (defun def-tutorial (&rest prompts)+ E2 o( ~0 |# ~/ v* w
- "批量生成教程房间。"
: B- ~7 f8 m1 Q) B9 V: C - (dolist (prompt prompts)# _3 g/ i* A! l3 W+ p7 l4 q6 \
- (def-tutorial-room prompt)))2 w5 `% `' N# Y/ j' T
- " ]- H7 C$ D3 b( P+ f; W0 `" }
- (def-tutorial" C- C; @7 ~; N2 N8 W
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
* {5 V1 z5 Y. B( I9 n2 N& S+ a - 1. 教程3 k* h9 T) Z5 K" k8 T8 A
- 2. 入门(3x3)3 K/ R4 M+ f" @, W! }
- 3. 初级(5x5)
3 F+ M; f I1 K1 n# K& i [ - 4. 中级(7x7)" B/ r4 j- u9 @6 e6 p0 C! l# F
- 5. 高级(9x9)$ y i* l; @6 Y/ Z7 b* M
- 0. 关于作者7 x/ ~6 {: r3 P! A2 V T
- 请选择1-5开始新游戏:8 E# Y6 J. z! m P% x
- 您现在正在游戏大厅里。3 x0 [( J9 u4 u2 c0 N
- 请输入“2”进入入门级房间"8 b+ H+ l& z4 X0 D/ q$ T* [
- " ①②③
7 d6 D. }0 |! G8 y8 ^+ Z6 a - 1 ■ 9 H( D$ x \9 ~. A; H4 c3 S) ^
- 2■■■' { \6 Y% G/ J! W
- 3 ■
4 z; `; P9 q7 [( ~ - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
$ U3 f& Q3 C$ d" s - 请输入“22”来关闭第2行第2列的窗户。"
! D! I/ U* I3 ^2 L - " ①②③
3 N' D) ?, a- _' l- ^6 B - 1
" j. T# |5 _4 U9 s2 l - 2 0 E' @" [3 Y8 D; P+ f9 b6 B
- 3
1 X* N& T s4 B+ O; u - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。2 w }! W8 b% }, I
- 请输入“11”,试着开启左上角的窗户。"
7 r& W. X: H6 a/ ?: K, q - " ①②③
+ i+ C. t5 O4 [* \ - 1■■
8 W* g) }4 W& Y - 2■
1 C H* r* ^. } o+ A3 a1 r - 3
# @/ \/ Q8 Z. ?8 m) _9 z - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。- E$ X' Y1 c4 N7 I# c
- 请输入“13”开启右上角的窗户。"7 b* N2 u: I# x8 j1 @, k2 O1 c# ~
- " ①②③9 G7 M+ i' s. b; s- I
- 1■ ■
7 ?5 m8 O7 u3 ^: {+ \ x - 2■ ■
2 o6 A/ x, y* U* } - 3
2 A0 h3 P+ d! f' F - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
; ^# G, N" k: L5 Z$ R4 s5 I - 请输入“31”开启左下角的窗户。"
1 o$ E! b# V! O+ R# L' t - " ①②③
' |7 s, O5 z' ^% d) W1 C - 1■ ■- R5 [/ i* q9 I- M$ }( v" j
- 2 ■" F- R r6 k" {/ z4 o. n' B
- 3■■ # |3 s$ D* Z3 \( e( N
- 此时,总共有5扇窗户被开启了。
6 H+ O: ]' J( u! V" D - 请输入“33”开启右下角的窗户。"
" Q$ x, d% D: m9 b# G - " ①②③
2 D* b) A; V- C5 L# l - 1■ ■
3 q D9 D* r' s- N - 2 & k0 _2 a3 d# Z2 L# p d4 ^$ f
- 3■ ■
. V0 p5 k$ J U, d7 B/ F2 n" c - 现在,只有四个角落的窗户被打开。/ e f \& p) t* M) k6 G& d: C
- 请输入“22”完成最后一击!"
/ ^: _5 C( }" U/ [# W - " ①②③) N5 c7 f5 K5 S4 G6 C H
- 1■■■0 q, k- h: q0 u- \ ]9 [) W
- 2■■■5 q- Y+ U# D8 I
- 3■■■
! R7 m: E% }) M1 s% k0 g' S - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
8 G: \: C6 Y+ E$ i5 d - 3 _! h1 q4 E8 o D) W/ H9 e
- ;;; 棋盘
& {/ h" m7 t% s- e - (defconst *wechat-5x5-white-chess* 12288
! [7 {1 p& h; u. E) s. R9 a - " ")+ J7 o6 ^1 X5 L& Z
8 t2 {( j' H4 f. i! F- (defconst *wechat-5x5-black-chess* 9632 O0 [% E5 v$ z
- "■")
8 K' P! P% z4 C0 E3 \. _& }6 ~. ~ - $ } |4 B( g; G; O% Y& p
- (defmacro with-board (&rest body)3 {' q, Z: z5 |8 V. o9 h& L8 g3 C/ j
- `(with-temp-buffer
2 s5 Q, W3 V% x7 h+ C' s9 ]. n - (unwind-protect% {, A/ {5 c/ B, O& ?7 N8 v* r8 |
- (progn
2 H/ u) |4 ~4 u - (if (session "board")& O9 z' e' A( ~
- (insert (session "board")))' t- g5 n5 m! W4 z
- ,@body) ~# v8 n8 x4 ^! g7 Z+ W
- (session "board" (buffer-string)))))
* N% F D# `8 U4 E/ b
$ `3 G6 x9 \) @0 o- (defun board-init (size)
9 E) o4 c: {7 w d: }7 U - (session "size" size)
5 O% E2 \7 T4 P: X - (session "step" 0)9 |1 u0 {4 ]7 ]6 _. R
- (erase-buffer); D, i( S! t7 v9 n; |1 Q
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
; X/ ~) ]( D/ r$ i - (dotimes (row size)" I) c, E \7 I F9 @( c
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))/ u& O0 ~/ X8 U5 w" {% c
o- b: z- }- S0 p5 o. C- t% |- (defun board-contains-p (y x)2 |7 a( ^: C' @, K: @9 s( \% F% B
- (let ((size (session "size")))
# `/ e* o8 w6 c& T0 l5 N6 L - (and (<= 1 y) (<= y size)8 Y1 B* `0 v% E: s
- (<= 1 x) (<= x size))))
. U l1 M, i& W- t - ' J# n! P: q! N3 l
- (defun board-toggle (y x), B1 w( T0 T0 b: A
- (when (board-contains-p y x) Q9 k2 e( f" k* m. |6 a8 Z
- (goto-line (1+ y)). C* v% p- ^8 R4 n N
- (beginning-of-line)" p! [/ ?2 x- i b8 Z9 h
- (forward-char x)
' b; C) ^* v6 I. Y: { - (insert (if (= *wechat-5x5-white-chess* (following-char))" `+ M. q% J) [" X* B
- *wechat-5x5-black-chess*& ^ R! J' m4 o' w, y
- *wechat-5x5-white-chess*))
, |( P1 J9 ]+ k! {3 ~7 D/ { - (delete-char 1)))( S5 Q( f( `! W9 V$ {/ K% V/ P2 Q
- , j8 X7 S' ?! k
- (defun board-put (y x)
1 j c8 ?* H1 Z2 B: T! G$ x - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
8 P9 k. U1 X! R- g - (board-toggle (+ y (first dir))0 z* ]$ a H- o* H/ g
- (+ x (second dir)))))
" }* H" [! `! H0 _$ k0 B
: q9 B' t" n, c9 H6 m2 k' C$ j3 |) s! E, }- (defun game-over-p ()7 ]( U5 N/ z. U. y& o; E
- (beginning-of-buffer)0 a9 O9 T2 T% y0 z, z
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))" ^* \8 o8 C1 R
# A( ^+ g7 p* {- (defun board-show (); [3 _4 r" P" _8 R
- (with-board# B( i0 q# J% g& W, B; u* v
- (concat (buffer-string)
* I7 l, b% a8 O. w8 ? - (if (game-over-p): A" A& E8 B/ H, w) f0 K
- (format "共%d步,输入任意内容返回大厅" (session "step"))+ P* B; k) g7 {- H9 m
- (format "第%d步" (1+ (session "step"))))))), c3 v6 R5 x; [5 D7 j1 X, ?# k4 V
: Q0 T% N5 B' E6 x! B- (defun board-position-parse (cmd)
4 b" i, o' G3 x) d- H$ \! u! k& i - (if (= (length cmd) 2) q# e5 N9 a4 d; l Y
- (list (string-to-int (substring cmd 0 1))
. [1 H4 a- C. K: y0 a+ W - (string-to-int (substring cmd 1 2)))
$ W8 W$ W% E. k; n" i r8 I - '(0 0)))( Y/ u) E: c& V
& l4 e3 d8 g, N1 t0 F! R: D/ J- ;;; 游戏房间
) w1 B) b( W2 d) T) x q - (defun game-room-init (cmd)
3 j3 t! i! Y3 x0 L& G/ R - (let* ((middle (string-to-int cmd))
1 C3 R+ Z) q) f r) P* v) E% Q - (size (1- (* 2 middle))))0 [, ~5 [5 t0 S/ \1 k+ K" r
- (with-board7 K+ i6 `/ ]/ @3 @9 T0 Z% A" B
- (board-init size)( a6 @( T* s+ q
- (board-put middle middle)))
/ `8 O8 \; q% R; n+ ~ - 'game-room)3 T/ C1 m( _/ i- n- Y# a
' a Y0 K$ H/ L& c. r& F3 w" t- (def-room game-room8 E% A/ R. k K
- #'board-show
4 m3 v" a: U& | - (t (lambda (cmd)+ @, j& W l, t6 f/ U% p. U1 v
- (with-board4 y- U( Y( I9 {, H& Y! T) X
- (if (game-over-p)
# G- {# \- F" U - 'living-room; j: I- t, {5 H! i5 O
- (destructuring-bind (y x) (board-position-parse cmd)' K; _$ Q7 @& } R6 @4 j. u0 h
- (when (board-contains-p y x)
7 T% L8 M$ F2 Y! y0 S g - (board-toggle y x): @* r9 k7 p! s, U3 C2 M
- (session "step" (1+ (session "step"))))1 {& J2 W/ N, i2 T9 u
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|