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

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

  1. 5 o  j, S% ]4 u& Y$ e6 f5 I! 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;">;; 定义新的游戏地图. e1 j" I3 \! K+ U' n' Y
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    / z7 r7 \& U1 m* K2 T. e/ B' N
  4.   'tutorial-room-0)                     ; 默认的入口
    * R7 i" [4 D9 e" _! t
  5. # u" X! f/ F, u) G+ ]
  6. ;;; 游戏大厅. {$ P4 b; @9 H% W. a) u# y
  7. (def-room living-room
    / h+ V' h7 D' o) o6 R: B
  8.   ;; 进入该房间后的提示语0 U0 C+ Y. Q. A4 t4 [
  9.   "1. 教程
      n; `, Q: C3 p0 E9 C& l0 \& A$ a& S
  10. 2. 入门(3x3)2 a' B$ c. E- i0 R2 x" _& A5 M
  11. 3. 初级(5x5)
    - d% }# ~3 R- o0 C* t( r1 q" o6 S
  12. 4. 中级(7x7)
    " w& J$ V! K- i7 F
  13. 5. 高级(9x9). q  J$ M; v3 O! p
  14. 0. 关于作者
    6 b, U9 Z! l7 n: r6 q2 J! z- c+ f
  15. 请选择1-5开始新游戏:". O8 n6 k3 U' B0 J! L8 _
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名' D3 t( M, I3 D1 v. x: Z
  17.   ("1" tutorial-room-0)3 q; s; w0 s1 N% @$ v! |9 B
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    / G9 J; z+ g' q2 G3 _. I  r6 o
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    ; l- u  ~# I' T1 o" |
  20.   (t living-room))                      ; 如果条件为t,为永真
    % p# ?. T! `1 ?4 j( o+ @: w

  21. - p& Q+ C/ I" k) G+ D/ b
  22. ;;; 作者信息& W; d) n! G5 o) `' s
  23. (def-room about-room& z0 d; J% x. |& y# r0 |% W
  24.   "作者:redraiment
    * ^8 s, Z9 c3 B* m
  25. 微博:http://weibo.com/redraiment( f0 m# X1 F8 K4 x2 _: e! P
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    3 m8 s$ Y7 Y8 j0 L( r: Q: w+ \
  27. 请输入任意数字返回游戏大厅。"
    6 ]) U* H! }6 d
  28.   (t living-room))8 }/ Q8 S" j7 Z$ I! C& b5 e

  29. / U7 L  l0 ~6 i% [6 Q0 f5 w
  30. ;;; 教程! o! j) g" s* M! W1 o+ z& M+ _8 Q
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    6 [# t5 v. X5 l3 L$ Y2 f1 `
  32.   "The number of tutorial rooms")- b: e! B  R7 y  p+ r: I  V1 E

  33. % M1 u: \7 y; B( D
  34. ;;; 简化教程的定义
    1 x' M  P  k. @: K: m6 [, d/ W
  35. (defun string-last-line (content)
    ) o7 ]* Q! l1 k, N3 S
  36.   "多行内容组成的字符串中的最后一行"7 C+ a3 y9 d6 j+ b( T% V" d
  37.   (with-temp-buffer
    & C/ M- L5 R/ h: T6 i
  38.     (insert content)
    ; R. ]+ ]( }& {) W2 v
  39.     (buffer-substring (line-beginning-position)
    ( U9 r' y) ]: Q: K' `9 _
  40.                       (point-max))))
    + h4 q- J6 Q) c( J

  41. 5 ^( {& @# [0 a( O
  42. (defun def-tutorial-room (prompt)- ]" E3 f9 D/ Q/ B! L
  43.   "根据提示语自动生成教程房间。" _$ B4 A5 H) }! V, Q; p: o# n
  44. ' h1 y) ]/ E4 o! D
  45. 1. 提取最后一行作为问题;
    4 \4 ^1 `6 `0 a" p+ ?( \3 Z
  46. 2. 分析问题,获取期望用户输入的内容;; Y# J' \* M  |! V" y/ p
  47. 3. 定义教程房间和重复提问房间。"
    3 f8 k* i* C$ ?8 t+ S! f3 U
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    2 j8 O$ \- R# a# B2 e; `5 r
  49.          (repeat-room (concat room-name "-repeat"))
    - R2 h" F1 O0 Y# q  r
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    9 C: ~$ m7 |8 g- J5 S4 g
  51.          (question (string-last-line prompt)), p" X; ]: h1 F' L! G& b
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    4 X& T/ z) o7 r' O1 Z
  53.                      (match-string 1 question)))( ~; B- `8 D+ J2 f( A$ y/ X
  54.          (doors (if except/ q' e0 L( s1 X. S2 ~" s, K1 j
  55.                     `((,except ,(intern next-room))
    1 j% w( Y" c( m1 |$ Y0 ?- k$ u
  56.                       ("q" living-room)) l! k. d! M9 y( K" _* O2 h: e
  57.                       ("Q" living-room)7 e: U5 h$ |1 u8 e
  58.                       (t ,(intern repeat-room)))
      ^! D! j- _+ ~9 @
  59.                   '((t living-room)))))
    5 x) a3 e( M  i
  60.     (def-room-raw (intern room-name) prompt doors)- n, x1 u: E+ i  R5 E% s" |0 j
  61.     (def-room-raw (intern repeat-room) question doors)))
    1 i1 [/ l4 s6 Q

  62. ( m) N, m9 R" Z: z/ Q! e! J; L1 t, K
  63. (defun def-tutorial (&rest prompts)
    2 |  b# }6 ?) I
  64.   "批量生成教程房间。"
    ' |8 m6 J! @! k; _" n
  65.   (dolist (prompt prompts)
    $ ^0 M0 K0 @( \
  66.     (def-tutorial-room prompt)))
    . R& ^, ^3 T6 G+ h
  67. ) L. b: N! b# q  K% D
  68. (def-tutorial
    ( f$ S, O$ q! `+ `
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    + Y' @3 p& q# r
  70. 1. 教程4 l8 s' ?: l: v2 s. `
  71. 2. 入门(3x3)( f+ `" i! F+ Z2 a3 v
  72. 3. 初级(5x5)
    9 G; X6 N; Z! C4 [8 Q% I9 e- l- T
  73. 4. 中级(7x7)
    0 Y9 ~6 P' W, `% e9 F1 G
  74. 5. 高级(9x9)
    ; X8 {' v8 X& N3 y% F
  75. 0. 关于作者
    % a" b# j) S* y1 [" R
  76. 请选择1-5开始新游戏:
    * U4 x! _& K1 |
  77. 您现在正在游戏大厅里。5 g1 f4 l  h: M- b% `3 {: R6 @
  78. 请输入“2”进入入门级房间"! G" Q7 ]* i. O  n: g5 I2 o4 I9 Y
  79.   "  ①②③2 l- X. O. L4 C4 ?
  80. 1 ■ 
    7 f% k- a4 r1 v, \
  81. 2■■■
    5 x7 p4 N  q) Y* J; C
  82. 3 ■ + @/ x% g2 N: A. i
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    # L2 i- t" S/ c  A
  84. 请输入“22”来关闭第2行第2列的窗户。"
    3 ^2 x; ?! N, _) j
  85.   "  ①②③
    - _# I% w- r( b1 T
  86. 1   # G) _  J7 l# R
  87. 2   
    " T1 B0 G/ X* Z# X8 P- L
  88. 3   & v  A- N; e5 R+ m% ~
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。/ _# P8 ]$ P8 u/ s) W, E' D2 X
  90. 请输入“11”,试着开启左上角的窗户。"* k6 O! v+ V6 ]$ n: w, n* `- I& P1 R
  91.   "  ①②③
    / Z+ m/ t# D& w" l/ Y. R: t& f
  92. 1■■ 8 a8 A( k4 _4 q7 Z  g+ X/ G' }# Y
  93. 2■  
    , ?: Q  d9 L& i- T; L& i6 Y
  94. 3   
    + O+ x5 z4 ^6 u6 p) @
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    % R5 B, u3 u# I. I  n. m+ G
  96. 请输入“13”开启右上角的窗户。"% _6 C( a) \" t! T" Y
  97.   "  ①②③# F6 `6 ~% Y7 e5 v  e
  98. 1■ ■& e4 ~5 _7 Y+ P$ j8 M% v' G6 G1 A
  99. 2■ ■
    1 U) M' _, S. Q9 L$ W4 m3 H
  100. 3   
    ; e( A; n: n$ J$ f. a* m$ @
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。* t2 |8 G' ]1 q8 k
  102. 请输入“31”开启左下角的窗户。"3 M5 B4 [) T" y) h
  103.   "  ①②③2 v( q) ?% P& Q6 W* M9 ^
  104. 1■ ■+ Q6 [3 e( o) |4 {: ^4 F
  105. 2  ■
    $ s/ i, I- }  M
  106. 3■■   T1 w# B2 T) l" j
  107. 此时,总共有5扇窗户被开启了。6 |$ D0 }3 \4 I# s; U4 f
  108. 请输入“33”开启右下角的窗户。"( O  S7 t& ]* n8 B! {
  109.   "  ①②③2 C: }% v9 m+ Z
  110. 1■ ■& d  u6 b' }1 u3 c/ X4 c: T5 E
  111. 2   
    % @. X: c& X9 i6 R
  112. 3■ ■9 L6 n7 G  g) \7 U
  113. 现在,只有四个角落的窗户被打开。! ~& S/ W1 ~% s& ~6 V
  114. 请输入“22”完成最后一击!"
    : S9 c0 {8 w: S
  115.   "  ①②③& F$ {" M) h+ W
  116. 1■■■
    % j2 T( N% m( e: f& ]+ m
  117. 2■■■6 x! r" Z, F/ \2 T8 x0 C
  118. 3■■■
    ; U! o, E, x/ j$ j/ D6 I
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")! Q1 a: S( \) ^; `% I* R1 g

  120. 5 b, J/ T: n1 c$ B2 F
  121. ;;; 棋盘
    / t! ?* d! s- c: s/ l) n: n
  122. (defconst *wechat-5x5-white-chess* 122882 a. O- G  q6 D* a
  123.   " ")
    ; G  u! j( J1 U4 \+ F6 w0 e4 _2 Q

  124. & x" h3 i) S+ X) w/ ^
  125. (defconst *wechat-5x5-black-chess* 9632
    7 [) t, L. K+ h8 f  T+ d
  126.   "■")
    2 P3 q9 P- c" S5 R# Z
  127. 7 P4 i* l" _( H9 b/ @
  128. (defmacro with-board (&rest body)
    ( W/ J3 W* A% ~& Q, d
  129.   `(with-temp-buffer
    % f) i9 Y2 |$ v) d, z# C+ F
  130.      (unwind-protect
    ( [2 I9 A5 S4 U# B1 _% Y3 V9 \- D- ^4 m
  131.          (progn( Z; @7 K8 ^' t  k) Y, Q
  132.            (if (session "board")& n8 G' `; p: V1 q
  133.                (insert (session "board")))
    8 q$ I% u+ @- z  Y
  134.            ,@body)4 A- J1 F* F. p" p
  135.        (session "board" (buffer-string)))))
    9 |& V- G( |& m# R- p

  136. % _& s5 c' L& R" U# X
  137. (defun board-init (size)5 h2 A1 U, L  E1 m/ _
  138.   (session "size" size)
    , t0 z1 a' |& _) p
  139.   (session "step" 0)9 t/ Q' k- m- u1 e2 {
  140.   (erase-buffer)
    ' ~3 K: |0 L/ d9 \
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    ; c( k- ~, R: A8 x' N( t, r7 G
  142.   (dotimes (row size)6 [" ]* w" g/ X6 v; l+ ^5 l
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*))))); X) C9 a/ F. p/ L) U' n* y; M
  144. ; p' V9 X- p! m. X, V0 x
  145. (defun board-contains-p (y x)
    7 r- g. j9 J: a  X5 u5 z2 @
  146.   (let ((size (session "size")))! ?4 C: H4 Q1 C  X- u
  147.     (and (<= 1 y) (<= y size)
    ) y' a) z% d* o
  148.          (<= 1 x) (<= x size))))
    . ?# x9 Q1 C% K7 |8 s

  149. / _& F: j" W" U
  150. (defun board-toggle (y x)
    8 D4 n5 [6 ?/ h
  151.   (when (board-contains-p y x)& k# g  [9 H) G  ]" O% i! [7 N
  152.     (goto-line (1+ y))
    * M' E" W' x0 m
  153.     (beginning-of-line)3 k2 F- S4 _2 X
  154.     (forward-char x)8 k/ a5 h, i% ^% A$ T! C
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))3 X. f/ V5 B' r3 _
  156.                 *wechat-5x5-black-chess*
    & B1 R/ N9 l) B2 g9 h% i
  157.               *wechat-5x5-white-chess*))( i9 j1 o# L, p
  158.     (delete-char 1)))7 X) k5 D5 J; J+ L9 w( o1 P

  159. . }( [- [/ T2 `, T) N4 N( v
  160. (defun board-put (y x)' g) z. X& V2 R: m
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))/ G# f) Z9 ^0 j9 N$ U( a
  162.     (board-toggle (+ y (first dir))3 z/ ~5 s* d/ P( Y, v  U8 k; T# O, w
  163.                   (+ x (second dir)))))
    + U6 n6 t' T1 x
  164. , S% N; z' u1 ?+ u; D$ ]0 R0 V
  165. (defun game-over-p ()
    + Z: i7 I# ^9 A! e& a9 p) m7 l
  166.   (beginning-of-buffer)
    0 y% X$ R3 [) V" N: d/ U' k- p
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))) d# C+ V5 }2 V- ?

  168. 2 b0 v- h" @& R% \6 ]
  169. (defun board-show ()
    ; _2 b7 ?3 E4 N+ {# t  s, \
  170.   (with-board, i4 C( e. W; n7 e
  171.    (concat (buffer-string)
    & k4 k" f: K+ w  e
  172.            (if (game-over-p)+ M! E8 k. H: ~6 ^3 ?# c
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    ! r6 ~, w2 Z' Q+ M4 `
  174.             (format "第%d步" (1+ (session "step"))))))), H) J2 m  T/ I- R- j0 i

  175. , @4 x$ V" K0 f8 a1 G6 t# `
  176. (defun board-position-parse (cmd)0 E) Z; D2 z- ?- n. l
  177.   (if (= (length cmd) 2)
    : P5 v6 ?% B; W6 H/ l  x" A
  178.       (list (string-to-int (substring cmd 0 1))
    & I& v" }; W6 e; }
  179.             (string-to-int (substring cmd 1 2)))
    ! d: k) Z3 l. R( ]0 C% d: a1 _
  180.     '(0 0)))
    " E! Z! o( p, @

  181. 1 @& ^: \# |% k7 x$ E3 {+ Z
  182. ;;; 游戏房间3 S# c! h: \8 X& {
  183. (defun game-room-init (cmd)$ t: `2 H! v2 `: ^, E9 X
  184.   (let* ((middle (string-to-int cmd))( Y- _, n* j! J3 C
  185.          (size (1- (* 2 middle))))6 O& \+ w! A  y0 ]/ g$ J
  186.     (with-board
    5 T! }. U) T3 _% A8 {5 V
  187.      (board-init size)
    . c4 j# h# y+ h( a; u! G# t$ r5 \+ K
  188.      (board-put middle middle)))9 X4 L1 |+ n1 o4 G
  189.   'game-room)7 i0 U* ?7 W+ A4 j) t
  190. + U% `6 q: S9 b: E( I( B
  191. (def-room game-room
    7 m3 t6 Y) U% ?) C. k& w
  192.   #'board-show" f8 ]; }9 T2 S' |
  193.   (t (lambda (cmd)
    4 \/ |+ @, u/ T2 B3 H# f
  194.          (with-board
    4 L% i! e/ m* M/ M' Q
  195.           (if (game-over-p), J* I  B( e' ^9 C9 X
  196.               'living-room
    , k" s- E2 l1 L& |: A
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    . @$ l7 A# ?# a& O- {% q9 S6 v6 m
  198.               (when (board-contains-p y x)
    8 L- ^% Q2 ]9 |' P6 R+ R1 `/ ]
  199.                 (board-toggle y x)2 }+ a5 w# V0 R! w# l: X
  200.                 (session "step" (1+ (session "step"))))
    2 A7 f" Y$ e7 ?9 v
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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