[微信第三方] Emacs逆袭:开发微信公众平台小游戏

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。4 n% P0 z8 a5 Z. u
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。

  1. . U$ d% R7 Y9 a
  2. <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;">;; 定义新的游戏地图9 G. _$ q/ Q7 |0 z, ^! B
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL0 `' V5 ?+ Y2 {( z0 M# s
  4.   'tutorial-room-0)                     ; 默认的入口
    1 Z% \* P9 K; b1 \; w) l- u
  5. & Q* r" F% f! J- J- W8 W3 _
  6. ;;; 游戏大厅
    2 L: k0 u3 r. n) B5 S1 F
  7. (def-room living-room% c* Y% ]- u+ }: n% o
  8.   ;; 进入该房间后的提示语, w% B5 e) [) h
  9.   "1. 教程
    , r- M* g$ |. G" U1 b' j) W
  10. 2. 入门(3x3)! C7 ~' K4 [5 E( G' V5 r
  11. 3. 初级(5x5)
    3 m$ O. w4 r; I
  12. 4. 中级(7x7)! W" Y; {8 |# \- J7 k: t# c; U3 {
  13. 5. 高级(9x9)
    7 ?- Y2 K5 S7 ^  ^3 |
  14. 0. 关于作者* J1 N5 p! i7 _& r" m0 g) j6 v
  15. 请选择1-5开始新游戏:"
    7 |; X5 {, L4 p  u
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    & L5 s/ @- L  ~( j; j
  17.   ("1" tutorial-room-0)
    2 {- r( ~% W- b: d" r) W
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配. D7 l* W9 ~0 i+ D0 m2 {8 T
  19.                                         ; 相应的返回也可以为函数,动态返回房间名: E' E/ Q3 g' Z, e1 F8 [' q
  20.   (t living-room))                      ; 如果条件为t,为永真
    ( I/ S; |) T+ \0 o6 F0 Q' |6 \
  21. ; ?% b+ ]. Q9 K9 M
  22. ;;; 作者信息
    7 q- i: m2 V+ L6 q' X3 f3 }4 `
  23. (def-room about-room
    * H6 X5 Q; W9 A3 q
  24.   "作者:redraiment
    3 W4 A4 h$ d0 @) H5 U- E( m
  25. 微博:http://weibo.com/redraiment
    7 s$ h3 O' A! n& @
  26. 有任何建议,欢迎在微博或微信上联系redraiment。* S2 b5 w. ]2 u0 D& S
  27. 请输入任意数字返回游戏大厅。"# F$ K) A1 {. n* P  g( t# H
  28.   (t living-room))! X; L5 r) e5 s' {

  29. - p$ F2 y; E6 h3 K6 P# T3 j
  30. ;;; 教程
    . g6 |6 ^6 I7 k0 o7 Z* ]
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    ( w. a, h# M1 D0 p0 f/ G# p
  32.   "The number of tutorial rooms")
    2 k% ?5 B- b6 ~; {( q

  33. * k9 U  y0 l) ^) ?1 a8 B' y' ]* ~
  34. ;;; 简化教程的定义
    . ^6 u% G* M& e. }
  35. (defun string-last-line (content)
    * P$ ~5 d; e3 p! O: h6 t5 [2 ]
  36.   "多行内容组成的字符串中的最后一行"# a' G5 R$ I5 O# Q- f
  37.   (with-temp-buffer! s% G: B* s) l- `( `
  38.     (insert content)
    4 ^% M4 m0 z9 M1 @' W3 O' i
  39.     (buffer-substring (line-beginning-position)5 c7 r. w% {' @6 \- m
  40.                       (point-max))))
    , W5 t5 B* o" R- ]

  41. : q7 s' |$ e$ e5 @( K% Q4 I1 p8 d
  42. (defun def-tutorial-room (prompt)
    ' x, s4 z) M: I
  43.   "根据提示语自动生成教程房间。" W  g+ [! [  ~
  44. : ?( X$ ]. {. S& [8 _9 ?) R/ A
  45. 1. 提取最后一行作为问题;
    8 A4 m% A4 e, y! o( v
  46. 2. 分析问题,获取期望用户输入的内容;
    $ {) o! k2 a5 s% z. A; @
  47. 3. 定义教程房间和重复提问房间。", O9 q; `7 Q' S' j2 C
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))# Z( b6 e. s( W3 m( E! G1 a/ g
  49.          (repeat-room (concat room-name "-repeat"))9 J( Z/ _6 F# n& F' G6 C' z2 A+ M# Y) w
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))  d, _# t* R1 `. R4 s& a8 A: a3 a; U
  51.          (question (string-last-line prompt))
    * s$ X+ i9 s# T  F
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)" D, u* M! U) Q$ A* m
  53.                      (match-string 1 question)))% x- L4 \+ M; d0 p2 l2 [0 L
  54.          (doors (if except3 a) _, V* d$ L1 M  k' y
  55.                     `((,except ,(intern next-room))
    : y5 e$ s* o; \" d+ \% P& h% J
  56.                       ("q" living-room)* [- \, m# J! r1 ~* D5 q0 k
  57.                       ("Q" living-room), j# N- W1 H0 |7 a# ~( @4 x
  58.                       (t ,(intern repeat-room)))9 E6 \. i& `1 P7 p5 @
  59.                   '((t living-room)))))
    9 R- M' j" t% B
  60.     (def-room-raw (intern room-name) prompt doors)- ?9 b' o# l/ z
  61.     (def-room-raw (intern repeat-room) question doors)))
    / t4 y4 `  E' B# {, R

  62. 5 Y2 U6 S8 z. L. m0 l* ~" |
  63. (defun def-tutorial (&rest prompts)
    & A5 z* S- T- S0 e( l& j
  64.   "批量生成教程房间。"
    # j2 N7 B- z! d3 A+ L
  65.   (dolist (prompt prompts)9 X4 S: e5 N/ b0 E4 y2 s
  66.     (def-tutorial-room prompt)))
    1 l) c6 P6 v0 l1 a5 N+ T

  67. 8 Z. e4 j) @  L
  68. (def-tutorial
    8 J2 D; i! A  G5 F) G( J" T
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    7 t9 B. m9 B. z# w/ F
  70. 1. 教程% v* q6 w5 z# d: L1 X' I! ]& O6 n
  71. 2. 入门(3x3)
    " m+ {! o- n* q4 E6 ?
  72. 3. 初级(5x5)9 J; z# M2 t4 Z7 R* L
  73. 4. 中级(7x7)- G) _8 j: r) O6 H
  74. 5. 高级(9x9)
    . o+ w/ F' k7 q! }+ |" B5 w; u
  75. 0. 关于作者
    2 K3 v# r, `/ N' R7 H& V
  76. 请选择1-5开始新游戏:1 O/ `  U# }/ I7 N; ^
  77. 您现在正在游戏大厅里。" U+ H& Y7 U# Q! p0 Z  m7 F
  78. 请输入“2”进入入门级房间"
    6 Y# [; t" v( T9 q
  79.   "  ①②③
    & ]/ _/ }- i9 R+ T5 c$ J$ [. t* F
  80. 1 ■ 
    / Z5 D: p2 T- e  {. B" z% ?" S
  81. 2■■■
    9 X9 j# I: I  R& ]  g7 a
  82. 3 ■ + W2 r9 s+ L) X, @
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!5 d& A6 {9 v8 b+ o" F
  84. 请输入“22”来关闭第2行第2列的窗户。"
    : ]3 G8 _5 e" N
  85.   "  ①②③; t) I& w  F4 `1 h( b
  86. 1   9 S  D" m. {6 [; ]$ l( S3 p
  87. 2   
    . g  a9 i4 x* l: a: d- l7 |( M
  88. 3   
    0 Z" }9 H& N1 w) Z
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。- t6 E4 ]% A6 k1 j# }
  90. 请输入“11”,试着开启左上角的窗户。") y1 g8 [; b+ ?  l
  91.   "  ①②③
    3 H0 H( J. f8 ~6 a( c0 e' \5 l
  92. 1■■ 
    ' @. F( O1 Y7 R% _
  93. 2■  3 \/ b4 V9 b2 `, U) N
  94. 3   9 j: n% z% N$ c8 y' G) j4 ?6 l
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。" I& n" O1 A9 b( U
  96. 请输入“13”开启右上角的窗户。"" |0 E1 ~8 {8 f8 ?& T+ L, B& E
  97.   "  ①②③
    % x( f9 M5 j! D7 t
  98. 1■ ■4 T9 a1 l( T# W: l3 U
  99. 2■ ■
    / y8 w1 y5 z* Y9 M$ `1 k& q
  100. 3   
    : @* Q$ S$ a9 R4 j
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。; c1 A. D3 ~7 @7 [+ h; y
  102. 请输入“31”开启左下角的窗户。"( n2 l% M  G- J, P; E( Q
  103.   "  ①②③
    : q( Y4 x0 v2 W& ?
  104. 1■ ■( z. F/ Y8 p0 E- ]$ q
  105. 2  ■
      D9 }( Q+ p. D6 x
  106. 3■■ 3 v2 A8 I8 Q' [; J
  107. 此时,总共有5扇窗户被开启了。
    # R6 d6 Z7 C; i( D
  108. 请输入“33”开启右下角的窗户。"" U5 C# J( d9 S* y' y# `
  109.   "  ①②③% Z) a# p7 K: z0 _
  110. 1■ ■
    : Q+ h/ r4 u; L/ B- ?
  111. 2   9 P- x* q8 ~! [( p! w
  112. 3■ ■
    : n  `  {- |" f* R1 q8 p. b
  113. 现在,只有四个角落的窗户被打开。
    2 d) a3 k! C6 n! h* W; {  n& J
  114. 请输入“22”完成最后一击!"; i6 ]. m7 W, G: L1 {8 i" p# x9 M
  115.   "  ①②③
    , k7 I. x% b8 d( N
  116. 1■■■3 ]/ o- @- ?) }4 o
  117. 2■■■& E# A  D8 q& v0 z0 B
  118. 3■■■8 K9 k9 _4 [7 ^9 S' D+ d
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")6 F* [7 S8 c9 T& d$ i8 V
  120. 4 y( A& Y' _# A% V1 }, M
  121. ;;; 棋盘6 q5 R0 s3 W' o# `% E
  122. (defconst *wechat-5x5-white-chess* 12288
    8 J2 ?. f: M6 g
  123.   " ")
    + g' e9 @0 C$ f0 o3 U

  124. % b7 Y) ^8 d# h% H1 l& s
  125. (defconst *wechat-5x5-black-chess* 9632
    2 ?  ~4 \8 }) a+ k( f
  126.   "■")
    ( ]  O0 A: K9 T" u7 }; F

  127. 5 ?1 \4 s" `3 D( ?# p
  128. (defmacro with-board (&rest body)
    5 E+ y- P+ w6 Z
  129.   `(with-temp-buffer
    0 J% ~& @6 d  A
  130.      (unwind-protect
    # A+ }( D8 @' M2 J1 u0 V
  131.          (progn* K8 G* k' _# y/ y. T; h
  132.            (if (session "board")6 r2 t7 P0 S7 v- L$ ^  p
  133.                (insert (session "board"))). _- [8 P" X- D
  134.            ,@body)" f# c, d6 p, x( H( x
  135.        (session "board" (buffer-string)))))+ l5 g0 l# A* l, j
  136. , v2 ^/ _) _/ |3 N4 W+ T
  137. (defun board-init (size)
    + j: i& G# u4 k: d1 e
  138.   (session "size" size)% a8 e8 U0 m/ ~
  139.   (session "step" 0)$ p/ m" u" k8 g3 L& `2 T
  140.   (erase-buffer)+ X0 C6 V: }6 d/ Z+ o$ w
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    8 H# j: `& g# f+ N5 `
  142.   (dotimes (row size)
    0 [! z$ O9 j  _& p/ x
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    5 f* b' K/ u8 T( \3 m$ u+ {
  144. 1 C5 n( w" X2 b# y; A. R0 ?& f
  145. (defun board-contains-p (y x)
    4 {4 H" s8 ^" D! l+ i# G( M2 @, ^. [
  146.   (let ((size (session "size")))
    ( C% m$ p: ?# D2 n& t1 x) T0 H  ~1 i
  147.     (and (<= 1 y) (<= y size)) j( l4 z# Z" s2 T  n2 f8 j6 r
  148.          (<= 1 x) (<= x size))))
    , e) P1 W. J# T' {& g* R

  149. 5 p3 ^1 ~- l- H+ t4 l; {4 K4 n
  150. (defun board-toggle (y x)
    - |/ T7 O* T9 u& g3 F. n- I  Y
  151.   (when (board-contains-p y x)* |- E; Q  `; v% q" p6 x
  152.     (goto-line (1+ y))
    ( S8 K# e/ q% c5 r
  153.     (beginning-of-line)5 v3 o8 k4 E( @/ o1 J/ r
  154.     (forward-char x)
    - x! L$ _' K% {$ t# G6 ~
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    9 @! D0 n6 P' N. U9 ]9 L" w7 Q. K
  156.                 *wechat-5x5-black-chess*- M, v; Y2 m$ m
  157.               *wechat-5x5-white-chess*))
    . x* f/ \! |5 w' I
  158.     (delete-char 1)))
    9 L6 }0 I6 H" o

  159. ( M5 H. Y  I: E8 s! Z9 c
  160. (defun board-put (y x)
    " @" j& e$ \6 z( t# }# o
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))$ l2 g2 v5 s4 r
  162.     (board-toggle (+ y (first dir)). d, d( x' N* }8 f
  163.                   (+ x (second dir)))))
    4 s9 J- C7 d7 \- a1 B
  164. 1 q) \$ e( y% c9 A6 n& g& r
  165. (defun game-over-p ()
      P+ R. x: g: m: ^, s
  166.   (beginning-of-buffer)
    # w: _% U$ O6 n3 z1 M* e
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))8 F* v) h3 S% B- U- w, |0 h

  168. ; |( \# Z, M  n; Q
  169. (defun board-show ()
    6 Y* ~  s+ g% n. _! Z) j, g+ {) d
  170.   (with-board
    % ~8 t+ ?2 v0 B% F( m% _$ A2 j6 ^6 c, l
  171.    (concat (buffer-string)
    : f/ R& _, U5 P5 B
  172.            (if (game-over-p)1 d8 Z( w% u' q! d
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))1 [6 b1 Q' u& d: x
  174.             (format "第%d步" (1+ (session "step")))))))7 W9 z7 D% q( P1 m$ c- s4 v
  175. ; |/ U- ~  l) `4 E& x. z8 y( ]
  176. (defun board-position-parse (cmd)( T, q( p# e; P: @5 A
  177.   (if (= (length cmd) 2)
    6 \5 ^$ }- L# y2 S3 P& C% g
  178.       (list (string-to-int (substring cmd 0 1))' T5 Z$ M5 I9 `( Y# m; j
  179.             (string-to-int (substring cmd 1 2))), E! N4 y. I# N5 I$ Q
  180.     '(0 0)))$ a' N( D. d& q. }
  181. 3 W6 v0 H' [4 _4 g/ w: g$ K6 N
  182. ;;; 游戏房间
    + {- w5 j* u& d, ^" t- ~
  183. (defun game-room-init (cmd)2 R9 |/ n* X8 ?* Q4 N$ m9 u4 A
  184.   (let* ((middle (string-to-int cmd))
    0 C# n/ j" F8 B7 O
  185.          (size (1- (* 2 middle))))1 v9 Z9 f' p: M$ s5 P" b! a/ h
  186.     (with-board. @. J5 |; ~$ F9 B
  187.      (board-init size)' s+ x/ c+ f: l; C  B  {, e: a
  188.      (board-put middle middle)))1 a9 c5 A$ g3 W( K% m
  189.   'game-room)/ J6 U3 P8 `8 u

  190. ) y$ v0 L# N2 ]! n
  191. (def-room game-room9 W0 \0 x6 @: c# O4 z/ |* h% b
  192.   #'board-show8 h! s) M0 Q' K! X
  193.   (t (lambda (cmd)
    3 ~9 K' ~2 b* c
  194.          (with-board
    ( b+ w* `: t: d! z. z  j4 l
  195.           (if (game-over-p)$ ?# O5 M* a0 J0 K# q
  196.               'living-room8 U* h- k1 Y! \) \6 Q7 N. e
  197.             (destructuring-bind (y x) (board-position-parse cmd); V5 l+ a; B5 A+ f* d) i, p
  198.               (when (board-contains-p y x)& b: u$ d  o5 u( U8 ~
  199.                 (board-toggle y x)
    * F* s/ I# m: H( p7 ]
  200.                 (session "step" (1+ (session "step"))))
    5 _, F& K5 D: I+ a/ i% Y
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




上一篇:微信扫描登录
下一篇:微信红包
回复

使用道具 举报

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏

快速回复 返回顶部 返回列表