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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
6 k$ c8 ?) @% L, [# g! n  [( B- S借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1.   [- L. J2 [' l/ Z- R7 M
  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;">;; 定义新的游戏地图
    - z+ o0 ?) ~6 u3 [# g% A
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL0 W1 R6 u  Q/ r7 n. Q/ @- N
  4.   'tutorial-room-0)                     ; 默认的入口
      c9 z: w  _6 z  U
  5. $ P) U( h! v: y/ N* _4 d) ~
  6. ;;; 游戏大厅
    & E1 c$ h3 N% W: m& n4 E5 c
  7. (def-room living-room' m$ }( z5 [* _* H. _
  8.   ;; 进入该房间后的提示语! X2 _8 Y& Z: z
  9.   "1. 教程
    ( v& e# h: H( f' R9 x$ `
  10. 2. 入门(3x3)
    2 @- Y' \8 L  a  H
  11. 3. 初级(5x5): J- {+ K- f) Y- H+ U
  12. 4. 中级(7x7)7 D6 r) l' A3 U+ z4 v" n, q! z
  13. 5. 高级(9x9)
    6 ~) u8 V6 r2 u' u
  14. 0. 关于作者6 X+ d! z) Z$ |
  15. 请选择1-5开始新游戏:"
    / R# S2 e- h) v! i: e
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名( K: r6 T. J$ M7 ]6 c7 F# X
  17.   ("1" tutorial-room-0)8 d8 _: s( Y1 E
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    ' H  N( s* M1 r8 w8 R
  19.                                         ; 相应的返回也可以为函数,动态返回房间名! w  X% k9 L3 A
  20.   (t living-room))                      ; 如果条件为t,为永真
    2 C0 j1 R# d0 y+ J

  21. 6 [. N" _, b7 K# z$ W" |5 M4 X
  22. ;;; 作者信息
    # H- N  M* |$ X7 q: L" M
  23. (def-room about-room' G4 t4 ~1 x  g
  24.   "作者:redraiment" \3 |/ b: a- B5 w  {) T2 a9 h) O
  25. 微博:http://weibo.com/redraiment
    # M6 W# x6 O0 Z/ J7 m9 f7 H5 F
  26. 有任何建议,欢迎在微博或微信上联系redraiment。: A% f$ c, B; G: f
  27. 请输入任意数字返回游戏大厅。"  W: l8 n2 Y7 v5 V) m1 k& ^
  28.   (t living-room))
    ' F( Q- b" h" b
  29. / I$ T* {6 d* ]* Q; _& c
  30. ;;; 教程: G3 ~; W0 Y3 H5 {; T* w/ u
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    ; p3 ^8 y* [+ L
  32.   "The number of tutorial rooms")
      v) y+ E: Z* U- @

  33. & Y0 X. @6 D$ I" X9 [0 Y
  34. ;;; 简化教程的定义
    6 d) q2 y- p5 T) b
  35. (defun string-last-line (content)
    2 {: O/ V& a) z: r' N
  36.   "多行内容组成的字符串中的最后一行"  T0 z4 q9 @8 V8 T- v' e& x- E
  37.   (with-temp-buffer
    : G- m. T0 M( M
  38.     (insert content)' ]* I9 l  K0 ~- _
  39.     (buffer-substring (line-beginning-position)
    " t: N5 m, w) G6 e, `
  40.                       (point-max))))
    5 M9 I3 u% x1 }, {" `* W. X

  41. 5 }: N( M/ E: y& ^* u# b! b7 `! M
  42. (defun def-tutorial-room (prompt)
    1 [: E- V/ e# s" N
  43.   "根据提示语自动生成教程房间。- R5 i* |: y4 ^* i  y* \8 C$ f
  44. ; A' a# e+ S1 o7 y4 w: ~; D
  45. 1. 提取最后一行作为问题;# @; O4 u0 N8 f9 U- K
  46. 2. 分析问题,获取期望用户输入的内容;
    + `. F$ K4 H) D
  47. 3. 定义教程房间和重复提问房间。"
    9 I3 {% L1 b! B, _! o; H# S
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))! [5 R" ]7 S5 g0 q3 f! C4 `
  49.          (repeat-room (concat room-name "-repeat"))
    0 d! N+ l* F; C4 ?' B2 P
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    * U% z, b+ |9 I6 B" }6 x
  51.          (question (string-last-line prompt))4 c0 N* c* w8 H8 A9 T
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    ( T# j) T' {* C( ?/ ?7 n
  53.                      (match-string 1 question)))' D! |; K* B$ `$ b% ~4 M" Z3 x
  54.          (doors (if except' h. `% m$ I( ]+ o
  55.                     `((,except ,(intern next-room)): X; j4 O& u. q: m7 D$ a  P
  56.                       ("q" living-room)
    & e6 Z. b9 X/ D
  57.                       ("Q" living-room)
    & N, n" y6 `( Q) G) t) B
  58.                       (t ,(intern repeat-room)))
    * g; V2 `+ `0 e/ r
  59.                   '((t living-room)))))
    % M- f8 ?* C/ d# x
  60.     (def-room-raw (intern room-name) prompt doors)' b& ?8 q1 {" N+ B3 K4 p
  61.     (def-room-raw (intern repeat-room) question doors)))
    9 M0 ^! s- u: T7 }5 |- N

  62. 2 b- c2 X  K/ e. }
  63. (defun def-tutorial (&rest prompts)) m! }2 |( J2 T% w! _2 G- T1 V
  64.   "批量生成教程房间。"
    2 z2 D" X' W+ u+ ~+ ^. H
  65.   (dolist (prompt prompts)
    , q1 `  x2 H& v* J+ q5 G
  66.     (def-tutorial-room prompt)))
    + _# r1 t: \7 M  a# p3 p0 h

  67. 6 J+ m% m! a; ]" r+ A
  68. (def-tutorial  }  n7 i7 \* s
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    ' O& a1 q$ @  ^
  70. 1. 教程( R& ~6 `9 y0 S* Z/ R5 O. R' L  V
  71. 2. 入门(3x3)
    6 ~2 S4 s" F' C% v( p5 U# t
  72. 3. 初级(5x5)/ p) w3 k. `  h- B9 b) N
  73. 4. 中级(7x7)
    ! C% x$ z; d3 _& H/ h8 n  g
  74. 5. 高级(9x9)
    / ^. m& ]+ C' o3 J7 f3 s
  75. 0. 关于作者3 v- J' h' m& w5 O* l$ B
  76. 请选择1-5开始新游戏:/ }2 F/ }( ]- H
  77. 您现在正在游戏大厅里。; R* F2 s3 p/ W* m( g: q" r- J
  78. 请输入“2”进入入门级房间"
    6 l  K5 t' Q- e
  79.   "  ①②③2 H/ _( ^4 O- K' @( x0 T# F
  80. 1 ■ ) O* A" Z& m! n+ J& }" a) P! z- H
  81. 2■■■/ x9 Q  f5 G, Q4 y- V  R; ~) B
  82. 3 ■ : L$ @  H3 o& `
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    ; D' o0 i4 J, ]% l5 ~, i2 G2 R$ q
  84. 请输入“22”来关闭第2行第2列的窗户。"
    1 H. d9 `* z2 b8 K
  85.   "  ①②③
    8 @$ J3 a5 r2 I1 x1 L9 `0 f
  86. 1   
    5 e4 N9 B% w9 h
  87. 2   ( }5 S  D7 m1 F/ h$ ^0 j% e
  88. 3   
    0 G6 J( `) B# d$ d1 C8 [% [
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    6 j) d; H" w: F  ^, E
  90. 请输入“11”,试着开启左上角的窗户。"8 p* w2 _( R' a- M- w
  91.   "  ①②③
      B* m* U, `6 [( e. M
  92. 1■■ 9 Z6 u, d/ R/ l5 M" q% O" F
  93. 2■  0 z2 s, W6 m1 G- w' m
  94. 3   
    " R2 t6 m: H& T* i" a4 n
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。- K1 k6 b$ l! o  y! k7 ]* q
  96. 请输入“13”开启右上角的窗户。"6 e# s/ g7 M9 @* S  g9 N
  97.   "  ①②③
    ; C7 E- S0 g( ~5 J  x, j4 v
  98. 1■ ■& Q# S! V8 B; H. v
  99. 2■ ■# g' ]2 K& w0 X3 g
  100. 3   
    $ I. d3 A- R7 q: ?$ A0 _! G
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。% h9 N* k+ e; y$ g6 q2 A+ A' P
  102. 请输入“31”开启左下角的窗户。"( q% z& n; k) H) ~7 T' z' M
  103.   "  ①②③
    , J/ G  r  J" O+ X
  104. 1■ ■
    3 y/ [; a+ _: W2 g! u' ^" u9 T
  105. 2  ■) X0 Q; C/ a5 h% J6 p9 s- e
  106. 3■■ . `% O, j) _+ l* ^
  107. 此时,总共有5扇窗户被开启了。8 N) m0 a6 c+ H) g
  108. 请输入“33”开启右下角的窗户。"
    % A( a* Z& p) _0 L% E6 Y7 ?
  109.   "  ①②③2 Y' y9 ]8 j/ w! Q& X! B
  110. 1■ ■
    ' v$ j+ `7 L  |( A, ~
  111. 2   2 Z  c3 F2 o0 H( ?$ L4 f# a% f
  112. 3■ ■# U% _# a. s& x. [  g% f7 v9 m# X& k
  113. 现在,只有四个角落的窗户被打开。0 O* Y' R3 Q& A" Y' m
  114. 请输入“22”完成最后一击!"
    * [0 u9 o/ n. A/ ~
  115.   "  ①②③/ L5 o# s! l. Q8 ?/ N  B
  116. 1■■■$ ?7 \1 K, U) U) i8 }- i
  117. 2■■■% x/ i* V  y2 e+ X. H3 y4 H) o2 t
  118. 3■■■
    4 G8 t+ \2 O* q' S( g# m
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")" g6 g. U9 Y  V- \0 Q3 i

  120. $ s6 O. c, f8 ^
  121. ;;; 棋盘9 ^  @. L0 |3 h% T4 I4 u
  122. (defconst *wechat-5x5-white-chess* 12288
    2 H  w1 @$ p* o: S. Q
  123.   " ")
    % }% I4 Y+ |( d

  124. 4 M1 p; m1 \" p
  125. (defconst *wechat-5x5-black-chess* 9632
    2 ^0 j4 A6 x) x# P
  126.   "■")4 f6 v  z5 g$ c8 e  S1 Y

  127. - r1 q! S& b8 s
  128. (defmacro with-board (&rest body)5 v0 O3 j9 l+ r; K& D; l. S# F
  129.   `(with-temp-buffer
    , ?( l3 I1 j# b$ ^8 H
  130.      (unwind-protect
    - X9 q+ |$ p+ K1 l1 Z) L* A$ S# W
  131.          (progn
    # x. E2 p/ g# X- H) n% `
  132.            (if (session "board")4 O/ M6 z% ]0 X0 t+ ^# w
  133.                (insert (session "board")))
    9 ]! ~( h3 x- r) B' q8 E6 ~, ~
  134.            ,@body)
    7 ]+ c+ p- p) a4 y$ g7 O) E
  135.        (session "board" (buffer-string)))))
    2 Z* c( @9 D/ I2 J" x! E

  136. 4 ?6 k0 x3 }7 `# ]
  137. (defun board-init (size)' Q% Z4 I4 L3 y, V" F
  138.   (session "size" size)8 T6 X. a/ U# g9 F4 t3 `
  139.   (session "step" 0)
      j8 i  c# p! X  a5 P1 v0 V- r
  140.   (erase-buffer)5 W7 {. V: C4 [3 D8 M4 o) M
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))3 m3 S; H1 M4 V: ~6 K9 C
  142.   (dotimes (row size)
    # c1 J/ c0 o. N. C$ L: s/ [
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))- D  q$ ~; j9 `) f
  144. 4 y) b3 ?+ O# c4 {1 r
  145. (defun board-contains-p (y x)9 \) s* k: h$ v/ @
  146.   (let ((size (session "size")))
    1 {! [& `, R; \& G) X
  147.     (and (<= 1 y) (<= y size)! g+ [! D- ]) Z5 g4 ~
  148.          (<= 1 x) (<= x size))))% u' s' W/ ]- N4 }

  149. 9 O* j- j+ a4 P$ Z# ^4 t) Y9 K( ~4 z
  150. (defun board-toggle (y x)
    2 j( O! c3 ~: `; x1 Z' a! h/ Z
  151.   (when (board-contains-p y x)! t" x8 @$ F9 F9 U2 @
  152.     (goto-line (1+ y))1 v0 H8 _5 j9 [% i" }. ]
  153.     (beginning-of-line)
    6 [+ N/ \2 K- I- E" f
  154.     (forward-char x)4 S+ n9 A/ A  c
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))+ k, N# `/ H3 t
  156.                 *wechat-5x5-black-chess*
    0 J1 K1 Y6 n3 O3 w
  157.               *wechat-5x5-white-chess*))
    ( T% A8 O+ O* Z( \- f+ y8 Y1 Q
  158.     (delete-char 1)))
    # M$ k; P% L( q! M0 f
  159. : n* }  s! y- T$ I
  160. (defun board-put (y x): ]) f- t& n; G% G* H
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    : }, m. b" t) E5 G) D- E
  162.     (board-toggle (+ y (first dir))$ O5 w8 w: q% w/ h
  163.                   (+ x (second dir)))))
    5 V3 ]0 L9 @3 L+ j$ M* q4 n! P
  164. ' N  U# u) H0 E
  165. (defun game-over-p ()
    0 g' A" j) s8 }' y" Q# b7 _9 S* |
  166.   (beginning-of-buffer)
    * z$ b! q; o% c7 O! F
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))0 F( R6 [, g. L! I1 l4 L

  168. 2 \! t- o/ P4 I1 j9 N* f
  169. (defun board-show ()
    9 P- o5 I8 u: R/ z4 ~2 Z
  170.   (with-board+ ?. A/ t0 g# M3 |4 X, }+ R
  171.    (concat (buffer-string)
    " J: Y# E  B+ s( G
  172.            (if (game-over-p)4 |% N; {8 |- _- a; A/ x
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))% D9 R9 d( {9 ?' O" c& v2 R9 k
  174.             (format "第%d步" (1+ (session "step"))))))): }! z& S" T9 d7 B

  175. 5 T0 v, F0 S9 c! e) e
  176. (defun board-position-parse (cmd)
    0 e! P! ^8 [$ j3 D5 g2 G
  177.   (if (= (length cmd) 2)
      |$ |' l9 s  J' F: v6 i$ k' Q
  178.       (list (string-to-int (substring cmd 0 1))
    ( T+ L* G7 `) s# w3 z4 b
  179.             (string-to-int (substring cmd 1 2)))( V& u: x4 [1 F- `# i1 M4 ^8 U
  180.     '(0 0)))
    1 H9 J0 p" _! R

  181. ! H8 W2 k  q! V7 u3 k$ V, t
  182. ;;; 游戏房间( c3 F9 d$ y9 w% s/ {9 c: p
  183. (defun game-room-init (cmd)1 h5 o% e1 b6 e
  184.   (let* ((middle (string-to-int cmd))+ G3 `" L7 j, j) }; _8 [
  185.          (size (1- (* 2 middle))))
    8 n* L4 X- o: @1 ~
  186.     (with-board
    ' X1 D' K" W) `9 F9 _
  187.      (board-init size)* X4 L% c+ _. O  n2 u# f
  188.      (board-put middle middle)))' r; H4 g+ K- E- I0 V
  189.   'game-room)
    & |* N2 z8 K" I
  190. 0 }7 |/ M* |! o/ D/ I
  191. (def-room game-room
    % V2 T( J) n0 w5 |3 J6 E& N6 w  V
  192.   #'board-show5 g$ N8 t" \- ~0 g) c3 y. {
  193.   (t (lambda (cmd)! O1 _" A  p% \9 ~% L% [; t2 K
  194.          (with-board
    8 Y9 e& c0 H' M" V/ E$ c0 Z
  195.           (if (game-over-p)5 I- p3 _9 w3 ?
  196.               'living-room
    * o' l: g' W9 Y) s5 ]
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    0 G  f/ ?+ K1 Z3 ]2 ?; R6 V
  198.               (when (board-contains-p y x)
    # t5 y# K& e9 F. G8 B
  199.                 (board-toggle y x)
    1 V8 K5 e+ B4 i8 G+ q# w
  200.                 (session "step" (1+ (session "step"))))( r0 C4 t7 g( J
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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