wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。7 w9 \7 v! s: w$ p! G/ N
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
2 `4 l0 Z# s1 y4 v Q" | _- <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;">;; 定义新的游戏地图
7 L- u) I" l6 d( o9 V/ K - (def-map "/game/5x5.el" ; 对外开放的URL. H4 R, a# D+ t4 v7 m( K' w5 m
- 'tutorial-room-0) ; 默认的入口
+ O' c* X4 d2 ~) ]- H - ! R% v, Z* @2 Y- q; o
- ;;; 游戏大厅
) X' \, z/ g5 v/ i4 M - (def-room living-room
! o' ?4 `2 _* {! V* p' {! I& f$ c3 y - ;; 进入该房间后的提示语6 v2 p5 Z/ P% Y" L$ ~& F" ?: R$ Y# H) C
- "1. 教程
8 n0 X& g9 C5 ~ - 2. 入门(3x3)
" w. a% J% E1 u$ Q, v - 3. 初级(5x5)0 M v" Y- Z/ C+ Y1 d# Y2 y" V
- 4. 中级(7x7)8 W7 ?* H' R8 I
- 5. 高级(9x9)# \7 X- F! r, ] }7 F
- 0. 关于作者
) T& l1 h: [/ L3 }. z* s+ q) S - 请选择1-5开始新游戏:"1 M$ B0 @! N7 c. h j
- ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名( y% J% U( r& n) c5 n( s
- ("1" tutorial-room-0)+ H, n0 Y: x) h5 ?" D% f7 P5 V
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配
# C% U0 ^6 {0 |( \3 f - ; 相应的返回也可以为函数,动态返回房间名
L- h7 T4 k3 V8 \' q7 a - (t living-room)) ; 如果条件为t,为永真* D3 S; O4 g# c
! b; p2 P$ x& Z1 b" D- ;;; 作者信息
6 N( q6 z0 q: n; ~% } - (def-room about-room: k# u2 `7 q; a
- "作者:redraiment3 i1 ^2 p9 a9 v& H' V2 O/ \& Z; c
- 微博:http://weibo.com/redraiment4 O1 H' ?* B% M. A ~! R
- 有任何建议,欢迎在微博或微信上联系redraiment。
( E6 m' k, [( v( D' g% l - 请输入任意数字返回游戏大厅。"% |0 p4 b0 Y8 \9 |; J; A" W
- (t living-room))4 E1 R: F8 w4 K2 V+ c8 l0 M( {
9 E* S2 s! s4 l4 W0 u$ u- ;;; 教程1 V! c0 ^/ J! J8 s' \7 d
- (defvar *wechat-5x5-tutorial-rooms* 0
& X+ ^4 Q7 W0 V; z5 i& B* h, Q - "The number of tutorial rooms")0 c1 m- |# P8 h9 ~9 X ~
- ) G' @9 z/ L- E4 A" l# ]
- ;;; 简化教程的定义
7 ^7 Q5 } _) O: y: I - (defun string-last-line (content)
1 q& V: X u3 M" {# O - "多行内容组成的字符串中的最后一行") b; j+ Q# l: p8 ~" p, x3 G) `5 u
- (with-temp-buffer
$ {- q+ s$ T0 D6 Q& Z% X& `$ b - (insert content)
[7 |3 C T+ m5 r - (buffer-substring (line-beginning-position)+ H) w! x& S) h0 @5 p
- (point-max))))5 | s; U5 G- i* o
6 c- `; f* Y+ D B* m7 _- (defun def-tutorial-room (prompt)! V9 q1 ?% R$ E5 H+ d2 x
- "根据提示语自动生成教程房间。6 v. i# @# Q, W9 y
' k" m5 ]8 V' j) C- 1. 提取最后一行作为问题;
- r* Z2 m" U+ ^& h - 2. 分析问题,获取期望用户输入的内容;6 R- v+ Z0 s, u6 {9 I4 Q
- 3. 定义教程房间和重复提问房间。"
: J: |. W5 t$ C) z- W, b( D( o - (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
( t; {/ @2 U4 u: ^6 g5 U8 O - (repeat-room (concat room-name "-repeat"))
0 \1 P% R) ~0 z4 t n; w0 `) { - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
" q% a2 p3 m8 z; P8 c - (question (string-last-line prompt))
! s* J' i3 k1 B - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
# z# @: k d1 d& |* S - (match-string 1 question)))$ {+ ?6 c1 @5 [% [( q
- (doors (if except
0 H" q I8 N% P) b - `((,except ,(intern next-room))6 g. e5 Q0 I! ?2 r
- ("q" living-room); A; S2 ]3 v9 t5 h! ]2 O
- ("Q" living-room) u' u4 h8 R# a) }) r' i8 n. Q; U
- (t ,(intern repeat-room)))9 y( E1 J. G4 M% x, j9 J" J
- '((t living-room)))))
8 V$ a8 _ }) Q: | - (def-room-raw (intern room-name) prompt doors). a9 O ^1 h; h* p
- (def-room-raw (intern repeat-room) question doors)))
2 s) c$ c3 I. T! A/ Y, A - * h6 C6 a9 L! {, c0 K+ Y8 k
- (defun def-tutorial (&rest prompts)
8 Y9 J# l' e' M - "批量生成教程房间。"
, R. [9 y% d$ ~8 N) s/ g( D - (dolist (prompt prompts)
! {* t" f6 M+ r4 I* H - (def-tutorial-room prompt)))& U" n) R3 v- x7 D9 n; h% T
- ^' _% m. b% A* y# C7 S- (def-tutorial/ [% |& {% w" Z- Y" I3 B& \( |
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
7 Y: m9 Q) ]- [8 e3 [; Y4 O8 y% J - 1. 教程
" o- c7 [+ c% `; K5 @ - 2. 入门(3x3)
, H* L$ l% M( O$ Q - 3. 初级(5x5); E; U; r6 ^' V
- 4. 中级(7x7)
0 T: e- z! c O( ^ - 5. 高级(9x9)
( z- M% T j- E- Z! | - 0. 关于作者9 x) E7 m( ]) I6 ]+ u
- 请选择1-5开始新游戏:: Q% Q' Z4 Q' P$ ~4 X/ y" K( p
- 您现在正在游戏大厅里。
; a1 @/ a( ^, ^' x: r# I* M - 请输入“2”进入入门级房间"
* F8 Z; Q! B: {# D - " ①②③* B% J3 O" |1 D, c+ u9 Q
- 1 ■
/ e* [; D# v% s7 T8 `2 I% L( { - 2■■■; L! Y- k& D' d: |2 b3 L
- 3 ■
" N. j$ u, N) ^7 p( n - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
/ a/ v* l o4 A( [+ U! G) Z - 请输入“22”来关闭第2行第2列的窗户。"
5 l+ \& ^" P" i4 G - " ①②③
' _# x/ a& A! |( G- ?" y6 S - 1
9 O0 X4 p: b5 L# r8 q! N - 2 $ x/ I _+ D, h' c8 {. l
- 3
4 `7 j/ `3 R' ^/ j- v) r! Z% D - 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。+ s# q2 b) a. l" P4 J
- 请输入“11”,试着开启左上角的窗户。") I, v0 r+ Q9 U& B5 \) B% l
- " ①②③
2 D4 U- d9 l( _" h - 1■■
& f9 k/ k9 j4 T - 2■ 8 `& T8 [) m& o5 Y2 |
- 3 . b e. g4 R+ b& M2 y% Q* B* M
- 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。3 K q7 G9 A0 b$ x/ N j
- 请输入“13”开启右上角的窗户。"
2 j( b1 W4 R/ I1 j: F! N - " ①②③
. f7 _( n2 H+ r$ q6 X7 N4 J9 I - 1■ ■3 u, Y/ C6 a0 C* ^- ?
- 2■ ■' V5 ?" o* C7 m, G! K7 R7 p; R
- 3 9 T K7 ]: U8 G
- 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。3 A; a" I$ v, \) ?
- 请输入“31”开启左下角的窗户。"5 a6 ?- q2 {+ o2 p/ Q! A
- " ①②③3 V. [* _+ Y" v4 h, t7 u
- 1■ ■* {& Y& O9 ]" a/ w& s- h
- 2 ■
# h/ A t- u0 ` t( [3 M, E - 3■■
5 B4 ?1 C; m+ g" v - 此时,总共有5扇窗户被开启了。1 O! F# p1 z* F2 y
- 请输入“33”开启右下角的窗户。"- e: X5 } t v
- " ①②③1 M% o5 D$ u! ~5 E
- 1■ ■, i& S8 X8 N, c' C6 e+ F; v
- 2 ( |& z! i& @3 `5 t1 {1 R; f8 v+ f
- 3■ ■0 f- x) p: e% i* x x
- 现在,只有四个角落的窗户被打开。
) L7 s6 H9 v: S( P* H - 请输入“22”完成最后一击!"
- c7 k% {% |! g- q/ B - " ①②③
" g' b0 V4 k0 T - 1■■■8 E$ ]5 V8 M' S8 h# t* Q
- 2■■■
m3 F/ V, g! Y& l8 N2 } - 3■■■
5 O2 @% v3 u+ C6 S6 Z - 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
8 N4 d* M* l7 x6 ` - & p6 B$ w) m6 U- E& R! N
- ;;; 棋盘( @; N- j1 l l
- (defconst *wechat-5x5-white-chess* 12288
M7 V# E2 S, B - " ")5 \& b& ~; }. v y( Q% b4 ]! v
- ( c2 Y4 U5 i5 b. e2 g" E" R `
- (defconst *wechat-5x5-black-chess* 9632
* Q* H" N* ]8 s - "■")
0 e; @7 n( o" c4 W: j/ q5 n
- z) s. b8 z7 h# C' d" o. F- (defmacro with-board (&rest body)4 I+ i' C+ X2 h* R/ {% z/ I9 }9 e; r
- `(with-temp-buffer9 a' V4 |4 p9 S5 I" j
- (unwind-protect0 p1 V6 l& ^' `9 q. n" s6 C
- (progn; b% b p: w' B" k& W2 s5 q7 g
- (if (session "board")6 U7 [ p, k- k: N [2 y
- (insert (session "board")))# Z3 ~& g4 V+ a4 u9 c3 n
- ,@body)
( b$ h/ b! M6 R% c; e - (session "board" (buffer-string)))))
7 _- e1 f# |# s1 ?3 U, u - ) _; x/ N! N+ J7 s- G: E" F
- (defun board-init (size)
/ L" N O i: x3 V/ Q& `* t! d - (session "size" size)" U" I1 _: D# T3 r& j6 R
- (session "step" 0). `8 K* |: d& L0 J! ^$ J& V& {
- (erase-buffer)% R8 Z: Y0 t3 ~6 D
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))+ l: L1 \# c2 H9 b4 A
- (dotimes (row size)9 a5 z) d' @5 b) Z: E
- (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))# r, g0 Z( I3 x% i0 c2 z1 b
. x' h/ a6 y' A2 c- (defun board-contains-p (y x)
" i+ w' @9 l, f+ W" F/ Y+ H e" { - (let ((size (session "size")))2 w: K; ^" L% g- U* o
- (and (<= 1 y) (<= y size). ^" v3 Z2 G/ P4 ^
- (<= 1 x) (<= x size))))
( m. }0 g3 }2 }1 E/ |
9 j/ `0 O3 x8 w- (defun board-toggle (y x)
; C$ o5 E$ |% C! _3 A q - (when (board-contains-p y x) R& U1 I' x) }5 y
- (goto-line (1+ y))
" i5 \0 P& w% s* f& o! A. n - (beginning-of-line)
' ]1 w- w1 y% l& R' H6 s - (forward-char x)* m2 w1 j. \; b# Y$ C% p; _
- (insert (if (= *wechat-5x5-white-chess* (following-char))! K( I8 b) G/ w! i
- *wechat-5x5-black-chess*6 t W, D' z/ H
- *wechat-5x5-white-chess*))
- u( Q$ f8 k. t9 q - (delete-char 1)))
6 [1 _: _# ~2 B3 u
u6 ?* h/ A% R; f+ k* k+ i# t- (defun board-put (y x)+ K4 c) _5 q) `- u! z5 }, }' N
- (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
1 b) x3 V. J+ m - (board-toggle (+ y (first dir)), b2 B7 c, J3 k" u z/ `
- (+ x (second dir)))))
# Q" ^9 n/ p) v9 A* a4 A
" d, K5 w* k" Z: ?) H$ \0 i2 k- (defun game-over-p (): _/ F4 }7 h8 V* Q+ `7 X% c
- (beginning-of-buffer)
# B; g; ~0 g4 B3 ] - (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
$ [& S1 I4 M6 ?) R' M8 y
% D) [, u8 S9 c- (defun board-show () g0 E7 C3 \' p/ U
- (with-board; [6 p5 |8 E8 J' q% h! Z
- (concat (buffer-string)
. ]4 T7 \, O9 R: X) O% O$ _4 g - (if (game-over-p)
% e) }4 G- H7 E4 x* T - (format "共%d步,输入任意内容返回大厅" (session "step"))
. p; |$ n$ i7 H - (format "第%d步" (1+ (session "step")))))))
# c P$ h, p2 [
3 }" W7 n! R1 ]2 j3 |" r5 K9 O2 B- (defun board-position-parse (cmd)
+ h \0 t! C8 g% A/ D9 h6 X! i# N - (if (= (length cmd) 2)( `6 H1 K3 H/ W
- (list (string-to-int (substring cmd 0 1))
( n% _ O8 _. A: S - (string-to-int (substring cmd 1 2)))) B( _! y4 s. w* N1 q$ m
- '(0 0)))
2 Z, |/ I' w: w+ H0 p% O3 F
, n, N/ O% J o0 w1 p- m- ;;; 游戏房间
6 v! u1 @/ @0 Y; c/ w: z0 h' a! |/ | - (defun game-room-init (cmd)0 z) p' x |9 I1 C- X8 h, w
- (let* ((middle (string-to-int cmd))8 O/ R( S' d% D+ q+ D3 r# V
- (size (1- (* 2 middle))))" x& w9 e: @; i$ i! E+ Z
- (with-board
2 H ^' L9 C7 E( G - (board-init size)
( u( E+ v( ?. \/ M/ ~ - (board-put middle middle)))
) d+ f8 K. m8 k2 N: _6 D - 'game-room)
3 e& C; U) w! U4 j - . z$ K w" t) u4 q( }; P
- (def-room game-room$ h& S% Y: P" N$ o, F
- #'board-show
6 e& [, F2 [. ` - (t (lambda (cmd), g7 J6 f7 Z+ Z$ |4 m2 N
- (with-board
# Q! h% E$ q8 B - (if (game-over-p). M2 \0 X! }; v: n
- 'living-room1 M6 _; N" s& Y' k; Q% w" Q
- (destructuring-bind (y x) (board-position-parse cmd)
, I% u! ?$ d L; N - (when (board-contains-p y x)* p: i* u! J' z
- (board-toggle y x)0 Z) t% G3 X. t
- (session "step" (1+ (session "step"))))
( ~5 K- c" H7 @: [+ X: O& r$ T - 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|