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

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

  1. 8 X1 X: i9 n- }
  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;">;; 定义新的游戏地图5 S. f! a" h7 B6 d: z$ {& ]
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL9 j+ W! R/ Z- |: W. \+ \/ B
  4.   'tutorial-room-0)                     ; 默认的入口2 K9 O; r; d4 g3 Y

  5. $ q0 B; O# B3 R6 ]5 X
  6. ;;; 游戏大厅
    8 W; u0 p& \9 Q# W/ y+ O; M/ |6 m
  7. (def-room living-room
    ! p/ Z! u3 M' o3 a. ?; r
  8.   ;; 进入该房间后的提示语
    ) q' H4 ^8 V$ y0 I5 @( ]
  9.   "1. 教程9 ?( m3 R; Q$ n0 r! _# d/ P( o
  10. 2. 入门(3x3)
    / n7 m' H' }0 \$ @6 {& ?0 o
  11. 3. 初级(5x5)
    $ K7 V  {  z  t  l/ U
  12. 4. 中级(7x7)8 Q# |* x# \5 g
  13. 5. 高级(9x9)& Y3 B: o1 E" m- L% S; J% @5 N
  14. 0. 关于作者
    ; w8 K% F/ z% a* ^
  15. 请选择1-5开始新游戏:"
    3 v: \" H5 [3 F" ~2 A5 f0 |
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    5 u' W/ F2 z/ k" p0 n8 H
  17.   ("1" tutorial-room-0)
    % M6 V/ z+ }3 n' R7 F
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    3 g2 F6 R! n) R
  19.                                         ; 相应的返回也可以为函数,动态返回房间名8 m! {+ r8 y" @3 g8 b7 A+ Q
  20.   (t living-room))                      ; 如果条件为t,为永真( z7 G# \* t8 q& Y) [
  21. 0 y8 W$ z1 W  m4 U0 x
  22. ;;; 作者信息
    ; k) w* ^; h1 [. [1 c; d
  23. (def-room about-room
    5 n  J9 s/ U- G$ Y2 v
  24.   "作者:redraiment
    . \. \& c: n! G7 z2 G
  25. 微博:http://weibo.com/redraiment
    7 u8 ^+ a- D( u/ Z( S! e
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    6 f' c' x* c/ r
  27. 请输入任意数字返回游戏大厅。"
      j. |0 l; h9 P5 i: Q
  28.   (t living-room))" A7 ~% C  ~* T' m; n% r; A

  29. / q% [6 M" l4 L+ ^8 ?
  30. ;;; 教程7 i% w/ x. \! k. q1 s: M
  31. (defvar *wechat-5x5-tutorial-rooms* 08 V; ~# P+ [1 E
  32.   "The number of tutorial rooms")
    3 \, s3 N! X6 ^/ V$ O
  33. 4 \+ ~; k0 m7 T& ]$ L
  34. ;;; 简化教程的定义. D) |( |  `/ p# F8 b
  35. (defun string-last-line (content)* I3 y% F0 ^% K! w$ r9 b# p& W
  36.   "多行内容组成的字符串中的最后一行"! P* K1 t* Q! `/ b; c' R
  37.   (with-temp-buffer; t, ]- }; Q  ^0 P2 p3 U& u
  38.     (insert content)
    1 p" }- @" N6 X; w8 O* [
  39.     (buffer-substring (line-beginning-position)
    , ]7 j+ r+ Q- F: l; w! S
  40.                       (point-max))))" c- `0 [+ G6 L# h
  41. ( X! y1 G/ r$ S( e. b
  42. (defun def-tutorial-room (prompt)* }  r3 N$ p4 t4 b* f
  43.   "根据提示语自动生成教程房间。
    ; c0 j5 s$ |7 P6 \* b: c! A2 q7 j

  44.   \! [& l, D& I: b/ U) a& {2 G% ?$ A
  45. 1. 提取最后一行作为问题;/ j, d# D# I. m# Z' q- I* o. g
  46. 2. 分析问题,获取期望用户输入的内容;
    3 c7 x2 o0 D/ J; C% R* O2 t
  47. 3. 定义教程房间和重复提问房间。"! \* r+ n& ?, S) Z
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))7 r5 b' u8 G3 _" {5 v' q2 f9 |5 L  V9 ~2 Z
  49.          (repeat-room (concat room-name "-repeat"))
    . T; \3 y  l5 A" L+ F; ~1 x* h2 d
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    - I3 \3 h! C' x  Y& i# ]
  51.          (question (string-last-line prompt))
    # P9 T+ s# G2 e3 i  V, N
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    7 R$ N+ P( O# m) ?. w; L, P* p
  53.                      (match-string 1 question)))
    - n2 q+ a0 y( N, }3 T% p
  54.          (doors (if except
    - J5 b% t& {! y/ u
  55.                     `((,except ,(intern next-room))
    $ }# N$ q& H- f) S. a
  56.                       ("q" living-room)# q/ q- W. d& g7 t) y/ M
  57.                       ("Q" living-room)
    8 q% j5 B6 b0 @; c+ K# Q
  58.                       (t ,(intern repeat-room)))) l. ]3 k" Z5 z/ n# i
  59.                   '((t living-room)))))
    * I2 Z. L8 T) K- K7 [; h
  60.     (def-room-raw (intern room-name) prompt doors)
    5 ~7 j- T; X* d1 |
  61.     (def-room-raw (intern repeat-room) question doors)))+ a; c& X4 [$ n4 f6 m2 H1 H4 f+ e

  62. * @9 g& p+ H. y9 l3 y; ?
  63. (defun def-tutorial (&rest prompts)+ V+ c3 O! V/ W0 h
  64.   "批量生成教程房间。"0 O3 I6 U; y( Z. d' O/ z
  65.   (dolist (prompt prompts)2 r9 C" s& `2 L3 ^# o- s- u. L
  66.     (def-tutorial-room prompt)))
    ! x' F1 y6 y/ B9 ?4 k
  67. ( {; p1 m- n( O( s2 n4 ]0 n
  68. (def-tutorial8 l9 e% w8 y% X: \9 X$ S8 `
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。: g1 a5 `! L3 |" [
  70. 1. 教程
    8 I, C/ J0 O- k& P& P
  71. 2. 入门(3x3)
    0 s" e; ?& D0 e3 P7 a
  72. 3. 初级(5x5)
    " Z) i4 j$ m8 F) m' k, Z
  73. 4. 中级(7x7)+ j  Q/ P/ k1 X) I6 r3 d
  74. 5. 高级(9x9)
    ) @7 e" \; ~2 i3 Z5 R6 l; E( u, ^" q; S
  75. 0. 关于作者: v3 X0 _+ T; P' \; x
  76. 请选择1-5开始新游戏:/ U6 f. B2 t: p4 i8 m
  77. 您现在正在游戏大厅里。
    $ @+ V9 ~) ^' k% D" L5 y4 U
  78. 请输入“2”进入入门级房间"
    8 L+ M  c3 I  E5 n9 D
  79.   "  ①②③
    7 V! h  @) I9 I" X6 E! W0 R
  80. 1 ■ ; [. ]/ N! E+ C
  81. 2■■■6 x$ @1 o& q2 t/ @3 B" K
  82. 3 ■ 
    / K% C9 J- Z4 f
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    0 E6 d0 v: q# T& E8 f
  84. 请输入“22”来关闭第2行第2列的窗户。"6 R' y( W! ^; C  Y
  85.   "  ①②③
    ) C1 z1 S0 s/ m0 q* e6 x* {
  86. 1   , Y* H; n2 M* x& d
  87. 2   / B. u$ ^& d+ ^4 \7 @! T) _
  88. 3   5 v3 c. I( o" D
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    7 k$ x% @& U8 Q
  90. 请输入“11”,试着开启左上角的窗户。"$ @: b* a$ u" J7 e
  91.   "  ①②③
    5 j# p; E7 ?; `7 _+ P  A6 p
  92. 1■■ 
    ; j7 x7 |% J" L2 F& l* v2 J
  93. 2■  & `& G7 |. L8 V: `
  94. 3   
    : \- d4 F9 T0 ^" a5 A, k
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。: ^2 M* _$ k( ]( O3 N  }& p
  96. 请输入“13”开启右上角的窗户。"9 @4 L) v" H+ Z" q
  97.   "  ①②③8 f0 n8 M& r5 V
  98. 1■ ■  A0 `% ^1 \8 l9 v( ]
  99. 2■ ■
    , e3 w; r" r- B) d+ ]: S& B- }
  100. 3   
    & r' W8 p' f0 e) W  S: N
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    % w( ?2 o- U/ X( v6 @  v9 u2 h
  102. 请输入“31”开启左下角的窗户。"; J4 N& |; d! b, S1 N
  103.   "  ①②③& E* O) O% G' F4 p1 g7 y3 S* T* K! f
  104. 1■ ■
    2 j; t4 u% {* Z) f' u
  105. 2  ■
    ' a1 Z' i- t1 B2 j- I. e4 x& m; y
  106. 3■■ 
    5 k5 e" I: g) b- r0 ~
  107. 此时,总共有5扇窗户被开启了。) K/ b7 W" e. R! Z) o/ M3 M. c
  108. 请输入“33”开启右下角的窗户。"
    - }4 Y2 L  z( k, n) j$ o# u5 x
  109.   "  ①②③# i* \) c7 C' A# {5 L2 c  Z) B
  110. 1■ ■! X) w" w. B! y4 O- Z! ]$ \  J4 d
  111. 2   * O" ^9 B0 l0 M4 Z5 Z" ~; k
  112. 3■ ■
    * P+ ~! T* L- Y4 y0 b6 I
  113. 现在,只有四个角落的窗户被打开。1 B& ?" R4 d. D5 |6 N) b" W
  114. 请输入“22”完成最后一击!"
    , e6 a  t  W6 @7 s8 }
  115.   "  ①②③8 _2 R5 F+ _2 m2 j" @3 U- p& x) g, z
  116. 1■■■- w9 t5 \- {" a
  117. 2■■■
    - v; Y% f& H  N2 C& x- @& A
  118. 3■■■
    : p5 k; O/ ^- t5 E/ i+ K
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    : h, P" `- X7 R9 P" g( g

  120. % \1 k7 P* H. u6 |. g/ M# S$ a. E7 G# Q
  121. ;;; 棋盘( [) U% `3 Q/ M$ Z& H1 L+ e+ H4 y0 V/ z
  122. (defconst *wechat-5x5-white-chess* 12288
    2 @9 |8 o3 s" y9 p1 f) B
  123.   " ")
    $ y% b5 L+ |. _3 B6 B  i9 l; K1 Y
  124. ( s( `# g: V; L: _3 T. ?. B
  125. (defconst *wechat-5x5-black-chess* 9632
    8 C  C2 X3 I+ [8 P# J0 k( w+ L/ F
  126.   "■")
    . h) t% g* r. y6 S" u

  127. 9 j: \  T" Y' I5 z5 b+ ~/ q
  128. (defmacro with-board (&rest body)
    2 g0 O3 T, P1 r. K& r) r
  129.   `(with-temp-buffer& R& a; w+ `4 H/ e/ M! B2 i% v
  130.      (unwind-protect
    : s! }: \( I$ A  K  F" z
  131.          (progn; ]' U! [' @- X6 F: |# ~
  132.            (if (session "board")# h; E8 y9 g* q0 ?4 y
  133.                (insert (session "board")))# e% }1 s# A/ I2 m
  134.            ,@body)$ V: `$ G6 ~  x+ u0 _
  135.        (session "board" (buffer-string)))))& `, T/ h( l$ h, u# V% c

  136. % Y( P0 M# h  h- P8 `: x: m5 i
  137. (defun board-init (size)
    % C" s( d' X: \2 J2 o3 u, |
  138.   (session "size" size), s7 O; O8 Z( v
  139.   (session "step" 0)
    $ t8 A. k4 n) x9 B
  140.   (erase-buffer)
    - T7 }" }0 a* J$ {+ f
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))- y' C1 Z4 n0 {& z4 I7 ^. S& U
  142.   (dotimes (row size)& i: n; D0 I! N- x
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    ' @9 a* m; _* C9 z/ n9 K! x0 E

  144. ; g  ?. k; ^5 A4 {0 a
  145. (defun board-contains-p (y x)
    9 B$ Y* T' k) |& H8 ]
  146.   (let ((size (session "size")))
    3 O1 Q4 g* @4 T0 ?
  147.     (and (<= 1 y) (<= y size)
    7 X0 U0 v: C" ^
  148.          (<= 1 x) (<= x size))))
    - y7 ~5 v: U1 c* \
  149. 0 N: ]; y2 i! D3 x; q9 _
  150. (defun board-toggle (y x)
    9 w0 C# ^1 o! B3 Y3 Y' G8 b
  151.   (when (board-contains-p y x)
    ' o9 w) w5 d& ^  d# Y
  152.     (goto-line (1+ y))
    ; B- r" I% G  I6 _0 g% Y) h3 A6 E" }! q
  153.     (beginning-of-line): S- ?! U) ]2 h& P
  154.     (forward-char x)% W6 t; e% b$ G/ w* |
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    1 E  t0 a8 r8 L) u' t8 S8 q
  156.                 *wechat-5x5-black-chess*
    2 [& O( O* R7 p7 l3 {$ L2 l
  157.               *wechat-5x5-white-chess*))
    + F% u  o# `* V  l
  158.     (delete-char 1)))
    * }; D6 ?1 ?$ x6 X) `% N, U4 |
  159. 5 _5 ?& A# m) }/ b' n% j
  160. (defun board-put (y x)
    ' r+ `6 N7 S, @' |$ q' Y
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    % g/ W$ u; A* I7 k; s4 m( F- J7 Z
  162.     (board-toggle (+ y (first dir))# f, A2 z* W. f; Y* F
  163.                   (+ x (second dir)))))+ ?7 Y9 m4 P4 E% f

  164. : F4 l! x* }; Q
  165. (defun game-over-p ()4 l9 K, y! M: D& }5 W
  166.   (beginning-of-buffer)0 _7 h. O, }2 i+ G/ a6 V" d
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    7 K8 @8 F3 d4 a

  168. 2 t# h, e8 j- I9 P- R
  169. (defun board-show ()% L1 p3 D! g" A$ p' f  u$ s
  170.   (with-board2 G$ }  m* w$ b1 M$ P
  171.    (concat (buffer-string)2 I. p" k1 {  B5 @; h
  172.            (if (game-over-p)- y0 q/ E: Q4 v. e
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))- |2 W# z( n% F
  174.             (format "第%d步" (1+ (session "step")))))))
    $ p2 k% z5 e! t6 `  U& F
  175. 1 ]1 k9 p$ j; U- K5 ^) Z5 ^
  176. (defun board-position-parse (cmd)
    ! g0 ]' P# N3 u, S4 I9 Y0 ~
  177.   (if (= (length cmd) 2)* k5 O3 Z) i. G
  178.       (list (string-to-int (substring cmd 0 1))
    - L# C8 S* b& y$ B+ R
  179.             (string-to-int (substring cmd 1 2)))( i: I9 {( S5 u( C% k0 t
  180.     '(0 0)))
    0 E; b: \$ N+ r% g

  181. 5 H8 b. x' F$ `2 r4 v
  182. ;;; 游戏房间
    ; e" J% g4 P% N  \
  183. (defun game-room-init (cmd)2 P0 E8 w- r* b9 K2 D
  184.   (let* ((middle (string-to-int cmd))' m% e! h% x" N/ P- }8 P6 G
  185.          (size (1- (* 2 middle))))
    : P; h0 V. R- |8 g3 k' V; p1 {
  186.     (with-board$ K5 C/ K1 C3 A$ ^
  187.      (board-init size)
    4 Y  R$ }4 ^8 O# u
  188.      (board-put middle middle)))# e6 D. E  v* z$ ^: C
  189.   'game-room)
    7 |0 E  A" ^9 w( X
  190. - Z3 ^: W. `! G6 s( V
  191. (def-room game-room
    0 w* T4 Z0 q# E4 {1 Z
  192.   #'board-show+ H* h/ X6 X) b; N6 b# L
  193.   (t (lambda (cmd)& ?; l/ U8 D: ?6 S; W* `$ S8 y
  194.          (with-board" w, _) ^: I6 e; m
  195.           (if (game-over-p)7 C5 D: B4 x- U! I
  196.               'living-room
    # s; Z) Z' R6 y' |
  197.             (destructuring-bind (y x) (board-position-parse cmd)8 p" I0 X+ g' S, w# L
  198.               (when (board-contains-p y x)
    4 W& Q% j( [5 o+ N- Z+ {! `( F+ H
  199.                 (board-toggle y x)
    3 R* ^1 Q4 e  l6 ~
  200.                 (session "step" (1+ (session "step"))))$ ]: h, V4 P. w* N) R
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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