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

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

  1. + O4 @8 A. V# N- T' Z
  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 z6 l6 k, f, L# d
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL" X; d" D4 ?& @  w" I$ ~/ e
  4.   'tutorial-room-0)                     ; 默认的入口
    * `; V% l' A9 [

  5. 8 S2 v9 a9 @, y+ w8 |/ P( t
  6. ;;; 游戏大厅
    * m% }2 i% Q( S) M8 x
  7. (def-room living-room. V1 K, K1 g+ a* k. i4 R
  8.   ;; 进入该房间后的提示语
    : J+ F& i; b# D. y0 _; i4 [' l. f
  9.   "1. 教程: M7 Y( J6 o8 g( W- ~% ?
  10. 2. 入门(3x3)
    1 f7 L% b5 V: w
  11. 3. 初级(5x5)
    % K' b3 e. R2 G7 v0 x
  12. 4. 中级(7x7)9 k( {' T7 p  O$ f
  13. 5. 高级(9x9). t$ b4 c: M9 L5 a6 L
  14. 0. 关于作者2 `- m* A& Y7 t2 G, g( H, ~! H7 q
  15. 请选择1-5开始新游戏:"
    ; \# X/ }+ u3 q
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    : B. i1 h. D0 d* i$ S
  17.   ("1" tutorial-room-0)3 f: `* |7 U1 M- s0 X! J0 x
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配4 Y. g# C' M6 a3 ]; D# ?1 |, S
  19.                                         ; 相应的返回也可以为函数,动态返回房间名( m! `3 b) ?5 p3 ]( M& B' H/ J
  20.   (t living-room))                      ; 如果条件为t,为永真1 R8 @# K3 T7 `) q5 U- r
  21. 3 t& a: U1 F- u+ \3 Y* _
  22. ;;; 作者信息
    + R" c4 n5 }* X6 ~2 V
  23. (def-room about-room  S7 b1 g; e" c# ~$ [) Q
  24.   "作者:redraiment9 E  y7 b1 v3 Y
  25. 微博:http://weibo.com/redraiment
    6 M# }" l2 n& l7 M3 Q
  26. 有任何建议,欢迎在微博或微信上联系redraiment。  s7 u& S% X6 C, L' `7 Y
  27. 请输入任意数字返回游戏大厅。"8 c5 M6 W! S; j5 q8 o! \$ h
  28.   (t living-room))
    ' U/ D& \; T, V# ?$ T9 o% g

  29. 4 {5 ?( |3 A1 R' |' o. E
  30. ;;; 教程
    & Q1 }+ p3 k. U
  31. (defvar *wechat-5x5-tutorial-rooms* 04 p/ a1 J, @, I( Z
  32.   "The number of tutorial rooms")
      b1 r# p" \0 ]7 v" C  d
  33. . }- h1 ~+ s, P6 o+ Y9 ?3 C
  34. ;;; 简化教程的定义
    " f" B+ W$ v) o9 F
  35. (defun string-last-line (content)
    4 v% e- b& S) l1 ]) g% U7 Q) [
  36.   "多行内容组成的字符串中的最后一行"
    * Q1 i: V- r( T
  37.   (with-temp-buffer8 o: ~! l) Z$ P+ N8 U
  38.     (insert content)
    . G" \) d( Y& @% V8 V
  39.     (buffer-substring (line-beginning-position)  _: f$ A# H3 Z
  40.                       (point-max))))
    % N1 e8 Q. r1 {6 _- u& V! a6 C# R! \

  41. ; o5 Z% @- p7 N$ g  d4 t
  42. (defun def-tutorial-room (prompt)
    3 A% V# c3 h6 @' x( N, G
  43.   "根据提示语自动生成教程房间。
    " F. p( z% @5 z# Y* a8 y  T) t2 z9 W

  44. 4 X  E- ]' P5 P- l
  45. 1. 提取最后一行作为问题;
    7 y, p2 W. G4 E5 p$ e
  46. 2. 分析问题,获取期望用户输入的内容;
    . R5 `3 l2 |- A! ]! T4 p3 M2 J8 T  P
  47. 3. 定义教程房间和重复提问房间。"4 R( z& u& v7 C
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))# y4 P! d  u  G  a$ N; t
  49.          (repeat-room (concat room-name "-repeat"))1 ]3 C+ O+ F: ^0 g4 I0 o6 F
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))% x7 @5 m: K4 G* n
  51.          (question (string-last-line prompt))
    9 d: h& r5 {0 M: z) C  e
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)& a8 ]4 w: q  M' E& s
  53.                      (match-string 1 question)))
    9 [) G% N7 x: a7 W% i
  54.          (doors (if except( M3 W8 @( V6 T) Q% d1 i
  55.                     `((,except ,(intern next-room))/ s2 u+ V9 Z# x  y
  56.                       ("q" living-room)
    6 I0 e! v6 @2 M/ P8 ?
  57.                       ("Q" living-room)" D; T* @+ Z9 U3 j& k
  58.                       (t ,(intern repeat-room)))
    , R" {# m* p, y3 }
  59.                   '((t living-room))))), n2 K5 a# q6 [8 g+ k# m3 v& a
  60.     (def-room-raw (intern room-name) prompt doors)4 |/ H: E0 A7 g2 e" q
  61.     (def-room-raw (intern repeat-room) question doors)))
    $ G2 _! V" F, d

  62. & t1 O& k, t/ n2 O3 C  K  e
  63. (defun def-tutorial (&rest prompts)7 C+ t6 o1 S2 V3 `5 A! |
  64.   "批量生成教程房间。"9 t% w/ X: t& ]  m* E8 f4 M
  65.   (dolist (prompt prompts)5 t0 N2 P7 w7 ?) I$ V/ a
  66.     (def-tutorial-room prompt))). g9 ]* @' B5 ^6 I5 U

  67. 3 I' l3 Q1 ?; Y7 s- h- O
  68. (def-tutorial; o$ j$ y2 o2 {3 k8 Y; }$ V
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    5 u" y4 d/ R4 Z# [9 H2 Y  `
  70. 1. 教程
    9 y" p+ U6 A2 a* O
  71. 2. 入门(3x3)
    % k& P0 l; B/ |% M
  72. 3. 初级(5x5)
    ( X. }3 y  k! e( G
  73. 4. 中级(7x7)3 n( T7 O7 l. u5 F! F* x
  74. 5. 高级(9x9)9 ~* h2 n4 x: X5 a: z- G+ E" Q4 a" u
  75. 0. 关于作者
    * c5 q9 V) y$ u3 K6 l' o( Z
  76. 请选择1-5开始新游戏:/ Z' j4 s& R6 I+ O6 R! @; x
  77. 您现在正在游戏大厅里。" E2 A) o2 A$ w; [  t
  78. 请输入“2”进入入门级房间"
    ; E1 v) n) p! W3 {' p
  79.   "  ①②③+ w! v6 V, |0 I) G& i  Q
  80. 1 ■ 
    * C& L% |. c% @! q
  81. 2■■■/ B! ]/ Y, @( J9 }) M( Z6 [
  82. 3 ■ $ e) n1 p$ W5 _4 n3 {) h
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!0 _1 J( c* P5 ]
  84. 请输入“22”来关闭第2行第2列的窗户。"
    $ I& Z/ M2 p+ w/ d3 ^$ Y3 [
  85.   "  ①②③
    / ]: Y1 D/ w# q. F! f6 B
  86. 1   6 y" J. T' A$ q
  87. 2   
    # e) v' t- [2 B. D0 W2 k
  88. 3   . p( ]. t  j7 f& V( a' b# W7 T3 k
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。, S3 l1 h1 N. t6 L4 G5 y9 _+ F$ |
  90. 请输入“11”,试着开启左上角的窗户。"
    % x/ P. s9 C- k3 W0 |7 t
  91.   "  ①②③3 ?) _& I3 x0 @* H: {5 A. |! U6 s
  92. 1■■ 
    2 e# R! i* e* e3 e1 Y" h8 f- I. L
  93. 2■  5 D5 r* E  w2 j7 c
  94. 3   
    . t3 p% C" O1 Z' W0 L. C
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    - p1 [: ^# ?' W- V2 @4 N' G( f: ~
  96. 请输入“13”开启右上角的窗户。"
    3 ~7 ^6 s3 f8 }& i+ i; j- Q
  97.   "  ①②③4 Z( D1 C$ k! f0 `9 @& _4 z1 \2 o
  98. 1■ ■6 Y) Z$ R! s1 X) s
  99. 2■ ■( {( h+ x+ T6 U# x
  100. 3   8 ~) f2 B2 D( s5 m* S8 Y2 ^
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
      N+ P4 U. W9 u- }, {3 }2 @: p: L
  102. 请输入“31”开启左下角的窗户。"4 w+ r; V! ?' ^* p! n# l
  103.   "  ①②③, ~; g1 H; t; T/ C* }+ o
  104. 1■ ■
    ; ?1 M8 X* ]9 j3 G# H- S6 ?
  105. 2  ■
    + b( t* n  |! V" k4 O4 S- i/ g5 |
  106. 3■■ & \- P: U4 _* \+ V/ R2 X
  107. 此时,总共有5扇窗户被开启了。9 `0 Y2 P. I( |& M
  108. 请输入“33”开启右下角的窗户。"
    ( F3 @! M: ~! \5 s% l7 v
  109.   "  ①②③
    % Y: [7 k$ E0 G
  110. 1■ ■, e' j  W, O! Z' K
  111. 2   ) S: p, t$ R; t6 Y
  112. 3■ ■
    & W: M% k0 j) M) y
  113. 现在,只有四个角落的窗户被打开。
    ! p/ S" y3 W9 D& j+ s2 M3 l' e
  114. 请输入“22”完成最后一击!"1 E% k! J# M$ X+ q& N; I; n+ l
  115.   "  ①②③
    0 E$ Z8 x, J, \' K3 `& {
  116. 1■■■# N* @  q$ g2 J1 c' T
  117. 2■■■* b8 @& V- M$ x/ T2 o; ~% e+ t5 u
  118. 3■■■
    ) k6 s. K, c6 K  e: s- O
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
      u# b1 g: T5 E& ^  y4 s

  120. 8 o: [3 p3 q: b4 W9 `: L5 |& N
  121. ;;; 棋盘+ f) j) m; H! W; H; o3 t
  122. (defconst *wechat-5x5-white-chess* 12288/ b; N, p/ @& k4 V& V" o
  123.   " ")4 @4 C9 d5 U0 y& _3 }3 H% o9 X
  124.   L6 L6 g: U5 y4 S4 N# Q3 t5 u
  125. (defconst *wechat-5x5-black-chess* 96323 Q. ?0 `' l3 ~+ {6 ~7 _
  126.   "■")
    9 a0 @, y6 S( B5 {7 K  ?
  127. 3 ^" P0 o; z1 k( Z$ T$ S
  128. (defmacro with-board (&rest body)
    5 @0 C; b8 ]" x
  129.   `(with-temp-buffer" \6 I1 s2 ~" q4 E# m2 [
  130.      (unwind-protect9 A+ m' {. V, ]+ S$ z5 {
  131.          (progn+ |: M0 R0 ?0 ?
  132.            (if (session "board")
    # J! q. h4 N9 ]. L8 Q6 U9 G8 z+ [
  133.                (insert (session "board")))
    3 c) q! p. F- |$ s
  134.            ,@body)
    ! L& i1 @9 ^  z6 s# D. W, Y
  135.        (session "board" (buffer-string)))))
    % ?* e8 G! j2 d4 u' R

  136. 1 k+ ?& H' o. t
  137. (defun board-init (size)
    5 F2 B8 _" J/ [% r' t/ P( Q& l
  138.   (session "size" size)
    $ h' V3 T5 H% X" N' S$ @2 g; j( B
  139.   (session "step" 0)4 `2 i/ ~$ S9 h9 H7 e
  140.   (erase-buffer). k" P+ z# K7 D3 g* C2 x! A
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))4 l7 M- t2 C4 F4 [' e
  142.   (dotimes (row size)5 T: n* J( c' W( O& q' Q- H8 r
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))1 O8 ]0 d, Y2 G  H/ j
  144. * c; Z& A2 w! C" B, [) \
  145. (defun board-contains-p (y x), K) \) o* @% ~
  146.   (let ((size (session "size")))/ q/ y* I  r4 s! N* P  }
  147.     (and (<= 1 y) (<= y size)' m) i% `2 u* B5 _0 ~) n" V
  148.          (<= 1 x) (<= x size))))
    - E! G( L9 j' g( v& o
  149. 4 |1 K. C6 q! y2 e0 C4 M  _- Q
  150. (defun board-toggle (y x)  f# @9 Y2 U4 u: r, N4 w% @" I
  151.   (when (board-contains-p y x)
    ) T: [  t6 |7 A/ J" P
  152.     (goto-line (1+ y)). L7 K  _+ a  N' d3 e# S& I
  153.     (beginning-of-line)9 e9 L+ ]( l5 T( w+ v
  154.     (forward-char x)7 {  e3 B  b9 G" M. p
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    7 }. D6 Z3 S' i& K- c1 B8 e  D
  156.                 *wechat-5x5-black-chess*
    8 a; Z( z2 r# X
  157.               *wechat-5x5-white-chess*))8 q; L1 V, b1 ^5 q
  158.     (delete-char 1)))
    ! n& [2 }' T2 J( \* c% Z5 l5 p, K
  159. $ C* @2 V* }$ s) o/ H
  160. (defun board-put (y x)$ V$ U' Z2 g. b4 b! A1 \* s7 T
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    : O' f7 b3 V5 _; [9 k3 `
  162.     (board-toggle (+ y (first dir))
    * G: t; o& g, N: V( Z3 I6 t( k
  163.                   (+ x (second dir))))): x  K5 Y  J+ d$ m2 ]
  164. ; k# o. }9 x+ C, z1 l3 ^) X" ?5 ]
  165. (defun game-over-p ()" I" C# s, m' B4 K
  166.   (beginning-of-buffer)2 g- O% o; O. V% G+ T% z& I
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    - P8 Q7 Y8 q, w6 C* Z1 m3 H% d! e
  168. * [7 B1 G, L' u- _4 v, B) J
  169. (defun board-show ()
    : [3 F- f* K- M) V. f' h2 g
  170.   (with-board
    " p$ p" @7 s. Y4 n: Y
  171.    (concat (buffer-string)$ t: S: e% U! q. I
  172.            (if (game-over-p). s! F0 T" l$ M. P7 ?. ?& x
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    8 R" V; d; v" c$ U. }& O4 W
  174.             (format "第%d步" (1+ (session "step")))))))
    " v: o% s( w+ d1 A# _: b
  175. ; @8 p9 z3 O# Z# F+ X
  176. (defun board-position-parse (cmd)
    % s" C. l2 l" g) ^$ f
  177.   (if (= (length cmd) 2)- }6 p1 j. j" {  N" Q, i4 N
  178.       (list (string-to-int (substring cmd 0 1))
    7 J3 S* e( H6 t- ?
  179.             (string-to-int (substring cmd 1 2)))) d; X# T# D8 n7 p! N3 w9 w
  180.     '(0 0)))8 A  N& [8 F$ Y( z

  181. , B" B0 l* h7 Y  X' l
  182. ;;; 游戏房间
    ! J; E. g  b2 u: d" [+ `4 z
  183. (defun game-room-init (cmd)+ \# _. S4 D+ `' o$ J
  184.   (let* ((middle (string-to-int cmd)); d4 A8 S' X: O3 Z* V! c* F* `. q
  185.          (size (1- (* 2 middle))))
    - F2 D1 B$ Z+ ^. P
  186.     (with-board
    ; X  i' z! F, A+ a' ^
  187.      (board-init size)
    8 f3 C. S* M0 s' Z: P& |; j
  188.      (board-put middle middle)))
    8 P: [2 q+ ^4 D
  189.   'game-room)8 t  p2 t7 ?. M  |# ~; i2 f

  190. 0 n& C% H' D& S3 K# ~  w
  191. (def-room game-room
    / X' e0 }, _4 v. J4 k" C9 \& s
  192.   #'board-show
    & E! Y7 c: Z# {* [8 g# y
  193.   (t (lambda (cmd)! O/ Q' ^$ Q" k) {! J
  194.          (with-board. j4 E% a+ F* `
  195.           (if (game-over-p)
    ) M+ l5 S8 }; I. U) z
  196.               'living-room
    * c, A0 g+ n) J8 y) n% |
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    9 N9 Y) N/ h6 P  i9 U* [7 H
  198.               (when (board-contains-p y x)" ]: S* V, x; q
  199.                 (board-toggle y x)
    3 u2 x- F9 b* q! V3 G, P& ]8 R
  200.                 (session "step" (1+ (session "step"))))) U$ J% B8 r2 D! i# O2 N! Y2 z
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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