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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。4 k+ P2 N' y; Y. f
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. 7 l7 n5 {7 D$ m4 Y' l1 s, F
  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;">;; 定义新的游戏地图7 x: Z; ?' [) {. M
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL# O: @# p. c! [  f% J
  4.   'tutorial-room-0)                     ; 默认的入口) X- q) q1 \6 E3 i8 j& r4 o- j

  5. 4 j. ~4 O1 e; C& G' n  O/ L+ w5 {' u
  6. ;;; 游戏大厅$ z+ d1 S4 m, @- t3 Y7 B
  7. (def-room living-room
    5 @0 \& {! Z$ V( l8 {2 O% p, }
  8.   ;; 进入该房间后的提示语  m. }/ R; D' J! ~- i
  9.   "1. 教程7 v& [( C: t5 t" P% w- ^
  10. 2. 入门(3x3)1 K1 i0 C' s) i$ v9 f. D7 V/ p
  11. 3. 初级(5x5)
    ' ^6 V& E( ^. @/ y3 p" \" N* f
  12. 4. 中级(7x7)
    : p5 i: F- W; P  J
  13. 5. 高级(9x9); Q- v, }4 c6 L
  14. 0. 关于作者
    + p9 Z! P0 H3 j, ]4 ?
  15. 请选择1-5开始新游戏:"9 u3 J2 ]% y& Y3 z4 N) j
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名/ r3 @- }  ^7 b& u
  17.   ("1" tutorial-room-0)& H: Z' K! `: k% y* Y$ E
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    2 b% [# @( `* [/ \3 e
  19.                                         ; 相应的返回也可以为函数,动态返回房间名* W# e7 M; ?2 f. |2 T% e! W
  20.   (t living-room))                      ; 如果条件为t,为永真; ?  i# h3 [3 A9 N7 c
  21. 4 z& m, L6 }8 D0 J
  22. ;;; 作者信息: e9 v# H. Q$ x! g+ F% Y
  23. (def-room about-room. A( [1 U2 A" P
  24.   "作者:redraiment( I7 X0 T- o9 U  z2 E
  25. 微博:http://weibo.com/redraiment
    8 k' _; [7 H9 ^& }6 n; I
  26. 有任何建议,欢迎在微博或微信上联系redraiment。# C' ~+ Q* V: h4 |
  27. 请输入任意数字返回游戏大厅。"
    2 g! x. `% P" b2 A  a) O) u
  28.   (t living-room))" M  E- G. @* p  E  k

  29. 7 t$ E, i8 u. w$ S# M, d& ]
  30. ;;; 教程( d% J: s9 F9 E% Q9 s& w# D
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    / r* u6 l* s8 {9 C3 b
  32.   "The number of tutorial rooms")
    $ v, [' g8 E2 B5 h, S7 G, }' H
  33. * N2 ?. O2 S& v" M, o. C3 J1 W; Q4 M
  34. ;;; 简化教程的定义
    6 i9 i) `8 _7 y8 X0 S) {! s
  35. (defun string-last-line (content)
    2 v: A" w0 t; E5 C
  36.   "多行内容组成的字符串中的最后一行"
    5 y* j# n/ w0 t" u* x
  37.   (with-temp-buffer
    ; ]0 @: G3 g% O3 M6 G1 u- C
  38.     (insert content)
    ( w3 J0 M) Y7 S# D! x
  39.     (buffer-substring (line-beginning-position)
    5 T# m$ |0 w5 Q7 K' l
  40.                       (point-max))))
    / D' R$ U; Z. V% E* b! F& p
  41. 0 ^9 p. ^% R  L" ?
  42. (defun def-tutorial-room (prompt)7 @0 ~' n- D9 K+ K& Q! n
  43.   "根据提示语自动生成教程房间。+ p( P: x' \9 d7 |0 i
  44. 4 O9 M: F# k+ y$ j
  45. 1. 提取最后一行作为问题;
    . C, c+ o! e- C8 J+ B
  46. 2. 分析问题,获取期望用户输入的内容;
      Z( }& p3 R0 l% t1 B( U
  47. 3. 定义教程房间和重复提问房间。": H! J7 x# @* Q# `0 J1 `
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    9 }/ S7 I) G9 C# N" a1 C' _1 s
  49.          (repeat-room (concat room-name "-repeat")); }% {% x' n6 t( Y, C1 v; {; L
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    8 _0 F! H& \* E3 ~; i
  51.          (question (string-last-line prompt))
    ! s2 M! X3 p" G# C" S4 G( A/ B
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    : u$ C" Q1 o! T# M
  53.                      (match-string 1 question)))
    , c3 K$ t* I/ k# w8 q3 f3 ?+ o
  54.          (doors (if except# ?, o# s# C9 `7 G: S, E: W
  55.                     `((,except ,(intern next-room))5 E: q. W2 V  W1 `4 n
  56.                       ("q" living-room)
    , i; N# \0 R7 O: j1 y6 U
  57.                       ("Q" living-room)
    ) m2 f) ~- [( R# j6 N3 d) z+ O
  58.                       (t ,(intern repeat-room)))
    3 B  L! v. i9 v) T( R0 h5 N; ]
  59.                   '((t living-room)))))
    6 ?& I1 b# D( A- [( N9 G
  60.     (def-room-raw (intern room-name) prompt doors)
    4 {, y3 s1 @1 x
  61.     (def-room-raw (intern repeat-room) question doors)))7 I! U3 V- z" J, r/ K

  62. ! P: n7 N( n5 c
  63. (defun def-tutorial (&rest prompts)- E3 a/ T1 ]$ `3 a8 c2 T" n# n
  64.   "批量生成教程房间。"0 C; g6 @; {3 ^4 V4 X: V$ ]! N% c
  65.   (dolist (prompt prompts)
    0 H' m. {$ c- q% C' ?7 M5 D
  66.     (def-tutorial-room prompt)))
    " m- B% s  ^0 y/ D7 b- o6 ]

  67. ( _$ A. e6 [- x
  68. (def-tutorial: L9 S4 i' f0 S4 `; U
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    + t2 D4 c1 `: s7 P
  70. 1. 教程% r8 H( y9 k9 L: G# Q
  71. 2. 入门(3x3)' z+ L* o& n5 N, R
  72. 3. 初级(5x5)* l! g3 K, N" Z
  73. 4. 中级(7x7)
    0 H# |& C7 O& d3 Q8 A7 r
  74. 5. 高级(9x9)0 x0 d9 z2 w2 D: u
  75. 0. 关于作者6 m7 ^  D7 e) g: G- e! U4 D9 g1 U
  76. 请选择1-5开始新游戏:8 q6 t6 e0 {" @# T! M
  77. 您现在正在游戏大厅里。3 ^) m' k$ `% U- X
  78. 请输入“2”进入入门级房间"% }8 H  F, e5 r" ]/ ]% D2 ?
  79.   "  ①②③
    ( k$ F) S- I. l* R8 w* S. h6 v
  80. 1 ■ 
    0 M3 M: U  Z" h! _$ U& i, {
  81. 2■■■* v6 O7 F. ^% k" e
  82. 3 ■ 
    ( O2 K9 n4 K; u4 i2 S  j8 E
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!: O: ^- X( q8 c+ r6 f
  84. 请输入“22”来关闭第2行第2列的窗户。"
    2 O, z' h- b) R
  85.   "  ①②③: P) G# T  ]0 m6 Y4 D5 w$ @
  86. 1   % S$ N: @' d9 @" i
  87. 2   8 O/ \: v3 i0 i! o$ z' V' q
  88. 3   : I# g1 t1 J, Y0 n( i. P
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。: [9 h8 }% o. ~0 H
  90. 请输入“11”,试着开启左上角的窗户。". F& r* x- [; @: C( ^
  91.   "  ①②③3 s; C; Z( I6 N) p
  92. 1■■ 4 \, P) d8 h7 \% ?# E8 `
  93. 2■  2 Z# v" H. v: N- ]2 u  `
  94. 3   2 G8 w% U. X9 ^: O6 _' W; ?
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。2 j5 J/ c+ C1 F" o0 P+ [; J: r
  96. 请输入“13”开启右上角的窗户。"
    , X- V5 L. `: a; G
  97.   "  ①②③1 Q( Y& e: `- i( t  H
  98. 1■ ■
    ) t$ ]; k# L2 M" C  I) S/ }& |; d
  99. 2■ ■
    , \( a9 M* z6 O: E9 p' R
  100. 3   : i! h( s, }4 A9 P6 ~! L
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    : W; Y1 E. e- h
  102. 请输入“31”开启左下角的窗户。"# ^( f0 n# ?- u6 d0 Z
  103.   "  ①②③" b( I' w9 j% C/ a3 L
  104. 1■ ■8 L6 j! Z- I" y9 u7 D
  105. 2  ■
    7 z& P4 p7 c% |3 ]
  106. 3■■ 
    * ]# }: ]) ]# j; D
  107. 此时,总共有5扇窗户被开启了。  \" j( D$ g) J5 W9 M& x2 d
  108. 请输入“33”开启右下角的窗户。"
    0 v3 [, y" m  ?+ m) o& [
  109.   "  ①②③# u9 Q* f  o* M" X' Z+ f
  110. 1■ ■
    & v& H! K; U2 N  t8 d7 q
  111. 2   
      \! R/ g# j7 x2 X* M, V2 P( p
  112. 3■ ■2 i) w# T2 B; B3 c7 R* i, i  m& ^+ O
  113. 现在,只有四个角落的窗户被打开。
    ) ?( Z& V9 R' J4 g7 ]- v
  114. 请输入“22”完成最后一击!"# z& ^- |) T- U6 f8 `/ K  o% i. g
  115.   "  ①②③# ~5 r; b: C! u7 V9 }
  116. 1■■■
    * x7 e5 `) m% U4 r
  117. 2■■■* R7 {9 d# x+ E8 X' u7 K4 u
  118. 3■■■/ Y' S2 Y  i0 V# H$ |
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    5 b* z. G: z1 `4 \% D6 j
  120. " c/ F0 J9 w6 Z- R' W( Z" F7 ^: J4 s
  121. ;;; 棋盘
    , k1 P. g: K, y+ q8 l" J) J: V) _
  122. (defconst *wechat-5x5-white-chess* 12288
    - y( y4 r8 o5 Q0 U  h8 O; R
  123.   " ")
    ' z0 y- J& o* G( e) o! f" }

  124. 6 q' ~: W. X* V: x" U
  125. (defconst *wechat-5x5-black-chess* 9632
    # ~0 k: I  V/ Y( f& c
  126.   "■")
    $ |+ Z( H3 V, e. _4 m/ |9 \1 K
  127. 6 P4 {7 ]2 B2 B. \0 Q( w
  128. (defmacro with-board (&rest body)9 z! w. i) ?! ?- s( P& N
  129.   `(with-temp-buffer
    ) L% Q8 K% n- ~1 o  }' X* s
  130.      (unwind-protect
    ! ^7 q3 t' p" q" l; p! L/ I( W
  131.          (progn
    ! v9 J  q7 q" I+ D/ t" `, D8 C
  132.            (if (session "board")
    ! O% B" h  y; R# B; g
  133.                (insert (session "board")))
    + b) w5 ?  q7 ~: a, P' P
  134.            ,@body)4 W9 ]) s9 \4 v' e5 R" f1 e
  135.        (session "board" (buffer-string)))))# j) m  A% }+ n5 B% J% _( i

  136. ) j* n3 s) T, w* O2 L
  137. (defun board-init (size)
    ; k' H% m: ^+ [; H) U
  138.   (session "size" size)
    ( ^" b7 [6 W' R6 N: Q: r
  139.   (session "step" 0)& w3 V5 ?) f  P
  140.   (erase-buffer)
    ; E9 b+ v0 q2 P8 Y3 @* T
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))6 _1 ]! |8 T3 _
  142.   (dotimes (row size)5 H6 Q) {) B" B3 P' ^; d
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    / M0 ~4 k! G4 m- u. X

  144. ; u2 D: r3 R  J  j1 l6 x6 `. \% V
  145. (defun board-contains-p (y x)5 z0 T) k) a8 c
  146.   (let ((size (session "size")))
    % R  ~; |$ E' n
  147.     (and (<= 1 y) (<= y size)
    3 H# [; Z$ u" E4 i# `
  148.          (<= 1 x) (<= x size))))
    2 |# r9 @# D1 l, d% ^& s& G
  149. + }. t- X! N) }# ]4 k
  150. (defun board-toggle (y x)0 W) w1 ?8 _7 U* C
  151.   (when (board-contains-p y x)  T0 S' |8 j* f2 u8 q
  152.     (goto-line (1+ y)). ^- x6 ?' i/ r) ?7 T/ r/ i7 X
  153.     (beginning-of-line)2 I4 L$ a5 T& ^
  154.     (forward-char x)6 N' [& i+ \* d, T4 P- `+ Z
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    ! I7 C' w0 [' _
  156.                 *wechat-5x5-black-chess*
    ) s: A3 H' ^* P& v9 u: L2 T
  157.               *wechat-5x5-white-chess*))( y& \! q* Y: n; j
  158.     (delete-char 1)))
    : j# s, k& k8 Q( j0 A; D7 w
  159. ( x/ Z8 g/ t8 p5 N
  160. (defun board-put (y x)
    / A. j6 M) O/ C" w
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))1 J! Z7 D  V; A. [/ w7 K8 q
  162.     (board-toggle (+ y (first dir)); W$ W" x$ m" \; b+ I
  163.                   (+ x (second dir)))))6 m/ A# T0 J# h) J: l; g4 u& F

  164. : S9 S  r0 G- n- Y4 C
  165. (defun game-over-p ()! W8 g, S: K% ?
  166.   (beginning-of-buffer)
    8 O2 O/ j/ `; d' M3 t  [5 N+ y8 p
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    * r! x: b1 O( D8 @
  168. + {# Y0 [3 G- H
  169. (defun board-show ()
    - V) G7 Y7 m: w1 I* W1 t9 J2 y: r; \( G
  170.   (with-board
    7 n% k& x/ @6 ^5 p$ o# {+ s
  171.    (concat (buffer-string)
    ; I/ A: v0 O* J; f4 j2 O4 v5 U
  172.            (if (game-over-p)! x1 V8 x2 S# \) t' `
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    2 U' B) J# R! g& D
  174.             (format "第%d步" (1+ (session "step")))))))
    ' W3 ~& b. f, l1 b5 j" f7 V* V
  175. 8 |9 T3 ~6 k* q
  176. (defun board-position-parse (cmd)  I. l- R: `3 y  s* T* l' S
  177.   (if (= (length cmd) 2)
    3 P, A9 J) B+ ~" ^
  178.       (list (string-to-int (substring cmd 0 1))
    4 O3 a9 {2 P5 ~
  179.             (string-to-int (substring cmd 1 2)))
    + s% Z( V$ n' O( W3 d  [
  180.     '(0 0)))
    ' L0 b2 Q" S+ O8 c
  181. ( p* u# p/ s7 s# V. m: `7 D7 ~0 ~/ H
  182. ;;; 游戏房间
    2 ?8 H- ^! D( }4 f
  183. (defun game-room-init (cmd)
    " ?( o2 Z, C# n: z7 C: h
  184.   (let* ((middle (string-to-int cmd))$ B) n% s( i% {2 ^& [9 ~
  185.          (size (1- (* 2 middle))))
    0 A  X# g  d6 {
  186.     (with-board7 b: S2 [6 z3 W: m% E
  187.      (board-init size): I& g+ U7 y, Z& v$ r
  188.      (board-put middle middle)))( h+ g) o) D' h" f( d8 z
  189.   'game-room)
    * X9 g% v2 w) S

  190. 2 }; @. o: z3 W; T
  191. (def-room game-room( Y# d! b: Q5 \
  192.   #'board-show. M& M; Q- i7 C8 i
  193.   (t (lambda (cmd)! O: h0 n, q5 m5 |* k8 |! U
  194.          (with-board
    % p4 I& f1 e9 U3 S: R4 g# T
  195.           (if (game-over-p)
    4 j6 a- A0 W& {6 L- `( l
  196.               'living-room0 b( m' J3 j: Y% S9 @
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    0 v+ m  g2 X7 \" `0 Z
  198.               (when (board-contains-p y x)
    6 q3 C6 X- o2 }, o6 O6 i3 }
  199.                 (board-toggle y x)
    % ^  c4 T4 R% `% f# k" }/ f
  200.                 (session "step" (1+ (session "step"))))
    + i! T$ Y& o7 X
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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