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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
9 `+ R6 I* |2 @借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. ) r# T7 l" q2 q" I$ H! S% o
  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;">;; 定义新的游戏地图
    , f. J3 W5 q0 c* C
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    4 J$ ~/ @1 D! V, F0 ]
  4.   'tutorial-room-0)                     ; 默认的入口
    : v0 K2 l- A+ Y: s7 w+ t

  5. , g( z' N$ J' q( ~) W% B
  6. ;;; 游戏大厅- }8 d3 i) W4 ?9 l
  7. (def-room living-room( ^5 k, c" g/ ~) j, L. @3 }
  8.   ;; 进入该房间后的提示语
    . s; `3 F0 c% n; ?' O1 i
  9.   "1. 教程
    5 {- S2 K5 Y9 I4 M7 o
  10. 2. 入门(3x3)
    # }* ], H, k2 E+ u% H/ n
  11. 3. 初级(5x5)1 j3 H! f* p, P- U+ g, i  F' j
  12. 4. 中级(7x7)% i/ \; B; P0 R; g
  13. 5. 高级(9x9)
    0 n& C" @8 d6 n. l9 m# B
  14. 0. 关于作者( {' _& j3 I/ r) Y0 v
  15. 请选择1-5开始新游戏:"
    / P9 r) q* z3 f- Z
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    % ^6 l+ Y7 z' e( E: K: U$ ^
  17.   ("1" tutorial-room-0)
    ' K; k8 @* \& |% l+ Q% H
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配% r/ c+ |4 B* X/ }5 f) J
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    7 R. R& y/ F+ _( K& a0 C! u& v
  20.   (t living-room))                      ; 如果条件为t,为永真
    2 g1 z  g7 o  A7 Q$ w( z
  21. 7 w% d& c" A9 W( A) \5 M, U
  22. ;;; 作者信息
    ' z- p. B% X& V: _+ W1 w2 {, ?
  23. (def-room about-room  s* s' H2 n# G: a
  24.   "作者:redraiment
    . e+ E$ W; ]* d2 d: v5 X& F
  25. 微博:http://weibo.com/redraiment# e) \7 l& G4 Q; d) H
  26. 有任何建议,欢迎在微博或微信上联系redraiment。/ e. Z; h/ l6 K6 p. u5 }9 V
  27. 请输入任意数字返回游戏大厅。"4 {9 ^9 k# ^; N5 Q
  28.   (t living-room))
    * E' N6 n' P% ^6 ^' b% s- P0 u7 u' x. p
  29. ) t& k4 J  o5 N3 a4 G% h& n0 @; T
  30. ;;; 教程6 [" |" L1 J& Q; T& Q7 ]2 ^
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    7 t: S, V% o5 z% O, |0 o. X
  32.   "The number of tutorial rooms")
    4 ]7 ~) b, K2 x& [7 Q3 X
  33. * t' _. y+ e4 `$ v& N5 _
  34. ;;; 简化教程的定义6 U; J" }! ]6 p; u. D
  35. (defun string-last-line (content)9 m, l8 Z. Q+ R# v% A
  36.   "多行内容组成的字符串中的最后一行"/ v. E. ?. [) ?/ I  D# K
  37.   (with-temp-buffer, y4 W6 }% r; M( q: q0 u
  38.     (insert content)  q+ z! W. L, c! ?
  39.     (buffer-substring (line-beginning-position)
    ) P8 @+ f) V6 ?  [# Y2 f- v8 R/ B3 E
  40.                       (point-max))))
    8 f6 i8 D9 t+ X' R7 |

  41. 7 [8 ?& c" e5 p/ |2 s5 s
  42. (defun def-tutorial-room (prompt)/ o" j2 h) T) o( t
  43.   "根据提示语自动生成教程房间。0 l" T% g* k8 t$ j' }; ?

  44. ; X+ b2 N2 i9 B! t+ r
  45. 1. 提取最后一行作为问题;
    / r/ b$ \3 R8 b
  46. 2. 分析问题,获取期望用户输入的内容;% W# j5 p, J  i# o. ]  ]+ g
  47. 3. 定义教程房间和重复提问房间。"7 q# Z; K4 n' @, M( R
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    ! p% v$ i. R- ^; L; E% L* @
  49.          (repeat-room (concat room-name "-repeat"))
    " v  b% j9 r7 @$ H9 L
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    $ ^" D) @: R$ P7 q$ ]7 X8 ?
  51.          (question (string-last-line prompt)): m* B) }4 f+ _, d. K+ V6 B, U. q
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
      h0 O1 d6 E/ }8 b1 u
  53.                      (match-string 1 question)))7 Y4 Y3 c0 O, V. X
  54.          (doors (if except7 f- {, F* [6 {
  55.                     `((,except ,(intern next-room))6 E: q/ r; |# M/ V3 M! A$ f* ^
  56.                       ("q" living-room)
    ! [: i' O3 S* r- Z
  57.                       ("Q" living-room)! p7 `! z. ?; S7 ]3 D  T
  58.                       (t ,(intern repeat-room)))8 a, g+ r' h' t5 _% W
  59.                   '((t living-room)))))/ G- k! v! j% h1 N3 a! N1 k
  60.     (def-room-raw (intern room-name) prompt doors)! m# _  T; G# D1 s# R8 F8 r
  61.     (def-room-raw (intern repeat-room) question doors)))4 f/ |+ V1 j3 J: A, N' ?& |

  62. ! w8 z3 h5 L5 N. {7 {2 I6 [
  63. (defun def-tutorial (&rest prompts)
    ' i, A+ @/ d# a7 f
  64.   "批量生成教程房间。"
    1 G6 v  _+ m# T  L, E# I
  65.   (dolist (prompt prompts)  f+ t- l) G. ?) m
  66.     (def-tutorial-room prompt)))
    9 {: N7 S5 l0 T6 _

  67. - r0 K/ w2 W0 d9 R/ |; N- R* W
  68. (def-tutorial
    1 j" G- z- z0 C" l) X8 U
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    ' H6 `. i0 l8 A7 B' W
  70. 1. 教程
    7 P& L+ [8 z6 {
  71. 2. 入门(3x3), \6 [5 n/ L" I& ?; m4 L
  72. 3. 初级(5x5)8 M# n- b5 H( d3 q- M4 d
  73. 4. 中级(7x7)
    * Y1 s4 D) O# K1 h+ y( A7 S5 F
  74. 5. 高级(9x9)
    & V( p1 Q4 t5 s/ Z: ^& W& l: Z
  75. 0. 关于作者) f4 r7 B( X! u6 Z6 O8 C/ K
  76. 请选择1-5开始新游戏:
    ! z$ L* r$ ?: x7 `3 G
  77. 您现在正在游戏大厅里。
      C( g* l. K$ v& g
  78. 请输入“2”进入入门级房间"" c1 k: S8 Q5 n3 }' W& h
  79.   "  ①②③
    2 R- |, @9 I  ~" O5 f& b
  80. 1 ■ , h8 z: _2 {) l8 O$ ~
  81. 2■■■: ?+ c6 k6 a* f  r0 o5 T3 u
  82. 3 ■ 9 R" v* s2 e+ a
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    ( V0 q) r& N4 ]/ b
  84. 请输入“22”来关闭第2行第2列的窗户。"
    * r% I' j- I9 V3 E' W' `  @
  85.   "  ①②③2 m7 X9 ]% I: O5 r
  86. 1   / U  y1 [, W0 `% x) \1 P
  87. 2   
    * T& k. |. S9 f5 R
  88. 3   , ^/ P5 |+ z  l
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。" H( G4 `$ w7 M+ v/ A
  90. 请输入“11”,试着开启左上角的窗户。"" a0 W$ V9 k) K. ~  k* i
  91.   "  ①②③) s. R! q" L5 g; n
  92. 1■■ + w7 M8 o+ {( I! s2 X
  93. 2■  
    + G' l: @" C2 F7 n7 _  ]
  94. 3     |; }$ W( x$ _0 G
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    + S3 f0 a) b- w9 F2 ~
  96. 请输入“13”开启右上角的窗户。"
    3 P, |9 L  Q6 p8 Z
  97.   "  ①②③( Y/ ~& j6 g! s, R% b/ `
  98. 1■ ■
    ( S8 q" z+ |! r6 B) a+ ^
  99. 2■ ■5 \) ^8 T! ^/ \) T6 A- N
  100. 3   : `9 ~. H3 ]2 {' z& f. A, Y
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。8 O' e' a4 _9 o* i
  102. 请输入“31”开启左下角的窗户。"
    6 n8 `8 K5 C& t1 Z
  103.   "  ①②③
      _3 _% F8 z% i+ l/ C- }6 u) V
  104. 1■ ■
    , o) h! u( m2 x* ~. d' D
  105. 2  ■
    7 J! p+ v- j: g" \
  106. 3■■ 
    * @3 l8 {  K% `& }) Q0 r
  107. 此时,总共有5扇窗户被开启了。
    . u* l9 w8 c! P) _- g9 S
  108. 请输入“33”开启右下角的窗户。"
    0 `# b* }* L6 }+ W2 O
  109.   "  ①②③/ t. G/ Z# `3 v! B& v' i2 f
  110. 1■ ■% [9 ~( j% y- {2 w, }% i, q. s
  111. 2   
    7 T+ ^2 x1 ]2 Z- @6 \% R5 i2 ~6 j
  112. 3■ ■
    4 h8 x& A1 Z3 K" ^: p& ~
  113. 现在,只有四个角落的窗户被打开。
    6 W( h; V6 ^$ B2 Y7 T: E
  114. 请输入“22”完成最后一击!"
    4 H$ y9 i, }+ _* ^
  115.   "  ①②③
    4 L7 D, A! N1 `5 U( M6 e4 D
  116. 1■■■: ?- E, B1 w# Y+ x4 S$ r+ O
  117. 2■■■& R! H; I8 }7 s
  118. 3■■■
    ; @9 v' S0 e" _! ~; H4 |
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!"). }- c. t) G5 w- z6 E( `9 Z3 _
  120. . m4 n+ U# m: H, O9 t
  121. ;;; 棋盘0 R2 z; G8 _- E' D, W" g4 f
  122. (defconst *wechat-5x5-white-chess* 12288
      j% t9 H! J6 m2 C, q7 V% W# S
  123.   " ")5 g, J: k8 O$ I, G: L
  124. ( R2 k. N: B) ~5 A. Z4 p; J( X
  125. (defconst *wechat-5x5-black-chess* 96325 ~+ _5 v" e/ G0 W) ~
  126.   "■")
    / t2 E8 a. d! q* x5 U
  127. 8 B0 N: y: q/ m
  128. (defmacro with-board (&rest body)  L8 A0 ^1 ]$ P4 R. c* ~' b
  129.   `(with-temp-buffer
    " r9 P$ j0 F& Q9 x
  130.      (unwind-protect
    6 P4 z! V; g; [6 q3 v5 x+ }+ h5 e
  131.          (progn
    5 R6 b9 c" j. z4 c' w
  132.            (if (session "board")
    ' K- ?( _) k. A5 s
  133.                (insert (session "board")))
    ) z4 ?3 U* V- q5 d2 W8 \0 Q
  134.            ,@body)/ ~( s1 Q" _3 W6 y
  135.        (session "board" (buffer-string)))))
    1 c6 b1 n0 {) I7 v
  136. 6 N& g7 Y1 E3 f$ M9 n0 R+ h' _
  137. (defun board-init (size)
    5 G( H' l, b3 {- r* y- D: P
  138.   (session "size" size)$ F1 ?7 s8 X- o. `! g. s5 ^3 e% v# Z
  139.   (session "step" 0)% h4 Y/ y( i$ Z* J1 ]4 ^! a
  140.   (erase-buffer)% d' ^9 T4 D% h4 K
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))! v; K1 V1 H: L0 m; Y
  142.   (dotimes (row size)9 A1 o' R. u6 z$ V* M$ U/ ?* X
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*))))); W* `5 A6 Z7 y( \

  144. $ f6 G( o/ j3 D  @! A7 o4 Q3 I
  145. (defun board-contains-p (y x)
    4 K+ V5 L( c, N' E( c
  146.   (let ((size (session "size"))): ?2 V: k- \5 {& f' g( v' v
  147.     (and (<= 1 y) (<= y size)
    3 H/ i* i, b' ?/ q2 O, g' a6 b: i/ M) ^
  148.          (<= 1 x) (<= x size))))
    " o3 L; J, Z4 j" r- n9 ~4 g7 L
  149. + ?3 @4 r/ ^( d  \" K
  150. (defun board-toggle (y x)
    ( k, {! ?1 L; U6 Y) E
  151.   (when (board-contains-p y x)  Y1 t" R% u* x; q( T* F/ k3 P
  152.     (goto-line (1+ y))
    4 |$ G0 g# ?) V* k0 F
  153.     (beginning-of-line)
    ; E5 i% M- m# l1 s/ X% {
  154.     (forward-char x). H4 G8 _0 r8 `
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
      z* a4 v. f! [: \% f* L7 Z; u
  156.                 *wechat-5x5-black-chess*: o( F# y9 n% f- x
  157.               *wechat-5x5-white-chess*))
    ; n, T& c  U0 ?$ X2 Y' {
  158.     (delete-char 1)))) Z8 A6 T+ _; [4 G" j
  159. ) g5 ]0 r& U- m7 P/ G, W$ P
  160. (defun board-put (y x)
    . H$ `4 r2 U# {5 o, p
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
      H4 ^# I" m/ a; f
  162.     (board-toggle (+ y (first dir))
    : P6 l' T. t, A9 H4 p
  163.                   (+ x (second dir)))))
    / o7 n5 a: m9 j: _& w  U0 {5 f

  164. # b0 H/ M* Q% U2 @& v
  165. (defun game-over-p ()8 f7 Z" {4 Q! ~& w- ]& z6 i
  166.   (beginning-of-buffer)6 \. R( M& d' y( V
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    7 z: P4 v; z3 w  Z% I1 q

  168. 0 R  l/ [% \. v8 B8 B
  169. (defun board-show ()
    $ t" r9 U" K% q2 n. _
  170.   (with-board
    ; H( ?/ U" g: x' N4 H7 ]
  171.    (concat (buffer-string)
    6 w: C1 a9 K8 d; R6 E
  172.            (if (game-over-p)
    8 O* r* P. T1 I  r3 O2 c8 i' H
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    . c: x# u# o' }4 ^0 x5 e
  174.             (format "第%d步" (1+ (session "step")))))))' }6 e  y+ I" s  e1 y

  175. ) m  w& m1 L/ A" Y8 p" Q0 y
  176. (defun board-position-parse (cmd)1 }7 H3 k& I0 c1 e0 l/ _
  177.   (if (= (length cmd) 2)* v. T  ]) D4 o' Y. W
  178.       (list (string-to-int (substring cmd 0 1)). e0 z, y9 q6 b
  179.             (string-to-int (substring cmd 1 2)))$ }" r  F5 U5 E
  180.     '(0 0)))4 y9 O$ m: O+ H1 e/ t
  181. ; l& Y8 A, F' Y5 |4 ]
  182. ;;; 游戏房间# D% q( x) t! S! `
  183. (defun game-room-init (cmd)1 J3 [! {/ ^$ u8 Q4 @; J
  184.   (let* ((middle (string-to-int cmd))( E3 v2 U  P" h9 J" Z
  185.          (size (1- (* 2 middle))))! o0 `; F  T( r7 R  o/ @( T
  186.     (with-board
    8 u) Q3 R3 t9 S4 R' U
  187.      (board-init size)
    ! h3 O' t/ e1 |' y9 f9 }7 q. Z
  188.      (board-put middle middle)))
    # S" @4 r- K: \  U8 G
  189.   'game-room)% u) U( D4 {2 n) W: w

  190. ; v. v$ Z% l" U% i* R
  191. (def-room game-room) L+ N  G" v/ T# w2 E
  192.   #'board-show' t) |; M) s0 i2 B1 w  B0 {
  193.   (t (lambda (cmd)
    ( r5 ?- N# q0 p
  194.          (with-board
    ; K+ I/ H0 Q4 o9 P) {* o
  195.           (if (game-over-p)
    , D' H: `0 n7 v" n
  196.               'living-room
    ! Q; I* S, ~; M6 {6 z: o
  197.             (destructuring-bind (y x) (board-position-parse cmd)( ]7 ?4 ?" ]2 Q& |
  198.               (when (board-contains-p y x)
    * B* a1 R) ?5 X; B
  199.                 (board-toggle y x)
    , _; O+ H' K! I! d4 S& D& A
  200.                 (session "step" (1+ (session "step"))))8 x- u" C& K% h& ~& _
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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