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

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

  1. + F* ^/ n6 @% O- u
  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;">;; 定义新的游戏地图/ r- @* o! ~. [
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    - E" `5 z9 L/ Z7 b5 C' K" o
  4.   'tutorial-room-0)                     ; 默认的入口
    , a- x8 H3 y$ d

  5. ) n5 f- h  V4 {
  6. ;;; 游戏大厅- J" F, |0 Y; e9 q% T
  7. (def-room living-room
    % n3 B* k4 I! r5 d2 e* Z7 K
  8.   ;; 进入该房间后的提示语
    6 P/ Y+ n- r6 c. U7 l% K
  9.   "1. 教程3 q) K4 [5 \# v) I3 h4 D: L
  10. 2. 入门(3x3)* ^2 A$ x* M: r2 s' W) Z4 W& @
  11. 3. 初级(5x5)
    ; b' L7 f1 l) p& ]
  12. 4. 中级(7x7)
    - f+ O5 h2 B* g* d+ F* l) p
  13. 5. 高级(9x9)
    * D: ]/ A% v  Z& b1 ^
  14. 0. 关于作者
    0 T; R. I; ?) `3 x! p& h
  15. 请选择1-5开始新游戏:"8 X1 T* r: t) T: S+ t
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名" S0 n2 |6 X5 y5 X, b: E3 r  D
  17.   ("1" tutorial-room-0)6 d  k( J) N3 m( m
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配0 n3 [( A+ k$ F) K
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    / X6 Z; b  I0 C9 h0 C
  20.   (t living-room))                      ; 如果条件为t,为永真
    9 L* m& J4 n+ J, p8 N% ]& C6 [
  21. 5 H8 V/ l, r7 ?1 u3 y# Y
  22. ;;; 作者信息: R2 B  O! b& w5 M, r; E/ P/ b
  23. (def-room about-room
    ! J0 b( x* m  q  N3 J
  24.   "作者:redraiment
    / O+ n+ j$ [+ Z' x7 u
  25. 微博:http://weibo.com/redraiment
    6 Z! V" ]0 O3 S; Q. P% W
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    / K/ v/ @* U5 J" r" _
  27. 请输入任意数字返回游戏大厅。"$ }- I) _- A3 g2 }" [9 M0 [, R7 ~
  28.   (t living-room))
    4 f- C* v* Q: h7 m9 ~, U
  29. 2 O) t( V: m9 {7 A& x9 ]$ P, Z
  30. ;;; 教程
    ' p) X2 ~' A0 m; y- W+ I
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    & G( Y; A: \, [% V' D( {
  32.   "The number of tutorial rooms")- I* D1 q( J4 _1 X/ t$ T* D/ E/ L

  33. 3 Q% b' {5 B. z( Q! O2 H
  34. ;;; 简化教程的定义
    - k2 R1 @* I" N! J/ y. z% Y- x
  35. (defun string-last-line (content)
    ( p9 F4 D# y1 |# ~2 x
  36.   "多行内容组成的字符串中的最后一行"
    * y) _7 z2 I0 m
  37.   (with-temp-buffer. o  ?* [9 M: p" q9 b% ^# L
  38.     (insert content)7 q7 |" N$ F! S( d# P) p
  39.     (buffer-substring (line-beginning-position): m/ F( k. c9 |3 r" H. q0 `
  40.                       (point-max))))# E6 [* x! U: J0 l, I

  41. ! [! L7 v: z; C1 W
  42. (defun def-tutorial-room (prompt). S- M$ q7 d1 V+ n% |
  43.   "根据提示语自动生成教程房间。5 [% z" p3 P9 E; o( @+ d# R
  44. 0 h/ k( a* B8 z: z  G5 `" J. p
  45. 1. 提取最后一行作为问题;
    3 T5 T& R8 r. ~; e5 h) b
  46. 2. 分析问题,获取期望用户输入的内容;; e1 v3 R3 \) U3 }4 R1 z9 F
  47. 3. 定义教程房间和重复提问房间。"
    5 `( P/ [6 Y( p  ^0 b
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    : h& H- }8 @: s  Z
  49.          (repeat-room (concat room-name "-repeat"))% x# P" k9 {, U5 N
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))% j! Q0 Q2 T1 U) P
  51.          (question (string-last-line prompt))
    ) t" g- [! ^- n: K2 E9 `' J9 _
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)+ j. e4 O: e) b  r
  53.                      (match-string 1 question)))
    : _7 f0 D# s0 ^* Q
  54.          (doors (if except
    + t0 d  X& }, c
  55.                     `((,except ,(intern next-room))  t. t6 Y' |; Z  O9 t8 x
  56.                       ("q" living-room)
    ( d9 V; b* m0 C
  57.                       ("Q" living-room)
    ' p4 r' ^8 ?. p) ^
  58.                       (t ,(intern repeat-room)))5 B4 s" S& I* v  D( E* I% X+ L& }. ]* j
  59.                   '((t living-room)))))
    & Z( v& O! y2 B/ Y2 u
  60.     (def-room-raw (intern room-name) prompt doors)
    $ v- @8 p2 b# l* D
  61.     (def-room-raw (intern repeat-room) question doors)))
    ( o1 B1 e; Q6 n  O2 e
  62. , i  c" u3 H5 W/ v
  63. (defun def-tutorial (&rest prompts)- z/ c) V1 U# t& \
  64.   "批量生成教程房间。"- M! k7 w" E: [# b3 D: k
  65.   (dolist (prompt prompts)
    0 f/ y0 r; p% O4 L9 d! C
  66.     (def-tutorial-room prompt)))
    ( T/ G3 q: n" @) n( @/ `

  67. ' i" b) N, Z' K) ]% q/ ~1 N
  68. (def-tutorial
    % d- ]" b; }/ i  D3 x) p
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。' Z3 e! M! I& @  G  u% P
  70. 1. 教程
    1 {; U$ n0 I- B! u
  71. 2. 入门(3x3)) p0 |6 R* @# a2 H* g
  72. 3. 初级(5x5)
    . G/ v7 ^. \/ \) }2 b
  73. 4. 中级(7x7). ^' o5 ]. R. q6 e7 X4 _6 s
  74. 5. 高级(9x9)
    7 ]8 i! V! ]) G; f. j9 Q- x
  75. 0. 关于作者4 Z! T- w0 h; s* c5 u7 x% z
  76. 请选择1-5开始新游戏:% V* j  @* }# ?/ }* R3 N
  77. 您现在正在游戏大厅里。% }& n- V, N; K" w" d9 {7 w
  78. 请输入“2”进入入门级房间"
    & h6 m% B4 M( i! p8 `* t
  79.   "  ①②③
    * X) s- h+ [; @6 P( e+ Q4 M. a6 ^
  80. 1 ■ ; ]; u) S& x! }; T0 a
  81. 2■■■% w0 c4 e$ E9 U8 W6 s1 T5 C" G$ q
  82. 3 ■ - i( s* ~) R1 }/ j8 w. o
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!4 Z0 W' n# j: |( ?1 u9 T: s
  84. 请输入“22”来关闭第2行第2列的窗户。"
    , A8 b  ^' ]6 E/ T+ S; W# p9 `9 {3 [
  85.   "  ①②③
    ( ]8 P: }: K4 }/ y
  86. 1   : I4 g1 `0 g5 {# y6 ?5 D
  87. 2   1 E# S2 _/ q3 a* }( |1 i4 r6 F
  88. 3   
    ' D/ u, P# U- w: s9 S9 H8 E
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    , ?# d6 Z7 n0 n2 @; _
  90. 请输入“11”,试着开启左上角的窗户。"9 j. }! P: l8 J  N( a
  91.   "  ①②③
    . B; [; x+ P$ A" _( k! @8 D6 q
  92. 1■■ ; Z* Y" ^& q' v
  93. 2■  
    , Z2 B$ X% T- m' b$ Q( P" k  p
  94. 3   
      v/ E2 x9 v4 S8 Y
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。8 x6 C: r1 t8 W8 y; _
  96. 请输入“13”开启右上角的窗户。"
    4 I# }1 f; W7 F! p6 K
  97.   "  ①②③
    % O7 n9 z$ A6 R2 q
  98. 1■ ■
      k" o& H- j) w
  99. 2■ ■
    6 @9 T' Q# }4 B+ v, Z' q
  100. 3   6 h# F: g* n9 J  q1 q& L$ P& Q
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
      G$ ?2 L! z$ n! [* i8 M
  102. 请输入“31”开启左下角的窗户。"
    4 z) O$ |0 |+ D, _* J; n
  103.   "  ①②③
    ) j# V! S7 v' g: ?
  104. 1■ ■4 a2 Q4 u/ K: m; Q! `
  105. 2  ■
    9 ?5 R+ l: l0 i7 O) u
  106. 3■■ * r4 V  W' r! c# \1 J
  107. 此时,总共有5扇窗户被开启了。
    + M# M8 z# I4 |
  108. 请输入“33”开启右下角的窗户。"# h5 B8 u& N/ e6 D
  109.   "  ①②③) j2 r+ K  e, g! u
  110. 1■ ■( G$ V# d4 c- N$ z9 ?' J' g6 G" W: G4 I
  111. 2   2 y! V/ r) o5 Z) {7 I9 Q
  112. 3■ ■* s8 x: P0 |3 p4 k* A
  113. 现在,只有四个角落的窗户被打开。1 n4 f. h4 b9 Q5 ], \
  114. 请输入“22”完成最后一击!"
    1 n8 i/ d4 ?  V8 o3 u/ M' z! ?
  115.   "  ①②③5 F1 I0 S/ y4 p) R/ y5 a
  116. 1■■■
      h) |' y* w4 j/ d; U
  117. 2■■■5 Y" ?0 N9 l+ J0 {
  118. 3■■■
    1 s7 R1 ]. [9 v% N" ^0 c7 [* i+ ~
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")& v7 v! C8 O6 L
  120. 3 L: ?  s2 N- T$ T- V
  121. ;;; 棋盘( f6 _6 d) L( R3 a! w* g" O
  122. (defconst *wechat-5x5-white-chess* 12288/ [+ r# L" D0 Y- q
  123.   " ")
    " X) ?; T' O! ^5 I1 j8 b* `" P

  124. / L1 X5 L8 z4 k+ m4 s& E0 Z9 W
  125. (defconst *wechat-5x5-black-chess* 9632; `! k9 ]8 l8 D/ }4 K
  126.   "■")
    # t& T: N7 \( q

  127.   W1 ^+ j: R: ^; e* X/ ~0 F" H* F
  128. (defmacro with-board (&rest body)
    * v- C: E: l6 ]8 T) P
  129.   `(with-temp-buffer1 q4 Y6 L$ U0 L. d
  130.      (unwind-protect
    9 @, A7 S6 G% l$ G- F/ C
  131.          (progn5 s# i5 J* N- \6 k, A: H
  132.            (if (session "board")
    . D! w8 A& V" _
  133.                (insert (session "board")))
    / e8 l# C. J7 w) }
  134.            ,@body). L! D9 R8 }5 J3 p  }
  135.        (session "board" (buffer-string)))))
    2 R" j) D5 U6 j3 Z; }  o

  136. # P* E( M( e4 q( K$ u) |) U7 O
  137. (defun board-init (size)
    * |/ @) r5 g$ ^) c/ I) L2 O- p. \
  138.   (session "size" size)% f% I4 Y: J' F9 y; J% H
  139.   (session "step" 0)
    8 H: q1 H) Y' e' V  i9 o  E
  140.   (erase-buffer)
    & J" L3 H6 n$ Y1 R3 @9 Y$ W
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))- {- b9 u% L0 J7 S- l
  142.   (dotimes (row size)/ x$ e2 x1 m/ f
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))* P+ q; S8 D. o( H; y0 r$ R
  144. 6 S, V" g5 }5 p. }: u  j
  145. (defun board-contains-p (y x)
    * @+ N. V- ^( o. w% N! N, i( O0 o
  146.   (let ((size (session "size")))2 n  ^& N) s) S* p# V& ]
  147.     (and (<= 1 y) (<= y size)
    # P: s, O8 c1 \& b
  148.          (<= 1 x) (<= x size))))
    " C5 l+ h- c# n6 W

  149. - `. E! w- L7 C1 k, n" Y
  150. (defun board-toggle (y x)! ~7 @& v2 T# K1 G+ U: D! `
  151.   (when (board-contains-p y x)
    4 a' w' T  z: E% h7 D
  152.     (goto-line (1+ y))
    / ~. j# F' e0 G
  153.     (beginning-of-line)4 j& y! `' e$ t
  154.     (forward-char x)
    " t9 X+ s3 E9 I( h
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    " L9 ]+ G8 O; l' T' z# m/ S- t
  156.                 *wechat-5x5-black-chess*
    ( S2 g! |3 n- X: z8 t
  157.               *wechat-5x5-white-chess*))
    & z1 e/ ~# W9 \  h4 a6 U" h
  158.     (delete-char 1)))
    " W0 r" x! ]: q( j' k' o% I
  159. ; l5 p# e) ]$ [) A( N# ^
  160. (defun board-put (y x)
    * r0 ]3 H$ G( z: \/ u
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))5 ^( {* p1 R( r/ B1 E6 p' @3 X- }
  162.     (board-toggle (+ y (first dir))8 a" B  M/ B' `
  163.                   (+ x (second dir)))))
    " B7 i/ M4 E9 `+ b

  164. 8 t2 [" o3 Z7 c# p4 p1 @; l
  165. (defun game-over-p ()6 F: ~% Z/ e9 F  @5 D! ]" R/ o
  166.   (beginning-of-buffer)
    6 D: l9 s, h' J6 d. v2 p* m7 M
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t))), X. b, {# y3 ^! V' c

  168. ( [5 i0 m' W2 z/ a' h# {
  169. (defun board-show ()8 [# f; R& M0 f5 G1 X
  170.   (with-board. f6 ^& S' l7 m$ P% O  x* `
  171.    (concat (buffer-string)
    ( \$ C# k8 d5 @" y- o0 b7 \
  172.            (if (game-over-p)
    - D8 F4 @# u: e2 x' F9 i
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))/ o* V4 M8 P; t8 |7 ], ^
  174.             (format "第%d步" (1+ (session "step")))))))0 Q- g, j$ [+ L) u
  175. 0 u( k5 `' e' X- E( m
  176. (defun board-position-parse (cmd)& S! @3 w; h8 c9 H
  177.   (if (= (length cmd) 2)
    . J8 r* F; i* R* m% t
  178.       (list (string-to-int (substring cmd 0 1))+ I  V# Q1 T! e% ^* x4 \' Z+ J# G+ x
  179.             (string-to-int (substring cmd 1 2)))
    5 S) J1 f3 z* M6 b( W2 w2 Z
  180.     '(0 0)))
    & Y; f# I" L1 u1 T. d% \

  181. . v# n8 E% J- G! }  e6 J
  182. ;;; 游戏房间
    ! W4 J  E7 H  X3 l
  183. (defun game-room-init (cmd)% a* i4 X. S# ^0 z9 |: w
  184.   (let* ((middle (string-to-int cmd))
    ' d- X- }7 h! l& u2 i+ P4 ]
  185.          (size (1- (* 2 middle))))
    3 |, ]- U1 B  m' I5 m
  186.     (with-board
    ( v7 [* q; t" _/ l* }$ t1 A
  187.      (board-init size)+ S) N1 v5 z! t
  188.      (board-put middle middle)))  n* {2 C8 J, o1 g! Q  B
  189.   'game-room)
    2 e; z6 D# G  {3 ]  M" e
  190. 6 z' G$ f" a3 l  t
  191. (def-room game-room
      S  e1 C8 A1 \+ Z" P
  192.   #'board-show$ E5 d( G9 U# v  ^! |
  193.   (t (lambda (cmd)5 t5 T3 I. [$ T( k8 M' S  T/ y
  194.          (with-board
    ' S  H, W( V( ]" V( x
  195.           (if (game-over-p)
    * p' c# g: V$ {- P1 U
  196.               'living-room
    % I* {1 ^5 A, `3 @3 j
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    # D) j& W! e" A' X: b. \1 S
  198.               (when (board-contains-p y x)3 l0 c, g9 K' j
  199.                 (board-toggle y x)
    . ?# b* I# L$ z7 M- L
  200.                 (session "step" (1+ (session "step"))))/ n' j* @% E0 N/ L
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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