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

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

  1. 9 U: r/ o( ?' R' {' ~* D
  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;">;; 定义新的游戏地图
    4 _% _7 o8 t: j. O9 w) T! O' p
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    ( }) e6 L5 r! T1 y
  4.   'tutorial-room-0)                     ; 默认的入口. U; H4 A0 ?0 n  R* R  g; t+ j( \
  5. # k. f, v0 p, @0 X# m" J2 `
  6. ;;; 游戏大厅
    & h* G# q% o' B
  7. (def-room living-room
    / ?/ C/ {' a6 S6 q2 {
  8.   ;; 进入该房间后的提示语
    6 b3 O( G9 t/ D9 N7 h
  9.   "1. 教程
    / R% u! M9 ?- \0 H) y3 i
  10. 2. 入门(3x3). B) s1 p0 x9 u+ g
  11. 3. 初级(5x5)
    ' H. A" I8 M: c& d
  12. 4. 中级(7x7)
    * S8 D: C8 t% r3 C! J$ _# X$ q7 Y$ @: }
  13. 5. 高级(9x9)
      y) x& F; K8 ]! P
  14. 0. 关于作者
    7 H' {% [8 K+ r0 s$ `) ~  N
  15. 请选择1-5开始新游戏:"5 b1 o/ G8 f/ {# @$ j( v0 e" P
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名" g4 @7 W& c! X1 _% D+ s
  17.   ("1" tutorial-room-0)2 K( Q, e  m( v; @* u) @: x4 S. b
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    , U8 C+ q  @+ L6 `0 J$ ~* d3 D
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    * d6 n+ {; g3 y' V1 m' H* y* \/ K, ^
  20.   (t living-room))                      ; 如果条件为t,为永真8 A+ |. A9 C' [
  21. 3 m5 S6 u* f$ M. `/ U) {+ W; t2 q
  22. ;;; 作者信息
    ' ?2 `& k% z1 Y
  23. (def-room about-room( o- B3 {$ n; A, g: i# I
  24.   "作者:redraiment- g9 I, `, ?/ p
  25. 微博:http://weibo.com/redraiment
    7 ~; _7 p* `+ [3 a" d; ~8 [' y; t
  26. 有任何建议,欢迎在微博或微信上联系redraiment。$ I) x& d7 s7 ~# j/ f/ |" r
  27. 请输入任意数字返回游戏大厅。"
    9 o& k; s4 s) l. }4 A
  28.   (t living-room))
    , l8 P" Y& m1 r  i, s0 M5 U

  29. 5 C' z) ^5 N) k# R7 t5 ?
  30. ;;; 教程4 n7 U( Z9 ]* B0 i
  31. (defvar *wechat-5x5-tutorial-rooms* 0( F, O! N" L& U0 P
  32.   "The number of tutorial rooms")3 Q3 @! f8 F9 o- I6 Z
  33. 4 R- q: e( V3 r# J
  34. ;;; 简化教程的定义
    # ~8 k7 `4 z6 v+ u: i  w
  35. (defun string-last-line (content)
    1 z: S. i3 z0 W& Q! x6 }. Z
  36.   "多行内容组成的字符串中的最后一行"
    - b- m0 `8 }2 @; w4 `
  37.   (with-temp-buffer+ b- s% g7 f  r( J
  38.     (insert content)
    9 x4 m4 G) p* ?" v6 Z
  39.     (buffer-substring (line-beginning-position)
    " d8 o3 `2 r3 ^5 z# o$ [$ A& c1 b
  40.                       (point-max))))
    ! U* L4 J. @) C6 N$ R6 z( U

  41. 4 W' B4 I% D+ W7 g7 n% d  b! O
  42. (defun def-tutorial-room (prompt)
    ! h/ V0 \. f5 b+ K
  43.   "根据提示语自动生成教程房间。
    + U) k7 T4 I4 S* F" P" F7 a

  44. " {& @# N5 H9 {. w2 _* |( q
  45. 1. 提取最后一行作为问题;1 w/ t) J3 |' e1 X9 K. B
  46. 2. 分析问题,获取期望用户输入的内容;5 O, n+ [  V6 @; r2 M8 d
  47. 3. 定义教程房间和重复提问房间。"; K; y; n( s1 e
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    / d6 d+ ~& O  z2 p9 j' v
  49.          (repeat-room (concat room-name "-repeat"))) l+ d- O* D& @3 }
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    ; i9 L- R) v8 h% a0 E8 ~
  51.          (question (string-last-line prompt))
    % i( ?4 `+ }. b. H+ ?: g4 `1 s& e
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    2 x; y5 s3 o* u0 Z' i. q. M
  53.                      (match-string 1 question)))
    " h3 ~) J1 ^- X0 B# D8 [
  54.          (doors (if except1 b9 F5 D) B, G8 K: x4 d" `  Q) B
  55.                     `((,except ,(intern next-room))4 K% s, V, G! e: E- B/ k, y+ M, m
  56.                       ("q" living-room)
    ! F1 ~$ N) S/ j, c  T# q
  57.                       ("Q" living-room)
    $ }! A) ^  e) y) T) m
  58.                       (t ,(intern repeat-room)))
    + }% x6 u3 q5 B) h
  59.                   '((t living-room)))))
    3 t/ f$ d, [7 G- j' N
  60.     (def-room-raw (intern room-name) prompt doors)$ i! S3 N2 b' h5 c9 h# n1 O  a! Z
  61.     (def-room-raw (intern repeat-room) question doors))), {& R( e% A. d6 B" Y

  62. % C  {' ^( B2 }) O+ c8 d5 z7 m9 |
  63. (defun def-tutorial (&rest prompts)
    3 ?1 V9 L, {, {. C, ?# D
  64.   "批量生成教程房间。"1 J; C/ B6 f  G% |: A
  65.   (dolist (prompt prompts)
    ! v2 P) g* m, N
  66.     (def-tutorial-room prompt)))+ j. m. b+ F1 S9 I( E

  67. 9 U$ ?) y2 Z  r$ q' S
  68. (def-tutorial; X. O! R( n' |+ |4 L! z# k& z; ~6 s
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。) }5 R) i. h9 y. r. T
  70. 1. 教程
    & r7 W  s$ Q# w+ M; ]8 H
  71. 2. 入门(3x3)1 q* _2 N1 Z: k) j
  72. 3. 初级(5x5)5 m7 R5 o2 q8 I
  73. 4. 中级(7x7)
    1 q/ M7 D  W" @* W6 s$ V# p
  74. 5. 高级(9x9)4 y' ^1 B+ T, b
  75. 0. 关于作者' m1 U7 \+ j: Q% W& m! o
  76. 请选择1-5开始新游戏:& F3 d7 y" _! D) d. w
  77. 您现在正在游戏大厅里。
    % v/ i) s3 z- A4 e
  78. 请输入“2”进入入门级房间"
    " Z2 X# a0 u* g
  79.   "  ①②③
    / T( ?  J7 g8 Q1 G) w" g+ B/ o
  80. 1 ■ 
      v- z, u5 b8 X' ~
  81. 2■■■
    2 Q. l2 L/ G* Q( \. `) p' c5 C
  82. 3 ■ : F: [* {# _0 P( E7 K- i; k. [: @7 T
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    8 U* i& s: P- Q. k: {) u- O# L, s
  84. 请输入“22”来关闭第2行第2列的窗户。"
      t7 M' j0 U. j
  85.   "  ①②③. C7 R1 X' R3 I0 g, ], E* T6 ~8 \
  86. 1   
    ; p8 g7 y0 T. ~5 r  g
  87. 2     _. K" q* I. X7 G# q; V
  88. 3   6 X! ^" m( i3 n6 u9 I) c" ?# |
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。) I0 m+ @7 l: s: E4 s. @
  90. 请输入“11”,试着开启左上角的窗户。"
    5 g2 u0 x$ Q; q" V7 c- p% |+ k  U
  91.   "  ①②③
    3 V+ q# |& k+ @: U7 Q8 j+ S
  92. 1■■ 
    , |/ o+ Y# x* F% i8 I. |
  93. 2■  
    / I" V, b: i8 [( l- Z
  94. 3   
    + s7 j" k* ?, \* [1 S
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    2 V3 z2 [2 ]# ?( n- ^
  96. 请输入“13”开启右上角的窗户。"; p( ^/ I% B5 z+ L4 v
  97.   "  ①②③
      V  ]0 N! E; `* z8 q+ d
  98. 1■ ■
    0 x( I& x* `; T. S6 Z
  99. 2■ ■* m" X5 R* \' N/ e& W
  100. 3   9 }( b7 ~* g! m6 F8 ~
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    % `* `8 `+ P  S- A7 t; \
  102. 请输入“31”开启左下角的窗户。"
    $ w4 ^6 P! F1 n+ \
  103.   "  ①②③: X' K+ P% B" W0 i, v0 a
  104. 1■ ■6 U! q0 O) Z9 n. p+ D
  105. 2  ■& V4 O/ F+ P5 e! `  }: U' v& c
  106. 3■■ / O& C* T4 H8 G. {0 C/ i* e
  107. 此时,总共有5扇窗户被开启了。9 k) @+ y' U7 t' K
  108. 请输入“33”开启右下角的窗户。"
    , _- S7 U, X2 n& g/ f7 f4 m2 N
  109.   "  ①②③& S' O* F8 l9 ?- j! c$ w' [- ^
  110. 1■ ■; \# i" C4 `) q" G- T
  111. 2   * i+ d' a( u  p8 ?- J/ q
  112. 3■ ■
    . v3 F# u$ S& H; V* ], i
  113. 现在,只有四个角落的窗户被打开。
    2 L5 U4 j! v3 o$ z. s5 R
  114. 请输入“22”完成最后一击!"$ s. h, k" v) l$ h) q" W% S, O5 o+ p
  115.   "  ①②③# h- ?2 u7 Q% b
  116. 1■■■
    1 I  _5 u. \' t! X1 \; j/ g, y
  117. 2■■■" R5 e  S; a! w" e! a  x1 Z8 W
  118. 3■■■
    4 z+ K; o- C* f: q5 a/ _8 z, c/ t# F
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    5 J/ ?  {1 t. f$ C+ G" `' m7 h

  120. # W1 d! r  Q$ @
  121. ;;; 棋盘
    7 V6 F) c# e* n6 ^3 H; C0 z
  122. (defconst *wechat-5x5-white-chess* 12288
    ; F8 \+ `" g6 V% Z5 r' P! n3 t
  123.   " ")* K7 u* A) L/ k, H2 D
  124. # K# x# Z: A  Y* O( g
  125. (defconst *wechat-5x5-black-chess* 9632
    $ o  }+ R% Q- f" s
  126.   "■")  a. J4 B  |9 Z( R" c- u' z
  127. 3 z) S3 l- ^  _
  128. (defmacro with-board (&rest body)
    6 z9 e+ t" U& n: r2 u  {% v
  129.   `(with-temp-buffer
    * g% Q2 O2 `- ~* S7 p& [$ L
  130.      (unwind-protect
    . W* R' K( ?$ I  t! M, u3 u8 c
  131.          (progn) L: o& _' c7 p, \: t
  132.            (if (session "board")7 s4 H" E  t! B( `2 e! t$ t
  133.                (insert (session "board")))9 e  g4 C. i2 b0 i
  134.            ,@body), l4 n9 f& l# B, P, q) \2 p+ \
  135.        (session "board" (buffer-string))))): W( y9 }% P2 b) |
  136. & C9 j" E9 ^; K+ D0 q6 U2 S
  137. (defun board-init (size)
    . Y) n: s/ j9 \( _
  138.   (session "size" size)
    , R8 H  f4 q2 |+ f; R' U9 b
  139.   (session "step" 0)9 T2 c. B! j; _5 w, ~1 q0 ^# l
  140.   (erase-buffer)2 ~6 |3 I+ b+ W& ~
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))2 {  E& N5 |! ^* W
  142.   (dotimes (row size)
      b/ M' j% l3 a% O3 }
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))9 H- j& o# {! S0 e

  144. * ]# h8 c. b0 w( z- S: F
  145. (defun board-contains-p (y x). H: G7 _. @8 `$ V9 V, ^
  146.   (let ((size (session "size")))
    4 o& {3 Z9 R1 t5 d* u8 w- k2 a
  147.     (and (<= 1 y) (<= y size)
    # [# n$ x9 I; F* L9 M
  148.          (<= 1 x) (<= x size))))
    2 Q& C; z" y$ U6 M: t& }) R
  149. 4 ?. Y+ w- I$ C  x5 M
  150. (defun board-toggle (y x)
    8 n; O% j7 C: y7 o" o$ S# R
  151.   (when (board-contains-p y x); Y) f/ \6 q6 P# a% W( s/ _
  152.     (goto-line (1+ y))
    6 ?# s: O) L: b; [. ~2 D* G
  153.     (beginning-of-line)' R8 R2 L. o' S! [" b
  154.     (forward-char x)- m6 F7 S: Q& P) `$ _$ V
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))7 a- J1 c) S% s( E2 [; j
  156.                 *wechat-5x5-black-chess*: X, @2 E: i- i& i' Q0 I8 c
  157.               *wechat-5x5-white-chess*))
    $ {; |6 A& T$ W9 u, I, Q1 b
  158.     (delete-char 1)))5 d# g3 s" W$ V5 w3 V% [1 {
  159. 0 W. S; k$ k# b9 e* A. g
  160. (defun board-put (y x)4 k$ C/ L4 c$ Q% {* d% f
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0))), U, V* P) a9 o9 i1 H
  162.     (board-toggle (+ y (first dir))+ q. r9 }: q8 V6 Y
  163.                   (+ x (second dir)))))
    + j* C: W" F% T. q! c( j/ w- K
  164. 3 @/ i. @6 T6 \6 z1 C. N
  165. (defun game-over-p ()
    % h/ `+ U; {) O
  166.   (beginning-of-buffer)" x+ z% G" o: t9 t8 q. ^* {6 }
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t))). h: r5 H* k. O5 J" J

  168. . U! @. z4 f, ~
  169. (defun board-show ()
    : N( x: c2 p* o- l8 Q3 l
  170.   (with-board
    7 [% C* Y& C/ V5 i; ^8 I& W: s
  171.    (concat (buffer-string)! Y5 O' }5 R; v
  172.            (if (game-over-p)% D2 l- S" ]& q! B5 z
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))0 i* d. w$ V% r; s; f
  174.             (format "第%d步" (1+ (session "step")))))))
    + Q: S& p1 f( J0 C- l

  175. 3 x; N3 w2 ?1 W7 w, f
  176. (defun board-position-parse (cmd)( c0 C: o" W9 ~0 ?3 v) k- d# G7 F
  177.   (if (= (length cmd) 2)
    . y& D1 E7 D) L/ [) ]
  178.       (list (string-to-int (substring cmd 0 1))
    9 X# F  W5 f$ j0 U4 u+ i5 w
  179.             (string-to-int (substring cmd 1 2))); C8 D+ @7 v# }% C- w
  180.     '(0 0)))
    6 j/ w0 f- P6 _7 R2 h8 T* _( b

  181. & B4 r$ [$ Q* O5 B
  182. ;;; 游戏房间
    4 e* Y- h3 K) d1 {2 _  N
  183. (defun game-room-init (cmd)
    " f# X5 Y) j: k& I3 T1 g& r
  184.   (let* ((middle (string-to-int cmd))
    8 M1 N* Y: l3 T/ R" `& l/ ^- S9 C
  185.          (size (1- (* 2 middle))))
    # V5 r6 {  K% {$ ]
  186.     (with-board
    - u  A6 [4 l& Q9 Y- V+ p- r
  187.      (board-init size)
    - I5 ]0 Q  S3 v) {% ?
  188.      (board-put middle middle)))7 k" |1 r$ _* P; U, x0 L
  189.   'game-room)5 Z- k4 P$ }/ E6 v- O

  190. ' F& L0 d; t: t# F9 E6 W
  191. (def-room game-room8 j0 K2 P6 T2 f) ?- [4 w3 Y; N/ z
  192.   #'board-show
    . m5 T% l% s; Q4 @) Y
  193.   (t (lambda (cmd)/ w8 U! l" g1 _* k4 U' ?
  194.          (with-board
    . q' ~& U1 p* h
  195.           (if (game-over-p)
    : J$ u5 I. F5 N3 F
  196.               'living-room
    8 L0 o0 C9 C3 [% J- h, W
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    $ P0 k7 _$ U. e# d' }6 [+ q. B
  198.               (when (board-contains-p y x). E6 Z' L7 W% I9 _
  199.                 (board-toggle y x)6 W6 b+ ~2 ~" _: w4 ]7 s, c2 N) K
  200.                 (session "step" (1+ (session "step"))))
    0 P5 X  V6 K) `+ `* F
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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