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

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

  1. + L6 T4 e  Q, b! U, V
  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;">;; 定义新的游戏地图
    1 j3 X8 q* v5 |
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    7 B; T, p0 u* v- Y3 f' x& \- w7 @4 Y3 Z
  4.   'tutorial-room-0)                     ; 默认的入口
    9 X. M( ^# @# t8 y

  5. ' b8 K5 X) w' x0 h
  6. ;;; 游戏大厅
    8 Z5 q) P: C& {; N3 `" }$ F
  7. (def-room living-room( J# f8 i9 s% A# L! L
  8.   ;; 进入该房间后的提示语, e4 ?9 v  m) Y1 b  P! ~( H$ G
  9.   "1. 教程* d  l( I" X9 e* `  h0 h
  10. 2. 入门(3x3)% z' J  X4 R. t) H
  11. 3. 初级(5x5)
    7 t, D! f1 w0 X2 J7 `# J* j8 G% n
  12. 4. 中级(7x7)
    ) u6 @4 |/ U9 R+ B9 Z
  13. 5. 高级(9x9)* m* {# X2 F: l: p
  14. 0. 关于作者) t1 ^/ i2 ?( u% D: B6 }% z6 T
  15. 请选择1-5开始新游戏:"
    - i+ v3 S/ l5 f  B- I6 @
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名# I; e2 o3 l& ?: l9 Z) i9 E: O$ Y
  17.   ("1" tutorial-room-0)
    6 ?6 U: E* U" H) i9 I4 X, |8 n5 f
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配4 G: a- G3 n1 h' J  j
  19.                                         ; 相应的返回也可以为函数,动态返回房间名+ ~& Q2 S9 E9 D6 s6 Z
  20.   (t living-room))                      ; 如果条件为t,为永真' s! U! \  _+ Y- @% E0 t
  21. : G( d5 m0 @& T0 N* c8 X
  22. ;;; 作者信息
    $ h+ L$ Z( \: C7 Z6 J- X
  23. (def-room about-room* W( G9 N" e! F, i. F- M
  24.   "作者:redraiment
    % `3 x" J+ c: q# w0 \
  25. 微博:http://weibo.com/redraiment) z" F& M  E2 Q7 O3 m
  26. 有任何建议,欢迎在微博或微信上联系redraiment。; A1 @9 B- Z, }/ `+ ?$ X$ S) q
  27. 请输入任意数字返回游戏大厅。"( d! T! \; c4 s* ?  F/ C
  28.   (t living-room))
    % R6 `, E4 c6 e" h

  29. 9 [; Y: Y/ H9 i) U, ?" e
  30. ;;; 教程
    / s( R; D2 |8 x( Z9 i0 n$ s
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    # P9 K' p1 u4 L5 w9 z
  32.   "The number of tutorial rooms")
    8 G* F$ g5 k: ~4 _$ p; O0 v
  33. 7 {0 o2 V6 T9 w0 t* s4 |
  34. ;;; 简化教程的定义
      Z5 P" I6 m( [8 O4 Q+ [
  35. (defun string-last-line (content)
    6 J* ]) K# b: c
  36.   "多行内容组成的字符串中的最后一行"7 r5 c$ z, H. f" A" x) z% W
  37.   (with-temp-buffer9 G0 Y" F& X" @7 ~( ?0 t, Z
  38.     (insert content)- n- e- B7 _6 r
  39.     (buffer-substring (line-beginning-position)
    0 B" m5 T* Q: L5 d" V
  40.                       (point-max))))
    + Z, b9 ~( w4 G$ J
  41. & U9 c/ S2 h. O2 s% e
  42. (defun def-tutorial-room (prompt)% c. t. ^) j9 b7 \
  43.   "根据提示语自动生成教程房间。
    " Y2 `2 ]3 `9 x2 v8 c1 P1 M) Q
  44. ! l( }1 n# W4 d* J
  45. 1. 提取最后一行作为问题;/ c0 v+ n1 N1 O' s0 ^2 r$ ~6 y
  46. 2. 分析问题,获取期望用户输入的内容;9 t( _$ Y& h7 o: ~7 w
  47. 3. 定义教程房间和重复提问房间。"
    . W* u. Y6 d* v- W- L: B
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))! h: a! W+ _1 w8 [, Z: `
  49.          (repeat-room (concat room-name "-repeat"))
    2 g" `- s8 K6 i# ]
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))/ [' h5 H+ G3 S* J" ~0 b' _3 A" g
  51.          (question (string-last-line prompt)); U8 T/ \5 w- i6 l  z
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    * N8 z) o+ H/ f" X9 c, {9 ?7 N
  53.                      (match-string 1 question))); N1 E8 i% h" |: q- _3 ]  d0 L
  54.          (doors (if except
    2 I. B$ E8 n1 b( s! r" ^- `
  55.                     `((,except ,(intern next-room))& q8 G, |8 C  m
  56.                       ("q" living-room)6 Z" f1 p6 Q3 \( |5 x: V+ l, ~
  57.                       ("Q" living-room)
    # ^7 {) k: X5 D0 Y
  58.                       (t ,(intern repeat-room)))
    7 Z" Q+ |( m$ y* d1 P* N
  59.                   '((t living-room)))))( \; j# V& n8 _3 s- B
  60.     (def-room-raw (intern room-name) prompt doors)
    5 D9 `8 D# o8 ?: s9 N# K
  61.     (def-room-raw (intern repeat-room) question doors)))* M- p! Z( X4 V; o0 T, T) e  Q
  62. ( k9 v  V$ r8 s0 k* {
  63. (defun def-tutorial (&rest prompts)# Z0 A0 W& s. C# f- r( A( w  _% s
  64.   "批量生成教程房间。"0 l1 c; Y4 ]" b$ _0 b' c! ?. k
  65.   (dolist (prompt prompts)0 u. l* S: i2 G/ ]
  66.     (def-tutorial-room prompt)))
      ]; U% C8 x2 o- U
  67. ' {+ _7 E, K+ M* B
  68. (def-tutorial# p  ^. X+ {' Z  M- D, }! H
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    : G: c' b* _  n" c
  70. 1. 教程
    , ^' R8 r, j8 F1 T: w$ r
  71. 2. 入门(3x3)
    $ L! |; l9 r  B$ [. w
  72. 3. 初级(5x5)
    3 A' V) i/ H' \* h" z
  73. 4. 中级(7x7)
    : c% }6 s. L. o9 _% h- z6 Y
  74. 5. 高级(9x9). I: z5 [. n7 |# z3 W% P% n) i
  75. 0. 关于作者
    5 r3 Z! @; n% P+ s/ Y0 e4 l( A
  76. 请选择1-5开始新游戏:
    - E! z2 U0 ~# ?/ B3 g- N  V$ Q
  77. 您现在正在游戏大厅里。
    3 E5 V5 D: t: X8 y
  78. 请输入“2”进入入门级房间"
    : l2 o/ m, S8 U+ z( X& c
  79.   "  ①②③
    1 o6 V& _$ E% z& E. }7 W$ Q  p# K
  80. 1 ■ ! [5 x; O) s& }2 p/ E" O
  81. 2■■■$ I. p! [6 v- z( M. f, ?9 Y
  82. 3 ■ 1 M- c( H; I' ^5 E
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    7 M5 b9 ?( Q/ m0 _' l& e/ a" N" O9 Q! C
  84. 请输入“22”来关闭第2行第2列的窗户。"( p# r% T' |" ^4 S
  85.   "  ①②③
      \4 H' v/ \$ L, r* J  ^/ V3 l
  86. 1   
    ) y2 F7 C! o1 K% q9 f8 @. F- M. w- D
  87. 2   
    " ^8 v" }* G" d$ y
  88. 3   
    ; H+ h* u: ?6 n% V! t) X
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。/ K+ e% N" q4 h8 @7 [, v
  90. 请输入“11”,试着开启左上角的窗户。"" g& Y( K6 D, R3 ^
  91.   "  ①②③
    8 E% n, Z' ]7 ]- V% K
  92. 1■■ 
    * S4 W* A  f7 B0 W9 U, n
  93. 2■  
    2 z; W) {: O1 a- x; `
  94. 3   
    6 ^7 b3 L4 r1 ~7 q0 B3 v& O  p
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。7 I; m6 u; _7 d) h8 c
  96. 请输入“13”开启右上角的窗户。"
    ; h9 n" d/ T$ e
  97.   "  ①②③
    2 m" p7 k" G/ N: v3 A# e
  98. 1■ ■7 V+ y; j- z) c1 F# p7 y
  99. 2■ ■
    ) g0 n# T. o+ y, X
  100. 3   1 b% E" E0 D. G2 b6 h5 K# {$ h1 W  ~
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。( A9 T, d/ |7 ^* N0 @" l! d
  102. 请输入“31”开启左下角的窗户。"
    $ V" y/ q& B& t6 h
  103.   "  ①②③. p5 U8 b3 Y4 L2 F$ l9 K6 P9 Y
  104. 1■ ■
    4 m. e1 L3 d, I1 h6 X% }
  105. 2  ■
    . c% [( U- }: o# C4 u! {1 A/ N0 q2 b
  106. 3■■ 
    5 U: G2 z1 \# n4 R( z* n2 L
  107. 此时,总共有5扇窗户被开启了。
    # V, @: _8 K; y
  108. 请输入“33”开启右下角的窗户。"
    7 V) o" H+ y1 V$ V! E$ S1 }0 z
  109.   "  ①②③2 `$ R' @3 Y. U  f( ]0 o9 E% s& F
  110. 1■ ■! J% g) V' K& F. f# ?( ^! j
  111. 2   
    - q3 d' r. R; r9 F8 t( `4 G
  112. 3■ ■
    8 s( n) e+ K; O, o' b
  113. 现在,只有四个角落的窗户被打开。
    4 P: T  u4 |: n; K
  114. 请输入“22”完成最后一击!"
    : }+ ^! i2 \* Q+ X+ x
  115.   "  ①②③
    " G6 `, o+ g) O; G: `5 A5 c, r
  116. 1■■■
    / Q& Z5 g+ V8 r2 [
  117. 2■■■$ N: X# ~$ i! L: g
  118. 3■■■4 P3 s; o; u5 j8 r
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")1 {# \  A/ [% S( t$ w0 P7 G. n
  120. - a6 j) m0 d6 S% Y7 z6 l9 Q" i
  121. ;;; 棋盘2 V0 M4 `& b0 ^5 V7 O
  122. (defconst *wechat-5x5-white-chess* 122882 B$ N; I, d( E- q) L- @
  123.   " "). E  i% w4 `. w4 q  ~
  124. 3 X$ f* ^8 H6 w  U
  125. (defconst *wechat-5x5-black-chess* 9632
    # _9 v6 B; C0 ]3 C' V4 N
  126.   "■")9 J6 {3 s  I" p9 M, P# T

  127. ! g. {3 N5 u1 S* z8 P% v- s& M
  128. (defmacro with-board (&rest body)
    % `% \# e2 E+ m/ S! S
  129.   `(with-temp-buffer$ M1 P$ U& v( Q7 p7 w1 K) z) j
  130.      (unwind-protect( p, y( V0 R+ v
  131.          (progn$ W1 h6 t0 I* |
  132.            (if (session "board")
    " y+ @- J9 \2 O: g
  133.                (insert (session "board")))
    # T, w; S/ b" N" e
  134.            ,@body): X5 Z" g& s' }) Z2 s8 j
  135.        (session "board" (buffer-string)))))- {( V, v8 }& w$ S$ x

  136. * C* h* K0 }7 a' @: X- O  U7 K
  137. (defun board-init (size)
    " {2 I3 J0 a3 I' X/ f
  138.   (session "size" size)
    ( O+ w( O( e2 J2 v  k: z
  139.   (session "step" 0)
    + W1 ?' j3 W! u6 W
  140.   (erase-buffer)% O% t4 `! D6 C+ P1 a! U* U# v' d
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    & Z: S/ F1 y- a
  142.   (dotimes (row size)
    8 A: f# K! Y: X0 t
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))( ]5 O) J  @2 M' u8 y$ A( r- E4 P

  144. ; n7 c( \$ s  q5 N8 [& ?* C
  145. (defun board-contains-p (y x)2 M7 f/ C8 V5 x( G8 p+ P/ y% i
  146.   (let ((size (session "size")))
    . o- n! L4 C0 P  m" ]: d2 ?2 ~
  147.     (and (<= 1 y) (<= y size)
    ; W' b7 W4 B8 R
  148.          (<= 1 x) (<= x size))))
    8 \9 _) c3 M9 J
  149. $ _9 @1 r3 K' ]8 z: F
  150. (defun board-toggle (y x)# v5 q+ o* o) D' V# O, l' Q
  151.   (when (board-contains-p y x)/ f7 @( j5 J$ j: r2 w8 g! q. [$ Y
  152.     (goto-line (1+ y)). ^# {" R  m1 Y( L0 r
  153.     (beginning-of-line)
    ! R" P) a! ^; R8 _9 U& W& z) I
  154.     (forward-char x)
    : Z+ }9 G  z6 B" {, h
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    0 u! ?% I# {1 x2 ]; K' ]
  156.                 *wechat-5x5-black-chess*/ Q$ G) {! L+ o5 A; B% n
  157.               *wechat-5x5-white-chess*))
    : B1 k; K# J) U% K6 x8 M+ M+ P
  158.     (delete-char 1)))3 t6 Q, p" d! ]* J; K1 g, ?
  159. # J0 l+ Y8 R; w* Z" j* Q* w3 t
  160. (defun board-put (y x)
    ) G# t- K2 |) ^* Z: `  W7 _) ]
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))2 E; P  M) j- ~3 u+ o9 }5 j
  162.     (board-toggle (+ y (first dir))
      g( Z8 H8 B0 p; K
  163.                   (+ x (second dir)))))+ a7 C; P, |5 _
  164. - T6 O0 [6 c* ~+ Z+ b
  165. (defun game-over-p ()
    : c, O# r7 n: b
  166.   (beginning-of-buffer)) b) n+ p, I" P7 M- a0 X
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    # r. ~" F( P7 `. f% G! W

  168. ; s- K5 N3 F2 {# D
  169. (defun board-show ()( W. X' F" h3 B1 u0 [; o
  170.   (with-board
    7 U# B* Z0 C' t
  171.    (concat (buffer-string)+ P% `4 V' e, j/ K$ y+ W- h
  172.            (if (game-over-p)
    " s# h6 g6 E; c" v1 H5 U
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    - \& w( T1 ]0 {! {7 j1 L/ \5 C7 M
  174.             (format "第%d步" (1+ (session "step")))))))3 u, o5 i# z! y% y3 B+ \

  175. ' b1 \& f& c( D0 e" v: l
  176. (defun board-position-parse (cmd)& _1 O# H7 M& b8 l
  177.   (if (= (length cmd) 2)
    9 q7 k1 h: j* M$ Y+ f
  178.       (list (string-to-int (substring cmd 0 1))
    0 H7 `" w8 T! g
  179.             (string-to-int (substring cmd 1 2)))
    1 e) S# T3 l/ q4 k% i3 g
  180.     '(0 0)))
    5 Q* P0 B3 v& X

  181. ( O1 a; V2 v8 `3 O# q9 E) A
  182. ;;; 游戏房间
    * l0 p% R* m5 D7 w
  183. (defun game-room-init (cmd)- E. J9 r5 Q1 h3 @  I4 a
  184.   (let* ((middle (string-to-int cmd))" _  m$ _( g7 l3 o+ I6 v0 M
  185.          (size (1- (* 2 middle))))
    ! w7 s5 u6 F# R+ M6 l" t
  186.     (with-board3 B- p1 t/ }7 H; N; p! q- p
  187.      (board-init size)
    ) Q; p9 Y# b- f( K
  188.      (board-put middle middle)))( s0 o$ P& H6 }
  189.   'game-room)
    ' y5 O. m$ I1 d) i1 i& g+ I- X
  190. - c0 p5 \0 o9 I( J' |9 T: ~
  191. (def-room game-room( a0 o  `) s. p
  192.   #'board-show
    9 A: L6 w) ^, W7 o: o1 ^( h8 D
  193.   (t (lambda (cmd)8 c$ S" L+ ?" p! X2 V* |% t
  194.          (with-board' H- [! U5 W1 }6 c9 P3 z
  195.           (if (game-over-p)
    ; G' N: X1 D; r4 r
  196.               'living-room
    , M  f: M5 L2 A) P' h
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    1 ?/ |' }) d6 ?$ Z7 O$ ]
  198.               (when (board-contains-p y x)1 d' s2 p* ^# k' T2 D" D
  199.                 (board-toggle y x)
    # f  c7 r  u! l. X( h
  200.                 (session "step" (1+ (session "step"))))
    % E5 n' J4 e  P" s5 a; f6 k
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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