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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
* Q" A: }; J5 }; g借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. 6 J1 S1 i! O& K/ D4 L
  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;">;; 定义新的游戏地图. d* m: g& R2 b8 Q4 R
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    7 k7 _+ z7 M# @4 c: k
  4.   'tutorial-room-0)                     ; 默认的入口3 `! u- g$ |& Y3 c) U

  5. & Z0 k6 g! O0 T* L2 u9 Z
  6. ;;; 游戏大厅# \) f: P( p  a3 ~
  7. (def-room living-room
    5 P& |3 I5 l; T6 V# ~  V, D9 C( K
  8.   ;; 进入该房间后的提示语
    $ j3 I/ j: U) s: ]7 }2 e
  9.   "1. 教程
    7 e6 M% n3 ?0 m2 W7 o2 h& ~
  10. 2. 入门(3x3)
    7 I! G6 I- j, P& Z& ]$ H; [$ D
  11. 3. 初级(5x5)
    1 O0 ~" z$ V* D, T, V7 t# `' h
  12. 4. 中级(7x7)
    1 P. W5 ^& M, w2 c1 @' C
  13. 5. 高级(9x9)% V. ^& I9 w9 P* q8 F
  14. 0. 关于作者
    " h) u' ?! ]3 Z( P' l
  15. 请选择1-5开始新游戏:", ~# W- k. O# A& z* I% r) x, R1 ~0 E
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
      Q- X, O& c7 d5 j# t
  17.   ("1" tutorial-room-0). H# \; j% Y- P6 }
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配% _0 Q$ f! O8 \
  19.                                         ; 相应的返回也可以为函数,动态返回房间名, C5 j: g- Q. m% h* z
  20.   (t living-room))                      ; 如果条件为t,为永真
    ( {/ ]. H; Z1 I0 Y6 [8 \; w
  21. 8 D( J  p* g0 K! ]3 s
  22. ;;; 作者信息
    ( K( l, D, t8 i5 p& y% {
  23. (def-room about-room
    4 E* h% H2 Y" d" @  R. q0 x
  24.   "作者:redraiment
    7 J6 k6 q( x, U: q0 R8 {* Y( e
  25. 微博:http://weibo.com/redraiment. y& U9 D6 S, B$ Q% q, \
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    ' @3 {8 `+ n& g5 a( E: O) k
  27. 请输入任意数字返回游戏大厅。"
    5 R& q! {4 k% z( y; D2 d' n
  28.   (t living-room))& V; K' G. d; Q* z

  29. : N8 Z7 U9 S+ a+ r: y" f& y/ Y) z
  30. ;;; 教程
    ' x& }8 H4 n6 ?/ @8 Z$ Z/ s! b
  31. (defvar *wechat-5x5-tutorial-rooms* 0* t; e, k7 P; p# B$ E+ F
  32.   "The number of tutorial rooms")$ O7 p) m7 t9 F( A. }" X; A

  33. + P  E/ Z. h3 t2 \9 ?
  34. ;;; 简化教程的定义1 B" \1 M. [9 q" I0 C& n
  35. (defun string-last-line (content)( E7 g7 f! P9 H& G
  36.   "多行内容组成的字符串中的最后一行") S9 k5 o0 V9 J) B$ S
  37.   (with-temp-buffer* j1 @6 t& b7 N0 O1 D
  38.     (insert content)
    7 i' V6 F* h/ J, F$ }
  39.     (buffer-substring (line-beginning-position)
      {+ }' R/ u' l) L4 p
  40.                       (point-max))))5 E) u, K+ D6 h) E3 w/ x3 x2 _
  41. 1 V; P1 n: |/ `
  42. (defun def-tutorial-room (prompt)
    $ |/ G4 B( C# {$ P
  43.   "根据提示语自动生成教程房间。
    2 ], S: G  O0 l, |( D$ |5 m/ a

  44. : H# @/ S) |2 [  M2 Y) M" j" ?
  45. 1. 提取最后一行作为问题;* h9 @; n( ^7 }$ u& ]$ ]
  46. 2. 分析问题,获取期望用户输入的内容;0 E- f! z9 |7 R8 \( P  h* S% k6 E& {
  47. 3. 定义教程房间和重复提问房间。"9 [2 y3 ?( u8 A  [4 y/ L4 y
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))( Z1 I' I0 Z1 w' b4 v$ b( D; e6 W3 R
  49.          (repeat-room (concat room-name "-repeat"))
    # o6 ?5 O( p+ r+ Z$ S, }
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))* J9 z4 C& ]. m- W5 |' x2 S; Q
  51.          (question (string-last-line prompt))
      n3 U" M3 y. P3 B7 T6 g6 K) O
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)" O# c' \* a9 Z
  53.                      (match-string 1 question)))0 A5 P- _8 e* k; y: g
  54.          (doors (if except; d* m) ^$ V. ]) h( J
  55.                     `((,except ,(intern next-room))9 X. K0 k' A" ~" W. `  q8 V
  56.                       ("q" living-room)8 u& ^5 u/ T7 W# L# I3 c
  57.                       ("Q" living-room)5 L( i, g1 l! d# |
  58.                       (t ,(intern repeat-room)))
    ( t% f* l" e3 j4 z
  59.                   '((t living-room)))))8 u3 x& S( I0 g, [% ^6 R8 r
  60.     (def-room-raw (intern room-name) prompt doors)
    ( k6 [' ]4 ^) a% m5 f5 `: I
  61.     (def-room-raw (intern repeat-room) question doors)))
    / m( R" w* x  h* A7 P
  62. 5 v  D4 L0 q8 L5 V9 y" V$ A% o9 S
  63. (defun def-tutorial (&rest prompts)9 _9 [' z! [3 o6 I  e
  64.   "批量生成教程房间。"0 n/ n: t% K( I& @; @* h
  65.   (dolist (prompt prompts)
    - ~- D  [; j: L4 S, a2 O8 O6 d
  66.     (def-tutorial-room prompt)))4 _' F8 r; }/ y1 ?) I, l8 N
  67. 7 g! ]+ j7 k, d. ]- F  e
  68. (def-tutorial( o7 r# l, o% m- j
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    * H' i  W+ z+ L* h  n0 F! n
  70. 1. 教程
    8 O2 v' I% O4 C3 e' x% F' f
  71. 2. 入门(3x3)
    ! |/ y5 Z3 H. d9 q/ P5 A( J7 s
  72. 3. 初级(5x5)
    ! g( i7 m& [% _2 z
  73. 4. 中级(7x7)5 T# B3 P- }: `/ D
  74. 5. 高级(9x9)5 c, c: A0 h4 u3 w1 J5 e* s
  75. 0. 关于作者; F3 E4 D; z# _4 f/ A& G3 S
  76. 请选择1-5开始新游戏:" U* d6 N# _5 q0 S
  77. 您现在正在游戏大厅里。+ Y7 ?% N7 e. f: G; D
  78. 请输入“2”进入入门级房间"
    8 W$ _( x0 j6 w
  79.   "  ①②③
    1 p: Z( u8 L; [
  80. 1 ■ 
    9 B8 f+ ], @9 S: T4 `+ t4 m
  81. 2■■■
      b$ [/ W3 }9 f# U; y% O' P; g1 V: q
  82. 3 ■ 
    * a0 l1 G& E# ^8 t9 q! _. i
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!4 I9 n2 {* n3 t' b
  84. 请输入“22”来关闭第2行第2列的窗户。"
    1 [+ c$ ~8 m3 p/ y
  85.   "  ①②③: e; C* v( x8 @! R
  86. 1   0 z& X6 H4 U9 [+ M( H
  87. 2   
    , s6 s4 b+ O1 z: a5 ?+ _
  88. 3   
    " D7 j; K4 X0 c8 `
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    # ^3 a: F& Z( |# U  ]; B8 [
  90. 请输入“11”,试着开启左上角的窗户。"! m  {- U$ y8 U- W0 N. x
  91.   "  ①②③7 m; S8 F1 p& o8 `
  92. 1■■ 
    # H: n+ C  \5 L
  93. 2■  
    ' R; N. _  N5 X& ^2 i" n; x# D; ?
  94. 3   
    * `* C/ ]3 J3 G$ {1 E
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    8 x: w' z. S9 v
  96. 请输入“13”开启右上角的窗户。"
    / s& b( B% _$ I( |3 ?
  97.   "  ①②③) P) d+ v: h( u3 Y& r( V
  98. 1■ ■* K1 b4 E. I7 d5 L$ Q
  99. 2■ ■
    ' s$ P* @& @$ Q
  100. 3   
    4 N7 e, p, o: f1 }- f
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。8 A7 X! P5 h9 Q; J0 l# k
  102. 请输入“31”开启左下角的窗户。"
    : o! v, Y- }8 [
  103.   "  ①②③6 r* U$ g5 f5 |2 n
  104. 1■ ■( v4 [; v/ D; q2 W+ y" q! c
  105. 2  ■
    # I6 l' m  w0 g0 J
  106. 3■■ % j5 a/ ?& q( z3 u, E. x
  107. 此时,总共有5扇窗户被开启了。
    & P$ h3 }& Q& b$ w# e) T
  108. 请输入“33”开启右下角的窗户。": j$ `, t6 V2 P
  109.   "  ①②③, d' ^, j9 y: s& t$ x) x
  110. 1■ ■
    2 I  Q/ O2 a% u& g& f
  111. 2   
    & @% l+ ~/ N. V: D& E9 ^& A9 W
  112. 3■ ■
    / |5 D" ^3 `' K) Y% Y
  113. 现在,只有四个角落的窗户被打开。! Y6 F; a" s& Y' A: y4 z
  114. 请输入“22”完成最后一击!"& r6 q9 g: k+ E3 g
  115.   "  ①②③% r. s( i( \& d9 j
  116. 1■■■
    - w, H$ b2 B0 M! X, W8 ?
  117. 2■■■; X  {7 h* C& _! N* @" W
  118. 3■■■. M2 ]& D$ B' w$ _4 |
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")0 o( B8 d, o# p* L# d$ V
  120. , K( i8 x" `6 A9 z. J
  121. ;;; 棋盘
      U5 @( T, V) t
  122. (defconst *wechat-5x5-white-chess* 12288* L. z6 {% d( R
  123.   " ")0 E8 q8 F2 y$ V  I
  124. ) ~/ C5 W9 ]% ?  i) }, D
  125. (defconst *wechat-5x5-black-chess* 9632
    % i$ n2 @9 W2 j$ ~; N' ]% e5 x
  126.   "■")
    & F& H$ C9 d7 B8 \
  127. , {7 C% k$ N" S" C9 p
  128. (defmacro with-board (&rest body)" o- t% k) ?* ]5 F+ F2 z/ w
  129.   `(with-temp-buffer2 r( b" I; ?0 Y7 Q! H+ y0 Q1 q( N2 Q
  130.      (unwind-protect
    : M: O0 v* v+ i0 J+ x4 J0 S
  131.          (progn$ _6 L; j3 x7 R& }0 m+ z
  132.            (if (session "board")0 _& ]) @' b" ^# z6 B% D
  133.                (insert (session "board")))
    4 R* ^4 C0 ^1 |* `: _/ {
  134.            ,@body)1 S2 ], X* E5 i$ w8 {1 q
  135.        (session "board" (buffer-string)))))
    6 m$ b5 I7 [. d% ?

  136. ' }0 L) {6 P+ Y6 q' K8 |
  137. (defun board-init (size)+ s* n/ S2 Y4 y9 k5 ~
  138.   (session "size" size)
    % J0 D+ l+ f' H  D& Q* c4 u2 l
  139.   (session "step" 0); z' T  [! N9 C
  140.   (erase-buffer)4 W% m5 m2 \+ V8 v8 L# q" k
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))6 w; K/ h4 y% l: o6 N
  142.   (dotimes (row size)% O/ }4 @) f/ }8 w. e1 t& B0 d& B
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))3 i& e/ ~2 T# B" [7 q" k( `! g6 z

  144. 1 E, H. F- {' m- c
  145. (defun board-contains-p (y x)
    # j) s$ Z  E( w0 [9 z
  146.   (let ((size (session "size")))
    ( \# |- {$ G3 Z( K/ r5 G/ M
  147.     (and (<= 1 y) (<= y size)
    : }" a( f- s: z/ q
  148.          (<= 1 x) (<= x size))))
    1 o  C9 C" C* k

  149. 4 v6 L% B. B- t
  150. (defun board-toggle (y x)
    5 G3 B1 k( t% I" N' ]2 T! e, _
  151.   (when (board-contains-p y x)
    % u  t( I) x& f, Q$ A5 F
  152.     (goto-line (1+ y))# F. }9 k( Q. X- u7 N/ K0 M
  153.     (beginning-of-line)
    - u; X. X; H; x' S
  154.     (forward-char x)
    " W; g, h0 y! i
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    % }. O" n: @, d& P2 i
  156.                 *wechat-5x5-black-chess*
    / D( S4 \/ [, {  o9 f
  157.               *wechat-5x5-white-chess*))
    - J5 v1 u' C* R+ F1 S1 f* Z
  158.     (delete-char 1)))
    # T! i2 ~% s- Q+ O
  159. , @2 e7 ^0 i% F. e- l# m
  160. (defun board-put (y x)( H% H, G# ^8 I$ j* H- O; T5 {
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    4 h# |: l* K, k) x7 m
  162.     (board-toggle (+ y (first dir))' c/ F: `& A& ?; O+ A
  163.                   (+ x (second dir)))))
    5 A# f$ L7 n% K5 u" L: [

  164. ! h0 K; |( W- `
  165. (defun game-over-p ()  [7 W. n; Y7 C3 q" j
  166.   (beginning-of-buffer)5 W+ C# j! M( J( H& @) q( C! V
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    : K7 N, B$ s! P: k/ A/ w$ j) n" N- C
  168. ( i3 h; J1 Y7 Y2 B$ q( J
  169. (defun board-show ()1 Y7 p  R, _6 R& S$ E5 P
  170.   (with-board! c2 Z  g7 x# l
  171.    (concat (buffer-string)
    ' K" t2 ]0 \8 a9 a
  172.            (if (game-over-p)
    ; S, C  k+ U" i) @1 |: e7 Z
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))5 ?- ~" g  \3 [0 W! T* g7 y6 l: {
  174.             (format "第%d步" (1+ (session "step")))))))
    ' r1 w, R" e9 {7 y0 u( X% m7 \1 f! J+ D
  175. & V& A" @/ W2 ?4 s8 K  `/ A. ?
  176. (defun board-position-parse (cmd)
    $ `/ L7 G6 K3 _! q8 M, G5 o
  177.   (if (= (length cmd) 2)
    ) ]+ g# y9 U" _' S4 H6 X9 @
  178.       (list (string-to-int (substring cmd 0 1))
    8 m9 c$ F0 m* k4 B4 O
  179.             (string-to-int (substring cmd 1 2)))7 O2 Q2 j  X1 F& E4 q
  180.     '(0 0)))
    * C; m% n% G( I0 M% j
  181. * T0 }3 o4 A+ S/ W3 u
  182. ;;; 游戏房间
    ' u4 P  w& f+ s7 `; t
  183. (defun game-room-init (cmd)7 E- [' ?" e' v
  184.   (let* ((middle (string-to-int cmd))
    % i0 ?& F- r  p
  185.          (size (1- (* 2 middle))))) k# U! u+ q. G9 ?  h* d
  186.     (with-board
      o  ]6 w7 S' I; O
  187.      (board-init size)# V& [) M# ]2 t% U1 S
  188.      (board-put middle middle))): `% q" d) ]& A3 y3 w  q
  189.   'game-room)3 h* j: c% l% u: l

  190. 5 b  B! W9 K; p( L
  191. (def-room game-room
    6 u! M4 G( _& h3 m( d  F
  192.   #'board-show" ~. D: `. Q4 f3 |1 A/ s* z, k, \5 q& g  `
  193.   (t (lambda (cmd)
    3 g% J+ z& t4 X& e) g5 R
  194.          (with-board
      a3 C; `+ ?! g! b6 D+ p9 g- F
  195.           (if (game-over-p)
    6 p+ {% K  z9 P/ s" S- s
  196.               'living-room
    & u8 H  l# z0 W: M5 F3 F- e
  197.             (destructuring-bind (y x) (board-position-parse cmd)7 E6 Y' k+ Y1 r* F( N7 Y
  198.               (when (board-contains-p y x)
    8 `! p" A1 V" T6 N) q
  199.                 (board-toggle y x)
    - ~7 Q% p) I  S2 `7 b
  200.                 (session "step" (1+ (session "step"))))
    # N) l! Y" V; \) Y9 z
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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