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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
' ]4 n8 I% ?2 y借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. # ^1 W' Z3 j4 ]
  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;">;; 定义新的游戏地图
    + _2 v: h# P+ f) ?# e! S- i4 F2 j
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL9 r! E+ y* y7 B9 x( q
  4.   'tutorial-room-0)                     ; 默认的入口
      d5 d7 r; F' @& N( H6 W

  5. ! R) }) T1 Y& B
  6. ;;; 游戏大厅
    0 u( M* F; j4 `* }& K$ D
  7. (def-room living-room
      D* m( T) B* f' @- i0 g, v
  8.   ;; 进入该房间后的提示语+ b4 F# c7 A: d- X2 ]
  9.   "1. 教程
    % J' w6 x( F/ `4 Q
  10. 2. 入门(3x3)
    % z3 R4 |% T- h; T1 I7 R4 [
  11. 3. 初级(5x5)
    2 J, v* m* }4 {- ]9 v" ^# p
  12. 4. 中级(7x7)
    + I, w; K. I. K" I% h
  13. 5. 高级(9x9)
    . Y( q6 |5 q; M" A
  14. 0. 关于作者
    5 J. e; x4 b8 \4 K* ~5 J3 ~
  15. 请选择1-5开始新游戏:"+ R+ Y7 N4 Q- w( b) @
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名2 a- l9 N' \+ }$ }$ d
  17.   ("1" tutorial-room-0)
    . Y0 ?/ @) k) _+ S0 H
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    7 u# ]( w5 y) n" D0 f2 f
  19.                                         ; 相应的返回也可以为函数,动态返回房间名& H! T2 b5 }& R, r' R  P
  20.   (t living-room))                      ; 如果条件为t,为永真0 N% m8 B  ^( {: b; b
  21. 1 m( D* I7 {4 D& ^, X
  22. ;;; 作者信息* C; G$ s, k3 f* _. z& m, G# g
  23. (def-room about-room
    ) n+ |$ V* M5 e  V: k
  24.   "作者:redraiment" h0 A: y3 Y7 M/ C7 h* F; k: `
  25. 微博:http://weibo.com/redraiment0 Z) s4 ~6 e0 Z* P9 ~! C
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    7 w7 N# n0 U6 \0 |) M
  27. 请输入任意数字返回游戏大厅。"1 P( q, I- C% _; {! v
  28.   (t living-room))! `8 {/ \# r) g3 k" O& s; L

  29. 5 W( Z: q' ~. I6 T  ~5 z- o
  30. ;;; 教程& u; c- R* i0 }. c4 O9 x
  31. (defvar *wechat-5x5-tutorial-rooms* 0- Q3 N4 z5 u" N
  32.   "The number of tutorial rooms")2 r7 D% h1 X; ~0 f* m, Q5 t

  33. 3 s/ z+ e# h' Y1 H2 d+ c& v
  34. ;;; 简化教程的定义0 y$ ^- Q+ c$ X  [
  35. (defun string-last-line (content)
    0 q1 t* Z8 [' r" M; \, X7 L
  36.   "多行内容组成的字符串中的最后一行"9 K! \3 _# m/ F$ M( c9 e1 U
  37.   (with-temp-buffer% ^3 ~- R7 m. ~7 m% I  }
  38.     (insert content)
    4 a2 {8 m( y" Z  p  F4 b; ~( r; _
  39.     (buffer-substring (line-beginning-position)
    $ P, L: L3 a" ^$ l
  40.                       (point-max)))); j' l/ ~1 N: j

  41. $ f( n8 F. K6 @, I) f
  42. (defun def-tutorial-room (prompt)
    ( c' j( P: l% N. {9 p
  43.   "根据提示语自动生成教程房间。
    / s2 D( t/ m7 Y7 K+ j: f! W' b' m& k

  44. , `- J$ X! O6 f. a
  45. 1. 提取最后一行作为问题;
    ( J, o4 E4 ~8 X6 @
  46. 2. 分析问题,获取期望用户输入的内容;
    : F" _) q! a7 v. K" u
  47. 3. 定义教程房间和重复提问房间。"
    $ `% F' N" f; @5 q0 o: m
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    7 l4 a! v0 U! T  u1 \
  49.          (repeat-room (concat room-name "-repeat"))" Y  ~3 L8 a. s
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    - \' C* ?, E1 y$ f  M
  51.          (question (string-last-line prompt))" {9 t! ]9 Z9 F) ~, k
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question); Y( n/ M0 l5 m; K! y+ E1 @) d
  53.                      (match-string 1 question)))
    ) r7 t1 T1 ^+ o9 Z/ o  M" y
  54.          (doors (if except
    ( e4 F8 C/ b. `
  55.                     `((,except ,(intern next-room)): N7 G+ C4 N6 u; K, ^. B1 V
  56.                       ("q" living-room)) B5 C0 u: A3 A# ^: L. ?, k
  57.                       ("Q" living-room)
    $ |6 s2 {& p& s, _
  58.                       (t ,(intern repeat-room)))" }4 J& L) {; @) j+ |0 r1 f
  59.                   '((t living-room)))))1 H& g1 }( {3 l1 s$ @; T2 y
  60.     (def-room-raw (intern room-name) prompt doors)
    5 _' ^( }4 R6 L% {$ ^
  61.     (def-room-raw (intern repeat-room) question doors)))4 M4 A4 b# ~/ a! R
  62. " J$ g, ?" l6 {$ S
  63. (defun def-tutorial (&rest prompts)
    - s5 x5 z1 m+ Y1 p# `, C) P
  64.   "批量生成教程房间。"
    / j9 Y$ e  M2 u
  65.   (dolist (prompt prompts)3 T( N3 d" a: K# B  H* I
  66.     (def-tutorial-room prompt)))6 Z- s$ w+ \3 i

  67.   r7 j, _$ H7 O; p/ K
  68. (def-tutorial8 l6 V5 W" }) j% `" I
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    4 t: l% G% H2 A
  70. 1. 教程
    : k! i' v* Y+ P
  71. 2. 入门(3x3)
    ( d. e2 s; x: ?9 d
  72. 3. 初级(5x5): f% L# U& p0 q/ q
  73. 4. 中级(7x7)* ~  z! ~' c' e& H# P3 U: L
  74. 5. 高级(9x9)
    * F  f  ^$ B2 }6 y
  75. 0. 关于作者1 M/ n2 _5 g! c% A2 P. J* s
  76. 请选择1-5开始新游戏:* ~( R8 g- b: x: W0 n8 u- e2 R
  77. 您现在正在游戏大厅里。
    & y, s4 p# f/ P6 B
  78. 请输入“2”进入入门级房间"
    ! ^. J, [/ G7 h, q5 ~# n% r# @
  79.   "  ①②③
    4 j9 z9 L. H# Z" f
  80. 1 ■ ) f7 C: y% w. I  c& {
  81. 2■■■
    0 J: O, w3 M; N( _+ Z1 k, \
  82. 3 ■ 3 I+ X% C3 V0 F: C5 l, a
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    ' w8 ^; W$ O' }) r
  84. 请输入“22”来关闭第2行第2列的窗户。": b- R2 _+ @- q
  85.   "  ①②③1 U- r" _! ^" F
  86. 1   % @+ E& Z; V. G0 G( }5 f, v* d
  87. 2   
    " j0 V4 @& a7 F! x" {
  88. 3   
    ' W" v3 w$ p0 B
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。/ U$ p. f  y9 C7 f0 ?
  90. 请输入“11”,试着开启左上角的窗户。"
    % V* ^) j& t3 ?; Z( t
  91.   "  ①②③' Y) a9 R- N9 k! c3 V
  92. 1■■ & l1 P, f% z  o: V' {( @  H
  93. 2■  
    / y" _9 N2 r1 n- E1 z0 i
  94. 3   
      N( T" A5 i& v
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    ' [# J" z/ ~! [( ~8 n# Q
  96. 请输入“13”开启右上角的窗户。"
    . c( w! d/ s) k/ \+ F! Z
  97.   "  ①②③# L! n6 j5 z1 Q, _
  98. 1■ ■
    9 t7 o+ N- o5 G% v$ |; C: Z6 w
  99. 2■ ■
    4 t+ g! z3 J( j9 g4 ~- S
  100. 3   : Q  X, q/ U) W$ y7 w' q7 Q
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    / F: z" ~, N. F7 B: S5 X, H# g; H
  102. 请输入“31”开启左下角的窗户。"
    & P& j' `8 w2 a1 u' C# |
  103.   "  ①②③8 ~# J6 g7 E# j: Z4 G
  104. 1■ ■
    - d* G/ Y, g( N& ^* x5 l9 s- y
  105. 2  ■/ e0 b2 _  A" I1 ~3 t4 R8 ^9 H+ T
  106. 3■■ 
    ! N  T9 y, F6 |3 h3 k! z/ o
  107. 此时,总共有5扇窗户被开启了。) v2 c8 }6 ~$ ?9 ^8 \6 y. b
  108. 请输入“33”开启右下角的窗户。"" x. O$ A. ]# P3 A
  109.   "  ①②③( O; t/ @$ J- p7 L  m+ a
  110. 1■ ■3 W( u$ O0 ?+ j
  111. 2   - `$ J0 z( Y: m# v) ^) |
  112. 3■ ■
    : q' z. _% V2 N. l9 h  @
  113. 现在,只有四个角落的窗户被打开。
    2 Y. h* I. b$ U! ~
  114. 请输入“22”完成最后一击!"
    5 `2 k! i9 y( F$ `3 G/ ]
  115.   "  ①②③
    1 @; v+ S, Q7 c# O, a2 H1 w
  116. 1■■■
    : Q$ f' Y8 ]; h2 }9 @6 E! ^. X) T
  117. 2■■■
    6 b. r1 E. V# @
  118. 3■■■+ L0 p! u( q. q
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")# {7 J# }' c# c4 i' i

  120. " G* M* ?0 u+ y( q# Z
  121. ;;; 棋盘
    ) D! {& s) R! N5 q9 E7 l
  122. (defconst *wechat-5x5-white-chess* 12288  T( M+ h# p; _! N6 ~/ T, O
  123.   " ")
    . d2 ?' A* S2 P2 s' v" P
  124. 7 @: m4 r8 F9 m7 |5 s- I# s
  125. (defconst *wechat-5x5-black-chess* 9632* K- _& S" U9 D8 r
  126.   "■")- v% G& ?1 ]$ D- u- i
  127. ) Q$ p* Y3 O5 k. L
  128. (defmacro with-board (&rest body)  c! v# x- ~, o
  129.   `(with-temp-buffer) \7 Z. {+ }- b" u  R! i5 M5 S
  130.      (unwind-protect
      L' G  H5 i) |4 u0 W3 i7 R
  131.          (progn
    2 ^! ~" k% ]) q0 h
  132.            (if (session "board")- O* a1 h* t: b
  133.                (insert (session "board")))
    / e  V! I9 k: Q  B6 u
  134.            ,@body)
    8 l: [) Q8 G$ _8 D: G" p( H0 |
  135.        (session "board" (buffer-string))))). B/ g- h) z0 G1 E  {- M

  136. . m4 x3 ~% U- ]- U7 h2 Y% j+ \
  137. (defun board-init (size)" _3 \& H$ a2 s3 J& r/ @; A
  138.   (session "size" size)8 f% i$ E; n: b' |( |/ E. q
  139.   (session "step" 0); e0 ]. f  N: M% k
  140.   (erase-buffer): a) S, R! h* m" ?$ R% A5 e
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    * V/ S# A/ t! b. F# J9 ]4 r7 A
  142.   (dotimes (row size)
    : w6 }9 ]8 e- c! H% {( q' o
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))1 j- ]8 b3 A. {, c$ i

  144. 4 a  Y3 b6 {* o+ \6 H; ^6 f% g- U* l
  145. (defun board-contains-p (y x): Z& Y3 X* P. {' m% ]$ i
  146.   (let ((size (session "size")))
    9 [) _/ g7 I4 R% V; f
  147.     (and (<= 1 y) (<= y size)
    8 l- Y& J8 A1 D" y0 ^5 }! X
  148.          (<= 1 x) (<= x size))))) V7 E6 {9 R% m+ Y2 y# ^$ r4 W2 Q
  149. ! p( ?1 Q6 m& b3 H+ t* j
  150. (defun board-toggle (y x)& Y8 c. k* \# Z. S
  151.   (when (board-contains-p y x)( Q4 T/ J' |9 s; U3 [
  152.     (goto-line (1+ y))
    + z6 Q% ?) w4 v( P
  153.     (beginning-of-line)7 x& @! g( b8 _# C
  154.     (forward-char x)
    ' Z4 c: a+ Y3 B! z) T2 x
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
      K; W4 u& s! Y
  156.                 *wechat-5x5-black-chess*
    ( c. `  p9 K3 v, N  F
  157.               *wechat-5x5-white-chess*))! L3 u4 j+ P+ l* \7 S
  158.     (delete-char 1)))% k0 f" X& j& J

  159. ! |1 P6 f6 G6 m
  160. (defun board-put (y x)
    - y: A& r5 B; b7 |+ ]- T
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))) w7 u! m( l7 E8 O) ]$ S, @
  162.     (board-toggle (+ y (first dir))
    ) `* }' Q# w  u! f, {
  163.                   (+ x (second dir)))))
      H) q2 J2 Y  p8 c  o! F- l

  164. 5 r% L7 Y& a- D
  165. (defun game-over-p ()
    5 W2 x; v2 j7 w6 e+ F& ~
  166.   (beginning-of-buffer)
    $ G* F' b. A2 T$ c* Q: ^( B: v9 p# L
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))& ]1 y+ {, F6 f4 I4 O6 ~

  168. 9 c" y" B: S. k" x" `$ x3 i$ y
  169. (defun board-show ()3 a- Q7 _. R1 r3 N! t
  170.   (with-board
    7 d6 U3 A9 s+ s
  171.    (concat (buffer-string)
    8 V% R5 u( d7 F( a0 R' b- n
  172.            (if (game-over-p)% F' \7 u  i7 [/ m; p7 P
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    $ G4 `  u. N1 b' v& |7 P! P) n$ d
  174.             (format "第%d步" (1+ (session "step")))))))- T$ ?) T. g( S( Y& W7 }( _6 A* N

  175. " X1 T" @+ ]7 @% Q' [
  176. (defun board-position-parse (cmd)  Y  R+ [( Y" i) r: L
  177.   (if (= (length cmd) 2)
      n$ f  _  S; M( ^+ a
  178.       (list (string-to-int (substring cmd 0 1)); ~% o5 ]! n7 }
  179.             (string-to-int (substring cmd 1 2)))
    ; F) x! L) d4 C) O% z9 k' c* _0 A
  180.     '(0 0)))" O; Y3 K9 |# k

  181. & a3 r/ @! ~; [' A5 e
  182. ;;; 游戏房间8 p0 g- c1 \6 U5 i/ A
  183. (defun game-room-init (cmd)( I( B0 l( ~! s
  184.   (let* ((middle (string-to-int cmd))
    - `+ B7 s: M1 I* {+ H
  185.          (size (1- (* 2 middle))))
    % i$ P/ M. |, O1 r
  186.     (with-board; @3 a- h; N+ S* B& m
  187.      (board-init size)
    6 V/ A  B: C+ ^, ]3 y; x
  188.      (board-put middle middle)))
    ( y0 U% @& Q* ]9 U" C1 u
  189.   'game-room)1 \/ H1 p, I: [2 ^0 r
  190. ; _% j* E( \2 l9 p- E
  191. (def-room game-room
    . G) C; A! M' n9 q9 g! g3 H
  192.   #'board-show5 G; o3 v' h& x3 _% f
  193.   (t (lambda (cmd)
    ( a" A" D0 s7 f5 Q! f2 h1 |
  194.          (with-board- {2 [% r  ]) ~+ r0 y
  195.           (if (game-over-p)
    8 x  g! g0 X* c; w3 }" d
  196.               'living-room7 ]9 J# j, x( l7 V( E$ K
  197.             (destructuring-bind (y x) (board-position-parse cmd), w( |7 k6 i: r$ `+ v0 v
  198.               (when (board-contains-p y x)
    3 H3 Q9 S/ f- U
  199.                 (board-toggle y x)
    / X% v% m. N" L* \4 c; D
  200.                 (session "step" (1+ (session "step"))))$ ?9 P# [$ D4 b* j3 V
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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