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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。! g$ A1 r! `( }, h1 k
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. , r9 p5 J$ a9 A7 W, p" I
  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;">;; 定义新的游戏地图+ F4 {6 O! \$ \3 H5 u2 V. ?
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    * B7 `# |0 g4 M! m0 f5 X" ?
  4.   'tutorial-room-0)                     ; 默认的入口7 G  q9 |3 b5 d( e: ?2 ?% U

  5. 0 s5 ~2 O% O9 m! c& L$ X/ O
  6. ;;; 游戏大厅; s; C# E, y0 L. f8 _$ l8 J
  7. (def-room living-room- L; T4 X/ O) Q
  8.   ;; 进入该房间后的提示语
    ) n( Y' I4 f' [- s" ^7 W3 I
  9.   "1. 教程
    2 s9 O6 ^7 z" D: Z  @0 w
  10. 2. 入门(3x3)  |1 {& M0 B' y2 z5 f/ E  r
  11. 3. 初级(5x5)
    * E* o8 }$ O1 ]$ v' Z( m
  12. 4. 中级(7x7)
    4 `. d. o) ~' ]! q+ ~) \
  13. 5. 高级(9x9)
    2 D) E: x: [: P. @
  14. 0. 关于作者2 e- |; H$ L! S2 y0 X
  15. 请选择1-5开始新游戏:"
    : @5 I$ m$ I# v) s9 \4 _0 j
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名( L/ o2 P! {% b7 ?# F5 r
  17.   ("1" tutorial-room-0)
    . c4 R! n- _7 a6 z- d7 J, c, ]' f- @6 J
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配1 w& g1 q' T/ l/ U. L
  19.                                         ; 相应的返回也可以为函数,动态返回房间名$ M) ~1 g) t5 {
  20.   (t living-room))                      ; 如果条件为t,为永真6 a. [% v2 d# T- Z% Y  k" v4 _
  21. % q7 O4 l* @6 I( p7 W- m
  22. ;;; 作者信息5 ^+ k" P' N' z- E5 ]7 i8 V! t8 Q
  23. (def-room about-room
    0 q& W$ `9 D" W2 w
  24.   "作者:redraiment3 Q& T/ w& }. b( J% |$ Y
  25. 微博:http://weibo.com/redraiment1 v! o0 b% t* r8 L; i3 W
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    - T" x1 h& _- A
  27. 请输入任意数字返回游戏大厅。"
    # g/ E4 H) `: K8 [
  28.   (t living-room))
    2 f/ i9 u* D8 K- k+ ]
  29. 1 O9 A# B/ F+ g/ V
  30. ;;; 教程- y' R, }+ ^# ?! @
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    # U4 g8 b4 X; G
  32.   "The number of tutorial rooms")/ `2 O  N$ C. x% d$ h1 a
  33. 5 ?5 F1 `% \0 |5 T/ Y
  34. ;;; 简化教程的定义' Y+ D! K; s$ I! F
  35. (defun string-last-line (content)& A& h  e* O/ I+ {  q
  36.   "多行内容组成的字符串中的最后一行"$ U+ A' m" `' B# R  l2 [: b# a2 {
  37.   (with-temp-buffer
    ! b( t  S/ }) v$ F% M" C9 v
  38.     (insert content)
    + d* i% w6 D5 Z0 [1 L' l1 X" D5 w
  39.     (buffer-substring (line-beginning-position)
    / p4 X% ]2 U1 h
  40.                       (point-max))))4 X: \/ {+ t+ u' S7 F# s

  41. ( H" L1 `5 ~0 u4 b( Z. j& s4 A
  42. (defun def-tutorial-room (prompt)% l9 a' f' Y4 a: [  l# }
  43.   "根据提示语自动生成教程房间。
    # G+ S6 s5 K4 j5 u$ M% x+ w
  44. 4 r: i! T* C  z# E; e
  45. 1. 提取最后一行作为问题;& S8 t% I& m  A2 x
  46. 2. 分析问题,获取期望用户输入的内容;
    9 g* a3 ~! e+ O) \+ @7 ?, w+ M& n" P
  47. 3. 定义教程房间和重复提问房间。"
    7 ^) f* O4 i; }( m' f6 M, C
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))+ M! _* N9 D2 B
  49.          (repeat-room (concat room-name "-repeat"))* V  g3 G' k# u' U3 E
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))" e) R  R! Y  `* y7 q
  51.          (question (string-last-line prompt))
    $ ^# M( Z' T: \/ k. h
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)$ [1 y$ g0 @. C: |+ W& [6 b; D  K
  53.                      (match-string 1 question)))+ k9 |$ m" w5 F( A! m; ]* B7 H
  54.          (doors (if except
    ) f0 ?* U7 o9 ?% Z. K
  55.                     `((,except ,(intern next-room))4 X. a  k4 e$ D' W& k- V' k
  56.                       ("q" living-room)
    : _% I" z- p% F# e: d
  57.                       ("Q" living-room)3 o) T, t' l* u* x* }; H
  58.                       (t ,(intern repeat-room)))
    + `& S0 z4 X' o7 W/ f% u; T# i6 c
  59.                   '((t living-room)))))
    . _9 h1 m! p" y* d: b( U
  60.     (def-room-raw (intern room-name) prompt doors)/ t. E+ ]# v3 \: o2 [) f5 S4 B
  61.     (def-room-raw (intern repeat-room) question doors)))
    ; h! z; W3 v$ e2 O
  62. & f& S* n7 Y; h0 P% e1 ^
  63. (defun def-tutorial (&rest prompts)  M4 x) u! k5 h- q8 P2 F8 z1 t
  64.   "批量生成教程房间。"& J7 ?4 T  {* E, g7 N7 F- H% Z
  65.   (dolist (prompt prompts)2 h0 O3 F7 S4 \/ p# n/ W% A; ^
  66.     (def-tutorial-room prompt)))
    1 p8 ^0 K6 k6 H/ K+ n
  67. / J! H0 a/ Q* n4 o5 X6 i
  68. (def-tutorial
    : D2 l# f9 o# }5 ?
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。% c* z% a! \, m0 r; T) U* W
  70. 1. 教程
    . @+ T& n9 B% N! U8 f& [- r  K
  71. 2. 入门(3x3). f# q5 h3 i, B9 ~/ E0 K
  72. 3. 初级(5x5)4 X- X3 P$ F+ x3 ]1 c5 V- n
  73. 4. 中级(7x7). k( H" r+ M2 F4 _) q) l" ]" h) V
  74. 5. 高级(9x9)& z  h2 S: E0 G% H
  75. 0. 关于作者
    $ s* m( P' @# S& E& p
  76. 请选择1-5开始新游戏:1 c/ J$ x# N1 V8 \* q. p
  77. 您现在正在游戏大厅里。- C1 U, J/ _7 T
  78. 请输入“2”进入入门级房间"1 s, H  u, X6 G0 O! }. V; t& j) N% Q
  79.   "  ①②③3 @  R' m  Z* O( o  Y( ~9 S1 V
  80. 1 ■   \0 [1 m$ q/ D2 q3 ^" E( b
  81. 2■■■
    % _; o9 \' R+ `7 d% O" z
  82. 3 ■ & t9 l+ _. G0 {3 c) |- d. E. }  z4 }6 L
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    " S" n" I; X  D' S6 r$ ]
  84. 请输入“22”来关闭第2行第2列的窗户。"0 Q' r0 y/ l/ {3 T/ k
  85.   "  ①②③5 S( V0 n) V- D( k0 r- I: z
  86. 1   # b* C3 @( p( z; r  m
  87. 2   8 x, K6 v' H8 {+ O& f  G+ Q% q
  88. 3   % {' I: `( @3 O& b. m* _3 y) x
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    - C; x0 O  D( l
  90. 请输入“11”,试着开启左上角的窗户。"3 }" O9 d8 t5 _2 F: D
  91.   "  ①②③
    % T5 R  \- P1 z
  92. 1■■ 
    & {1 U4 j5 T% U! Q7 N; o+ }
  93. 2■  0 `: k. X3 M5 p5 f  ]# b
  94. 3   4 o3 S2 o3 M7 I- f
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。9 n! k6 u5 O: A" n9 F' v9 f
  96. 请输入“13”开启右上角的窗户。"4 R& {0 h7 n" l& T, _! l
  97.   "  ①②③
    ) k/ _  c4 I' F! D  d$ v
  98. 1■ ■, X  S: O) o) W- t) h* s
  99. 2■ ■# g/ h$ ~! e- }
  100. 3   
    + R' G2 S9 U+ G0 b6 Y
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。: X4 M5 Q6 G. I1 L! m
  102. 请输入“31”开启左下角的窗户。"
    & b( \8 m' s- r$ ?
  103.   "  ①②③
    . \, M/ ?5 s9 N0 T, @7 A
  104. 1■ ■6 Y- M+ n1 \2 c3 @
  105. 2  ■5 r4 O& E$ q/ T) \" C8 o
  106. 3■■ ' \) c- |  i( Z% t5 V
  107. 此时,总共有5扇窗户被开启了。
    , \& }; J+ w4 C2 U1 Q. V( v  ]
  108. 请输入“33”开启右下角的窗户。"
      I- R& k- K. g8 N) q; E: w8 z; O2 `
  109.   "  ①②③3 I* s% `6 R& t
  110. 1■ ■
    $ K! I: }, }* S: N5 ~( e5 o
  111. 2   7 H4 s5 x/ m' E: X1 c8 K: n1 M
  112. 3■ ■
    ! g. ], W6 q/ @9 ]  Q
  113. 现在,只有四个角落的窗户被打开。" }: W9 S; D! j: s( B* u
  114. 请输入“22”完成最后一击!"
    1 R' V" p4 V" X3 g, s
  115.   "  ①②③
    2 Z* ?$ I) E6 L1 J7 H
  116. 1■■■$ O7 H1 W' q7 e- r8 H3 T
  117. 2■■■6 a- u. ?; c/ T3 p
  118. 3■■■
    1 J! }, D& m: h% y/ u3 c
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")/ q# t1 k9 ^; O0 t/ b

  120. , ^; v5 D  N) i' o. Z
  121. ;;; 棋盘
    3 i- E$ P4 x$ k/ X$ }# i
  122. (defconst *wechat-5x5-white-chess* 12288  T1 B$ g) t4 M% \4 e
  123.   " ")
      `/ O7 a- t, e+ e' [8 F
  124. . {) M" e- j+ N1 j: a
  125. (defconst *wechat-5x5-black-chess* 9632
    + X( E7 ^! |4 r7 X! f$ `/ B
  126.   "■")9 d& `3 Y$ ?9 ^+ D) `, U
  127. 9 r7 a& W+ k+ G) a) [
  128. (defmacro with-board (&rest body)" J& ~5 v8 \0 }# x7 u
  129.   `(with-temp-buffer
    % z& d$ b3 a" @
  130.      (unwind-protect7 x3 M* `# e, V, V" o
  131.          (progn
    0 {0 }0 T0 U# F, t0 M3 N0 u
  132.            (if (session "board")9 ~1 w$ m% N0 ]
  133.                (insert (session "board")))3 m4 z- B8 z  g) P  Y6 R+ E
  134.            ,@body)
    + ]- x. Z1 N2 G+ f8 x: ?: T; E
  135.        (session "board" (buffer-string)))))3 c' ?* g, z3 ]( J$ h

  136. + h7 I" S9 D2 G5 {/ a2 e  m8 q
  137. (defun board-init (size)
    ; x" R( E( ~$ F* z  i3 n
  138.   (session "size" size)
    8 Z, m- ~) D8 W9 u9 _
  139.   (session "step" 0)
      x: T' i6 s( y& p
  140.   (erase-buffer)
    " @6 ^5 H! ^0 I/ }2 }% E
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))! `4 _; g+ s! z) O5 o; B" r7 W
  142.   (dotimes (row size)* n1 _; j/ L1 b- h: H
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))7 U: L: s0 U2 a' {. ?0 U
  144. 8 a* O$ i0 p. w4 G
  145. (defun board-contains-p (y x). _5 F- f4 y% m7 Q
  146.   (let ((size (session "size")))
    * ^6 R3 |1 }3 q0 N
  147.     (and (<= 1 y) (<= y size)
    ; A9 L* X% b  a% O% H
  148.          (<= 1 x) (<= x size))))
    8 W  a, e3 X+ Z  f6 {& b. G
  149. / ~% [/ L) i# L& {: O
  150. (defun board-toggle (y x)
    * q% z) C* s+ a: `+ Y$ \
  151.   (when (board-contains-p y x)
    % p& c( Q% o) }8 J* \
  152.     (goto-line (1+ y))' o, |. y) m6 m3 y5 q7 b* k, U
  153.     (beginning-of-line)" s' U2 n1 g: ?& }; I' H
  154.     (forward-char x)" v4 x6 k" O' U" u
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    / m/ v8 c6 x+ |) L& p. G9 N) P5 `3 I
  156.                 *wechat-5x5-black-chess*$ I! Y2 W! q8 E3 t2 I# b0 |4 v
  157.               *wechat-5x5-white-chess*)), e7 C# c! O* f, y( I7 o
  158.     (delete-char 1)))) n. g4 P4 V' i. [
  159.   X& f/ M* H. {4 ^. l7 C9 e) O) K* C
  160. (defun board-put (y x)
    2 y, x: l4 Z3 D! J9 z: v" y  u0 N) y. v
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))  [8 N# M. G; O  ^  b* _+ g5 R$ T
  162.     (board-toggle (+ y (first dir))
    6 D$ F& R/ \; o
  163.                   (+ x (second dir)))))& E; f  I  h" c0 d
  164. . c7 d  ]# }5 ~0 r& @# d% u( C
  165. (defun game-over-p ()
    ( j( L/ S. a8 i" A" I8 s. Y
  166.   (beginning-of-buffer)/ e/ D! ~) X3 x/ @0 f
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))5 ^+ l) b' V6 T% Q) I, k& E5 Y

  168. 9 S- H: ~- n6 [8 }
  169. (defun board-show ()
    & {4 E3 i  P' q5 i2 Z, i
  170.   (with-board
    ' p3 H! I+ G/ I, d5 y* D' d
  171.    (concat (buffer-string)+ d9 \% Z, G1 ~& s8 J$ C6 T
  172.            (if (game-over-p)4 J& M8 r. u  U2 K1 z
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    ! m( b4 K" p. Z% t$ E
  174.             (format "第%d步" (1+ (session "step")))))))
    ' H2 x3 s; b6 A& I( A

  175. 5 _( x% y4 _4 h( S2 k# e* k
  176. (defun board-position-parse (cmd)# _2 b. Q+ {  K2 `" f& G; n& X
  177.   (if (= (length cmd) 2)! X* _/ B; R/ s7 P5 _4 {. V
  178.       (list (string-to-int (substring cmd 0 1))8 R% R! L! E0 @: e! y2 I$ n$ }5 l
  179.             (string-to-int (substring cmd 1 2)))2 P. E$ {- Y' d% ?- f
  180.     '(0 0)))" R# X- J5 s' }$ H

  181. ! I+ G" ^/ V( E' L
  182. ;;; 游戏房间
    + ]* ^3 A# \* j9 _9 ?2 I& r
  183. (defun game-room-init (cmd)7 y/ z6 W5 a2 r( M) d; Q
  184.   (let* ((middle (string-to-int cmd))2 u! W& q5 F3 K' m: L9 J
  185.          (size (1- (* 2 middle))))
    : j7 H) C1 y/ W- w% ^
  186.     (with-board2 f0 m! W8 H& s% P* j, }
  187.      (board-init size)& @! \/ J3 B% Y! A' ?
  188.      (board-put middle middle)))
    9 b) Y; V- z# Z  c
  189.   'game-room)+ Z; ~1 C9 I' ]. ?* j2 ^. P
  190. 1 _' {! {% [2 U3 Q6 X( q! Z0 v
  191. (def-room game-room
    - T0 w3 K! C" ^
  192.   #'board-show: s' h7 p. g7 O1 F) |( c( v/ O- L
  193.   (t (lambda (cmd)
    8 K( S$ W) b+ ^; m1 Z6 q  Y
  194.          (with-board. H/ z" R5 e8 I
  195.           (if (game-over-p)/ ]; ^0 y1 |0 L4 \+ G3 ?, _
  196.               'living-room
    * n/ n- X& Q5 e" z6 R: H
  197.             (destructuring-bind (y x) (board-position-parse cmd)# V7 c) J, X. t7 v& Y
  198.               (when (board-contains-p y x)
    ( ^7 Z" Z  L3 x* S" n2 _- i
  199.                 (board-toggle y x)
    % U4 B  x$ F. _
  200.                 (session "step" (1+ (session "step"))))
      V/ b7 T) z! ^! e
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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