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

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

  1. " x2 O. ]8 B; l3 T
  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;">;; 定义新的游戏地图
    ) j" C( j( a9 G& [9 j/ m0 w* y
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    . |7 `# @" Q3 o9 A" d& ]
  4.   'tutorial-room-0)                     ; 默认的入口
    7 I& v( Z% }4 O7 j: ^
  5. ! P/ m& d. O& B5 W7 G8 g- F6 l& R7 q7 U
  6. ;;; 游戏大厅
    . q3 x: U4 M$ `' H9 h- b
  7. (def-room living-room9 S  ~) G5 h1 F& k5 g  k$ x
  8.   ;; 进入该房间后的提示语6 Q! g. g& h  j, c* Q
  9.   "1. 教程- Y# Q0 q0 U" N) W4 ~( X
  10. 2. 入门(3x3)8 `+ M' Y1 s* \% J
  11. 3. 初级(5x5)5 v! k" y4 x# A7 y# [* d  d
  12. 4. 中级(7x7)( z, v4 e+ Y6 h
  13. 5. 高级(9x9)
    $ t* O/ b2 _4 g
  14. 0. 关于作者4 F! b0 K6 Q+ u5 ]! `, j0 e
  15. 请选择1-5开始新游戏:"
    $ y" a, ~4 [6 P
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名* w2 N/ |) s9 Q/ B" v# U1 G" E
  17.   ("1" tutorial-room-0)+ \+ I( |5 I% H( @7 K8 I- E# H
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    6 O: m7 R; {6 y& D+ s, |
  19.                                         ; 相应的返回也可以为函数,动态返回房间名& w" ]/ E- f* {0 g  ?
  20.   (t living-room))                      ; 如果条件为t,为永真* P4 e# Z! ^5 t" O& @+ x/ w
  21. ( P1 ~; @; u' @+ w- ~6 N
  22. ;;; 作者信息% Z( V# ~7 d- k9 z5 G1 n" v$ z$ e
  23. (def-room about-room
    * p! ^4 R5 R2 r: y0 c* Y
  24.   "作者:redraiment
    & Y9 J9 J0 r! Z8 H& b5 u
  25. 微博:http://weibo.com/redraiment, U% a, w6 V2 Y6 o
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    0 n9 G2 m" ]$ O7 g0 w( r8 E) n6 n
  27. 请输入任意数字返回游戏大厅。"# K( I' Q' ]% t9 K+ }( I5 \7 t
  28.   (t living-room))
    . \" R6 R  `2 X" r% [; e

  29. 1 Z! Z% V! `" T
  30. ;;; 教程
    : J" N9 _( @" |1 [: n6 m% r, G
  31. (defvar *wechat-5x5-tutorial-rooms* 0* p! x% ?; c6 h4 }, H2 V5 X; @, H
  32.   "The number of tutorial rooms")
    ' e# O2 p  v$ C5 q
  33. 8 L# ?  a) v; I
  34. ;;; 简化教程的定义7 E  Z: A0 o9 G! U3 x
  35. (defun string-last-line (content)* K3 j% k% B& _5 O
  36.   "多行内容组成的字符串中的最后一行"& e# E$ `+ y$ O$ S: o
  37.   (with-temp-buffer
    2 b* k9 ?7 ^4 N7 O9 s/ H/ k+ X
  38.     (insert content)
    - l. E" h# J1 x0 d
  39.     (buffer-substring (line-beginning-position)" ~9 @3 Z, W. \: y
  40.                       (point-max))))
    9 q8 s6 L- L8 R
  41. 6 s* S* G9 P1 G( Z
  42. (defun def-tutorial-room (prompt)( x/ ]( w3 A. C, f3 \, v
  43.   "根据提示语自动生成教程房间。, m4 r1 ~0 L# |3 z' U; \; i: |

  44. # P2 p. Z+ d- q
  45. 1. 提取最后一行作为问题;
    ; ^; G. x$ N* Z' d  R7 T6 w5 w
  46. 2. 分析问题,获取期望用户输入的内容;
    + V6 M- d4 s% t
  47. 3. 定义教程房间和重复提问房间。"
    # [& c9 b. ]6 Y& k  @3 W2 K
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    ( \3 U% I3 i6 q: j/ g" Q
  49.          (repeat-room (concat room-name "-repeat"))
    $ G5 p+ B8 M* [! Z0 H
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    , m+ J# {( e. s. ^5 ^
  51.          (question (string-last-line prompt))' r" F, v4 C8 [* t0 X1 x  C5 S. q
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)1 i& P8 t  d; a, j
  53.                      (match-string 1 question)))
    & \' t: Q  R" ?- t) N, V4 n
  54.          (doors (if except
    8 y4 ?8 f3 I' u( I' t$ ?
  55.                     `((,except ,(intern next-room))3 w1 Y2 B- B% r6 N
  56.                       ("q" living-room); D* U. x3 L( o% @9 _3 q$ M; r
  57.                       ("Q" living-room)
    1 a5 M. b' C9 V4 B
  58.                       (t ,(intern repeat-room)))" }* I8 s' L# \0 _* P3 U
  59.                   '((t living-room)))))
    9 P; r8 \, T* Z: a# ]
  60.     (def-room-raw (intern room-name) prompt doors)9 ?. G/ f6 g5 a; j# Q
  61.     (def-room-raw (intern repeat-room) question doors)))
    6 q- E5 B: q) P  [; D3 }

  62. 8 \3 T2 s; B" K8 h1 D: Z4 L4 Y
  63. (defun def-tutorial (&rest prompts)
      m# C: H( z4 |
  64.   "批量生成教程房间。"
    7 q& P  n7 o" _& G; X$ r' j
  65.   (dolist (prompt prompts). s: o. r) V  T" y8 R( W
  66.     (def-tutorial-room prompt)))
    6 s8 P3 f# H1 P! R  q& N7 l/ f

  67. 4 C( s  I+ I8 h# X) G
  68. (def-tutorial
    & E2 q9 G) G6 n, O" x5 S" s
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。5 d! U; \  R( l; n9 s6 D+ n
  70. 1. 教程
    3 _4 ^; H! \6 E
  71. 2. 入门(3x3)
    3 k3 Q4 D7 S% R* B8 ?# K
  72. 3. 初级(5x5)
    " @7 K8 t( Z$ l/ A: A
  73. 4. 中级(7x7)
    ' }7 J4 q$ C8 F; I/ X( M
  74. 5. 高级(9x9)/ V1 Y4 G: T' t2 _5 k# e* v) _$ W
  75. 0. 关于作者
    . g, I! q' @9 @) L, H0 E
  76. 请选择1-5开始新游戏:
    6 _: X2 f7 o% ?- N* Y. ?1 l, _
  77. 您现在正在游戏大厅里。
    6 m  A& M+ o, m0 \
  78. 请输入“2”进入入门级房间"
    9 c' j7 ]4 _- u- p# Y! Q
  79.   "  ①②③8 G" o0 E" u1 D- J$ v) @) H
  80. 1 ■ 5 G# H% e  c/ u+ }
  81. 2■■■- \6 |. Y- n  j
  82. 3 ■ ! Y, v! E2 _5 w4 p6 a, U: N
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    $ X3 E6 U" b! G5 H1 r0 }1 S
  84. 请输入“22”来关闭第2行第2列的窗户。"
    + x' f2 C3 @+ `' ]' F9 J; P
  85.   "  ①②③
    3 |5 m4 {" l% e
  86. 1   9 b, r4 f9 a% Q" [$ ?  Z' Z
  87. 2   ; f8 P+ V" ]/ n- `" C3 @( v, ^) ^
  88. 3   
    0 Q) M; s. c2 }; i" {0 \9 w$ p
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。& A( g+ W! {( F9 W4 {9 f
  90. 请输入“11”,试着开启左上角的窗户。"
    7 w5 c/ Z! M0 {7 C  r, D
  91.   "  ①②③
    ) H4 Q) d+ k; s" M0 U
  92. 1■■ : s0 o. q+ |- W  K
  93. 2■  # r5 I; M6 x4 J. v7 U
  94. 3   
    0 q2 ~9 p$ j' L# e) a( C, G
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。) {9 @( u/ q1 N) @3 R7 `3 x
  96. 请输入“13”开启右上角的窗户。"
    2 l8 T* E  E# T6 d$ b
  97.   "  ①②③' |3 A. @! s' l( {) M
  98. 1■ ■
    ; g" ?( r7 ^' ?$ q' S+ L
  99. 2■ ■
    " {! ?$ A& z; a
  100. 3   2 [6 U3 Z' |) ?9 X. h5 n$ z: K
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    " N1 r9 @& W- Y. _% H4 }' I
  102. 请输入“31”开启左下角的窗户。"
    4 v( i% e3 t8 r8 f+ z) B% i
  103.   "  ①②③
    9 r8 o4 r$ v2 A
  104. 1■ ■, L* L+ _, R9 W4 c
  105. 2  ■: e( x% F9 F5 G, ]
  106. 3■■ 5 e# K- B9 R( C+ m4 E# d" K% a- J
  107. 此时,总共有5扇窗户被开启了。! ]9 H, Q4 u8 W) v5 u" G0 D" q/ c
  108. 请输入“33”开启右下角的窗户。"
    * A& f. i  a+ X  l
  109.   "  ①②③
    6 y1 {* p7 ]; B, I" l9 N
  110. 1■ ■
    0 T. x" ^2 e& A" w; J" \$ Q
  111. 2   & U1 }% ^8 u6 D6 L( k
  112. 3■ ■. @3 w/ U& O) i; X' A
  113. 现在,只有四个角落的窗户被打开。8 c! X) l7 k1 S; ]3 b& P" G
  114. 请输入“22”完成最后一击!"
    5 w; Q1 j' b% @) T' c3 V$ t
  115.   "  ①②③
      s. P2 D, B( z$ D6 m* l; i9 s% K4 L3 C
  116. 1■■■
    , l& m. U3 k& b  G; {- g+ U
  117. 2■■■
    # Q4 n7 d* q( \: K( Q& }8 y% c
  118. 3■■■
    7 O, V. ]3 f' a
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    ; e- s/ @4 }/ `' U
  120. 1 D2 P$ `3 y+ H/ U2 J
  121. ;;; 棋盘
    . r3 C' ?2 K8 D- q1 L
  122. (defconst *wechat-5x5-white-chess* 12288! R8 d/ u: l+ n" n
  123.   " ")
    $ u4 h, p& Y7 [: ~( v' q6 T

  124. 9 f( Y- {! ?; O7 o4 a4 U
  125. (defconst *wechat-5x5-black-chess* 96327 s& R0 \1 }; u/ H" k4 E/ A4 V
  126.   "■")' `1 a" T; E7 e0 x2 w* G: f2 r. f

  127. ' [8 G' s$ n$ O" O6 T
  128. (defmacro with-board (&rest body)' s4 K6 X( y4 V/ R+ V
  129.   `(with-temp-buffer
    - q0 ?5 P6 N) q+ o3 X& C, \
  130.      (unwind-protect
    ! m' G% m# W. j$ z# E5 o
  131.          (progn
    0 e$ K/ T+ P  G8 J9 m3 {) v
  132.            (if (session "board")& h9 E2 n, ^: d7 [  d' E3 J
  133.                (insert (session "board")))' v9 ^" i) w7 }1 J0 B7 W
  134.            ,@body)
    ( a1 L' p/ H$ s8 W2 t
  135.        (session "board" (buffer-string)))))
    2 Z2 d/ r& [" l/ p$ m4 [- p  J

  136. ! C: u& A. F0 n2 g( z: d
  137. (defun board-init (size)
    9 U# M3 z1 t$ o  o  D( V
  138.   (session "size" size)
    # |% S! i6 a$ J8 ~5 Y
  139.   (session "step" 0)+ B7 B- l! y; D
  140.   (erase-buffer)' G# s8 e0 R5 t+ `
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨")): T' p% G4 q5 K1 t" ]6 k) f
  142.   (dotimes (row size)5 x$ d& W0 m$ x$ g! K
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    ' Z- T4 K, e6 c0 ~: u4 l

  144. " D  S- C! M. L; Q7 b
  145. (defun board-contains-p (y x)6 c0 e) R3 G+ N
  146.   (let ((size (session "size"))), o0 r. g. k2 Q$ F% d( X4 u* P- n7 C
  147.     (and (<= 1 y) (<= y size)
    : `4 c; O) s+ K3 T# l% K, l
  148.          (<= 1 x) (<= x size))))
    7 s& |; Y& o0 ]9 I7 `0 {7 {

  149. 8 h+ K2 j% N; l$ V
  150. (defun board-toggle (y x)6 D% L( {( Q! B7 I' H) D
  151.   (when (board-contains-p y x)
    - Y1 \$ n* D" T( C
  152.     (goto-line (1+ y))
    . R8 q8 |8 |6 b5 c- L, q
  153.     (beginning-of-line)
    . C: h) N+ x6 k7 `$ [/ z6 T  j
  154.     (forward-char x)
    ( X1 p' T9 ~& p* m
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))$ Y7 f" `* K5 v" n/ Q' k! i$ R/ |
  156.                 *wechat-5x5-black-chess*; W$ b7 W* V' u  D& M6 V
  157.               *wechat-5x5-white-chess*))
    ' M0 R# U9 Z3 G) T; @' ]
  158.     (delete-char 1)))
    6 G8 v9 B5 i  S# j' D( X* a

  159. ) }  L* Q& ^6 X2 N3 y6 i4 m& S8 r; `
  160. (defun board-put (y x)
    3 {% y" D9 Y  F! M. `
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))2 j1 Q, R$ c/ V5 n0 H2 Z
  162.     (board-toggle (+ y (first dir))
    2 G9 c6 U9 d+ v% C5 p: ?
  163.                   (+ x (second dir)))))4 l' ?% j7 f5 x* K

  164. + u7 b. ~: ~" w* Y/ d- b' U. M5 L! S
  165. (defun game-over-p ()5 x% ^# J1 h+ G  Y
  166.   (beginning-of-buffer)
    + W& C! l% l' i
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    1 e# H! a0 P" m- ?6 d$ e4 J6 Y2 ^
  168. : v; ^- j% N  M& V
  169. (defun board-show ()
    - s& B" [) s5 C: X. S- u1 z* f
  170.   (with-board
    ; x0 C5 J+ b, X0 f
  171.    (concat (buffer-string)4 _5 G+ j/ Q+ }+ ]' q% I
  172.            (if (game-over-p)
    $ h  d0 y  F2 k- {8 M
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    9 I3 ]: d0 }- x7 h
  174.             (format "第%d步" (1+ (session "step")))))))2 [6 r3 ~8 U; H3 z
  175. 2 p+ D6 ^& ]  l% L+ |7 F
  176. (defun board-position-parse (cmd)9 }2 F; _" w7 w5 S: L
  177.   (if (= (length cmd) 2)
    6 G' `0 M' \; H9 x* r9 H8 E
  178.       (list (string-to-int (substring cmd 0 1))
    ) G  \; f- a  n" N6 q) D0 O+ l# Q
  179.             (string-to-int (substring cmd 1 2)))6 Y9 Q* M9 P& M1 H. d: t
  180.     '(0 0)))
    / S2 W2 s1 s. x- ?( P( g( q

  181. 4 E& X8 [/ q0 d' k# f6 E" j
  182. ;;; 游戏房间1 R% n! _" w4 D/ [
  183. (defun game-room-init (cmd): E2 b0 }# b: C" I" a2 ?6 ]8 o
  184.   (let* ((middle (string-to-int cmd))
    " d8 }" w4 {: x. {4 P  O
  185.          (size (1- (* 2 middle))))# u+ z! _7 ~6 A/ p2 B  \9 A
  186.     (with-board
    2 B  M! a1 R" w$ w; D
  187.      (board-init size)
    & m( X/ `( J: D& [4 W+ P
  188.      (board-put middle middle)))! P1 R- c3 [/ E3 E
  189.   'game-room)
    " K5 {! [7 z# @. r
  190. : H7 }; i. ]9 g; _& k
  191. (def-room game-room
    : B1 F( {: T+ u2 o
  192.   #'board-show' A0 B2 F0 H7 e5 j2 |! H+ \( F
  193.   (t (lambda (cmd)
    / c+ C2 u( p6 t4 J' B! m
  194.          (with-board. W& E7 r$ V( i+ J& m: }
  195.           (if (game-over-p)
    3 E8 m! k% e* M. H  b: w' ~
  196.               'living-room! }1 k9 H- P( h& L+ N3 |
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    # ]8 `8 F& b0 h& p& ]3 k
  198.               (when (board-contains-p y x)
    ( x6 N# n' @; Z
  199.                 (board-toggle y x)& I! r' n8 \2 h
  200.                 (session "step" (1+ (session "step"))))' @7 T8 E" V8 N8 A" k: P7 q) ?
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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