wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。0 C! `& }8 n' [" K* @, P
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
: H9 D" t# v* k3 h8 W8 g- <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;">;; 定义新的游戏地图
: D9 V; y: I4 m( {" [. e& c5 R0 w. R - (def-map "/game/5x5.el" ; 对外开放的URL
{- `- g( n* g8 T) q6 F/ u - 'tutorial-room-0) ; 默认的入口- I" I8 z" w1 M# Z9 I2 T% D6 i8 T c
- 3 x- r/ e. F. I( g! A# s
- ;;; 游戏大厅
; B, H2 E( M+ I$ \, D2 {" {2 b# G - (def-room living-room* B- S6 L M, u9 \
- ;; 进入该房间后的提示语, w6 A$ e- T) [
- "1. 教程
$ k/ D2 }3 y# E5 j2 g7 _ - 2. 入门(3x3)
* @; n; G1 _$ R& T7 H% L9 m - 3. 初级(5x5)
/ H/ P6 R, l" c% l* d* T - 4. 中级(7x7)1 J+ J9 [2 \' n4 L1 ]
- 5. 高级(9x9)
" g- M& d) `+ _3 ~& `3 d9 K - 0. 关于作者% l& Z1 ~. J7 }2 n$ N; U8 E3 I
- 请选择1-5开始新游戏:"
+ d( M: P8 A1 y }8 b, g0 `) N - ("0" about-room) ; 期望用户输入的信息以及相应的后续房间名
7 ` [. t1 S$ C3 g+ l - ("1" tutorial-room-0) _5 U( S- j2 Z3 y1 J8 y0 l! v
- ("[2-5]" game-room-init) ; 用户输入信息可用正则表达式匹配; y5 k2 z# O; s$ P8 k+ H
- ; 相应的返回也可以为函数,动态返回房间名, _1 @ C* @7 C5 G
- (t living-room)) ; 如果条件为t,为永真 _. X! V/ J$ {! r
- D" B3 L4 ^) m# f5 [7 t- ;;; 作者信息
& Z# t1 x8 a" j6 ?4 G3 q5 c - (def-room about-room
# T }8 ]9 ?: S1 c) E; G - "作者:redraiment; E2 Q- S' p$ ~' I# ?% e
- 微博:http://weibo.com/redraiment0 }, a. { ?& h! P k0 Q9 u% y
- 有任何建议,欢迎在微博或微信上联系redraiment。! v* h! \! x9 o8 M" {
- 请输入任意数字返回游戏大厅。"6 r* e% D, T" {" A/ \
- (t living-room))
/ B& [' v9 E6 E4 ~* t
3 A& W- Z( O; N' j8 T4 p" v- ;;; 教程
! T; z5 b r& W1 a4 k% R1 c# G - (defvar *wechat-5x5-tutorial-rooms* 0& R6 h$ q7 i) k, [' t
- "The number of tutorial rooms")! @) J, h" s8 N$ K& C" L9 t# p
- ' ]8 O: w" s4 j0 _# f! x
- ;;; 简化教程的定义
) j! a. h% P ?4 o - (defun string-last-line (content). ` M$ |* |- ^- n# F
- "多行内容组成的字符串中的最后一行"1 \* b, q8 ~, s N" j) U" J! p, j
- (with-temp-buffer
- D0 {! J* ~" c# v7 e! Y - (insert content)
/ B/ | E" |- t3 S - (buffer-substring (line-beginning-position)
9 n/ M( @( b% K- w' v0 V& K7 c - (point-max))))
8 A$ a' D5 y6 `9 A: q! Y - + `6 X& J% u2 o u/ _2 s3 T
- (defun def-tutorial-room (prompt)
" H$ b2 ~' W& u" @! m( v% r( ? - "根据提示语自动生成教程房间。) R* G5 i0 V4 y$ R( ?; E* Y6 A
# ]7 S i2 E0 }0 p( |3 C- 1. 提取最后一行作为问题;
2 C4 J7 A6 l; U/ c6 Q( V - 2. 分析问题,获取期望用户输入的内容;
6 S' {2 P8 T# p+ \3 M! k# B - 3. 定义教程房间和重复提问房间。"# x' H1 Z+ S; _
- (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))# M' V7 k2 h* M N- c p
- (repeat-room (concat room-name "-repeat"))
9 A6 h0 O% T8 x* r - (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
3 V. h( \2 A: f2 h2 ~1 S$ J - (question (string-last-line prompt))
& z. R; y1 r/ k' }1 U - (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)" {; D3 S# Q/ n4 E9 k
- (match-string 1 question)))
/ l7 C; c) F; w* a) Q) T - (doors (if except$ v& [" y# A$ \# p% k
- `((,except ,(intern next-room))
7 S0 m- ]0 Q& f3 y9 M - ("q" living-room)8 i$ a; P6 [# P1 G3 ~5 ^& D
- ("Q" living-room)+ j7 F3 h$ F. W! T
- (t ,(intern repeat-room)))
' D# h8 X7 _5 o2 r3 Z - '((t living-room)))))3 }1 W) o% y2 V3 C! r0 O, t
- (def-room-raw (intern room-name) prompt doors)! n" z( f1 [4 |9 E" z2 D
- (def-room-raw (intern repeat-room) question doors)))
6 N/ u0 K* B0 n3 Q) }$ L
% g% M9 [* G/ c% y6 D5 [$ N( O2 v- (defun def-tutorial (&rest prompts)% @2 }+ G) e ?* O% i' y1 M1 g
- "批量生成教程房间。"& f* ~3 w! m7 W4 s# j
- (dolist (prompt prompts)" R, D+ t# C: ^( U
- (def-tutorial-room prompt))) n7 m2 x: R0 ^. @: C) ^: \+ m' R: O
1 i1 R$ Y9 X7 W- (def-tutorial6 }- x0 k0 C# f
- "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
2 }! h9 d( `9 i1 Q, q6 \ - 1. 教程" p& l$ [7 @- H5 a* _9 r" F& Y
- 2. 入门(3x3)
& K8 ]9 {4 `1 o$ }# J" G - 3. 初级(5x5)
% l2 K/ h& L/ e0 e" i7 x( o - 4. 中级(7x7)7 k p; U" M: a, x
- 5. 高级(9x9). U" ~3 G B6 b! e; @; m
- 0. 关于作者
: D+ `; L* L$ i+ O% m/ b - 请选择1-5开始新游戏:
; J# y9 o) N* {5 ] - 您现在正在游戏大厅里。' |4 v8 ^% w: z3 _- S2 d
- 请输入“2”进入入门级房间"9 X0 E- O3 l( s
- " ①②③
& v5 W* M' W- e9 o" V! b/ l - 1 ■ s- m6 A4 V% G3 u, a
- 2■■■
& B9 C/ j, W7 Z2 r) ^9 f; h0 z - 3 ■
, _& V& v5 A& e* @ E - 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!4 |2 t: H" X2 C: @8 G4 ]
- 请输入“22”来关闭第2行第2列的窗户。"$ M9 ?- E( v, j* g# c
- " ①②③8 ?$ U% @$ u6 Y
- 1 ( _7 y8 b. t9 q8 j3 h
- 2 . _& {1 A0 y# d$ ~
- 3 $ g) T" \" d/ c+ Y
- 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
: `; X" v; G+ v9 s( L i0 D0 R6 M - 请输入“11”,试着开启左上角的窗户。"0 B# I9 W( j5 h4 u9 K
- " ①②③
+ i' w2 j$ d' l# W" H4 O" ?( u \ - 1■■ " N; r% u, V4 c7 _; V- b* H
- 2■
% X- X& ?! e6 i1 w! g) }& P - 3
2 Z1 \: C* y: Q- p7 G8 x" G - 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。 v" O n+ s! m7 \
- 请输入“13”开启右上角的窗户。"
, c5 L9 ~3 X5 g2 J) i" M$ T - " ①②③
4 k* v# B& a6 J# N - 1■ ■
& Y$ n% H3 p3 \; Q X - 2■ ■
0 E2 Q0 m+ e+ n& H# W - 3
( V& i. D# C0 M$ p( G - 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。1 R5 i* u. Z; w, |
- 请输入“31”开启左下角的窗户。"
& O, z5 [6 d' q - " ①②③: H J. C, D" Y" B) E( {' ^ j& |
- 1■ ■1 N/ R- Z1 c( r7 R2 b! ?
- 2 ■
7 W) M0 \" r: U+ w6 D( _1 R! ]& y( z& P - 3■■
3 ~0 ^3 X7 a! p+ E8 T+ h - 此时,总共有5扇窗户被开启了。* Y( u4 s* e P/ @; r
- 请输入“33”开启右下角的窗户。"
! o* t4 l+ D7 [- W - " ①②③
5 p+ e8 i6 z. E! A - 1■ ■7 l: }% c l! \
- 2
5 a* y2 }/ E* N. {; L - 3■ ■7 V' [6 b9 b3 j( H) v& v) P
- 现在,只有四个角落的窗户被打开。
3 e7 W6 K# L+ G* a! }' ?) J - 请输入“22”完成最后一击!"
, ]0 M: k/ L c% p! c% V% a6 R - " ①②③
6 N* E' U( Y& g. X9 [; k - 1■■■' I t+ d9 h5 j' }" E. w0 |# |
- 2■■■; z5 H2 ]- x# T) R2 E2 S
- 3■■■+ Z( q7 d' u. {, O7 E: G& P
- 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")7 u9 `; [8 G5 C0 d, {8 C
- 2 A4 x6 J2 l) |7 ^
- ;;; 棋盘; r% }% T& X4 R) D
- (defconst *wechat-5x5-white-chess* 12288
' Q% L8 ? P1 e - " ")8 z" B N% o4 F8 O
- 7 { V' l1 s* Q, S+ a c t- [
- (defconst *wechat-5x5-black-chess* 9632. z) j5 F. @5 V0 s9 f- N
- "■")
' Y' l* { j3 V4 z& ? - 7 Z U4 n) x( ]' a2 Y
- (defmacro with-board (&rest body)* X2 g V+ Q" N7 f, v
- `(with-temp-buffer. f+ k3 }% e$ S0 o. b- s: y+ j
- (unwind-protect# X5 B& h% q9 L" I6 P( K7 L. m
- (progn9 e5 t& J2 |& v+ u
- (if (session "board")
: ~9 q" W# ?3 H) \8 ?) a2 e - (insert (session "board"))) b, n4 d3 E" n9 Y
- ,@body)
; s9 s8 ]9 S4 `* J6 a1 _! U - (session "board" (buffer-string)))))
) k* _% J O+ z& ~ - 9 Y# [7 E5 L. `* D& \8 n, u c" w
- (defun board-init (size)
3 X& T/ S' y! Y6 T3 \9 ]. l a - (session "size" size)2 Y6 Q$ S* A5 a. ^0 s/ x
- (session "step" 0)
+ c. ^# |6 F/ Q' Z& n! a' D4 h - (erase-buffer)6 G$ w( ?3 S! u" i: Y
- (insert (format (format " %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
" y6 E) `: l) a% R - (dotimes (row size)
6 X3 A, k, r. C( ]% j - (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
3 m# K8 \$ j& k5 |: W& W |' w* V$ e - 0 } ^+ \2 [- F3 L
- (defun board-contains-p (y x)% q4 z4 U' r3 d% H. r$ U7 L
- (let ((size (session "size")))) D6 |+ D0 V6 h3 ^
- (and (<= 1 y) (<= y size). D" B7 V8 h% B" _7 K
- (<= 1 x) (<= x size))))
: H$ n! @' G% |2 f+ g
) U' C4 j6 \2 E2 ^, T8 w- (defun board-toggle (y x)
3 h4 Z) s$ p2 e- ^ - (when (board-contains-p y x)4 @. Z. ^# z& [3 X- y6 n" j$ v
- (goto-line (1+ y))
' c; o( K5 k9 N - (beginning-of-line) P# r6 u/ C3 [# K
- (forward-char x)5 n$ _/ {2 E8 Y T% k! r0 X
- (insert (if (= *wechat-5x5-white-chess* (following-char))$ ^6 k, U( B$ S! O- s
- *wechat-5x5-black-chess*4 ] j6 d6 s' l) H
- *wechat-5x5-white-chess*))
. B$ @5 r" d, P* p6 ]$ g - (delete-char 1)))
! p# w# R! c, a( B8 ^& q0 m# u
2 i4 g4 a! J4 G- (defun board-put (y x)
. p3 @) L& A: M. S - (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
Q" c) l. ?4 s* [; w - (board-toggle (+ y (first dir))
" s/ ^+ N: @7 e- l2 Z# f - (+ x (second dir)))))
- {4 _6 C' Y Z
9 Z* v( [6 s7 [# z- (defun game-over-p ()
" F# h" }0 s) Z - (beginning-of-buffer)3 f- k' `0 ~; y1 X' Y) W6 o
- (not (search-forward (string *wechat-5x5-white-chess*) nil t)))$ R+ D& P% y8 q1 N- l( t* P* N; \
- 4 Q$ G+ K2 o. P& c
- (defun board-show ()
. T1 q( X; T. h& o( B0 a1 @ - (with-board% A& t5 Q$ S# O2 ~/ ]) e
- (concat (buffer-string)
6 q0 S8 D; d2 v - (if (game-over-p)
3 J" L1 b. [6 s r7 A7 U - (format "共%d步,输入任意内容返回大厅" (session "step"))
; w8 x, Q( C2 k+ ^8 i" q - (format "第%d步" (1+ (session "step")))))))
4 l4 |! ~# S# o5 p% f - 7 a) B$ `- e6 r
- (defun board-position-parse (cmd); ]2 U5 l' a4 C% }/ {
- (if (= (length cmd) 2)0 q) D4 L1 e% N" d" |' s
- (list (string-to-int (substring cmd 0 1))
- X9 e# a7 a/ K$ H ]5 \1 \ - (string-to-int (substring cmd 1 2))); _- V( m$ n6 b. ` u6 x( {, f
- '(0 0)))" H) R D! U3 B& |2 `
- ! U! X$ E7 g" {& _$ m' v
- ;;; 游戏房间4 i% Q# Z6 A; |5 b
- (defun game-room-init (cmd)
" @# j8 }0 I, {, z) A+ @1 f - (let* ((middle (string-to-int cmd))
9 l! f) S1 F# ?/ T1 ? - (size (1- (* 2 middle))))
1 w) I1 q1 l8 i0 t/ d: k, O7 S! i$ X - (with-board
- g/ R$ N, k% u* G) j) E& A0 ?, g - (board-init size)! m* A& Q8 Y; e$ E
- (board-put middle middle)))+ B$ r" \8 S( R% }4 U
- 'game-room)1 a* F; z# n7 V4 D! [( A
- % Z! L$ g- J1 v* d4 X" p/ l
- (def-room game-room& }& Q; K# Q" m6 H P3 w6 Q t
- #'board-show. h& Z# Y" R" U* `) ~* e J
- (t (lambda (cmd)
4 b- P, I9 V5 u& p' N - (with-board0 k5 t% z& |+ Q1 P& v7 O) y
- (if (game-over-p)0 f9 f- x# t2 b, h
- 'living-room& T* v) ]8 s3 n1 l4 B0 l2 A7 ]' }: i- s6 |
- (destructuring-bind (y x) (board-position-parse cmd)* \- f& C0 P0 w! n& H
- (when (board-contains-p y x)# O/ p+ \ ^0 a/ J( O
- (board-toggle y x)9 m* N7 u; a5 R! B
- (session "step" (1+ (session "step"))))0 n0 Q7 P4 P) W1 d& S6 z+ K+ I8 k
- 'game-room))))))</pre>
复制代码 |
上一篇:微信扫描登录下一篇:微信红包
|