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

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

  1. 1 N/ ?# C  L' N* m, 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;">;; 定义新的游戏地图
    8 p$ t: f% T4 `! _2 w  s1 ^
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL; o/ {9 _3 H9 ]1 m$ m! ~" e
  4.   'tutorial-room-0)                     ; 默认的入口
    7 w! r; w0 q- g
  5. . ~6 B  {9 y( ~0 v4 z7 x; f# F3 R
  6. ;;; 游戏大厅
    2 j6 u+ [9 A* }5 a, S
  7. (def-room living-room
    % H! v7 f9 _0 [3 J/ N. l3 y
  8.   ;; 进入该房间后的提示语  h# I! `, k9 B9 V- P  F
  9.   "1. 教程
    # |2 S8 I" ]3 ]* b$ \
  10. 2. 入门(3x3)! @9 k, R* `' f1 R3 `5 w# e
  11. 3. 初级(5x5)) w9 y* ~3 J4 f1 m+ x3 x2 O
  12. 4. 中级(7x7)
    " O* }  c7 L+ H8 K% h: D$ }. }" ?
  13. 5. 高级(9x9)
    - E/ r# U; Z( ?# Y4 j7 q" g) @8 K
  14. 0. 关于作者' x0 e8 d( g& d' D# o  o6 w
  15. 请选择1-5开始新游戏:". o; J+ _9 ?& G( y9 ]' X0 T2 y8 E( ^9 N
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名9 R9 b7 Z6 u3 I) d( A* s' X  s7 p. Y
  17.   ("1" tutorial-room-0). ]% m9 A5 J- O9 o: B% g
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    : B6 q; u, P- [, P8 U0 }
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    / F3 Q' f6 _& S1 K6 J3 J- _6 `
  20.   (t living-room))                      ; 如果条件为t,为永真
    ' O& J. l7 i; a3 K* D
  21.   R! I3 a# s9 U9 T
  22. ;;; 作者信息4 E0 M1 J- o9 H! u! b
  23. (def-room about-room' T% r0 h2 g  M' |* K, f
  24.   "作者:redraiment4 W' V0 h- L  @' \6 t5 O/ m+ }
  25. 微博:http://weibo.com/redraiment
    8 h, h9 T' ^7 F. w
  26. 有任何建议,欢迎在微博或微信上联系redraiment。7 w( }3 K: M  T6 B
  27. 请输入任意数字返回游戏大厅。"7 k% |) ]1 s3 d: H0 N/ j
  28.   (t living-room))* ^+ i: g( ?9 T  L1 }
  29. 7 n; y  Z  @0 n
  30. ;;; 教程0 }/ G% w1 o  |4 G
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    . J3 r! z8 ]4 Y/ e5 C
  32.   "The number of tutorial rooms")
    ; w1 ^$ t. u# @" _* G: N4 i5 _# k! X
  33. 4 {3 s" G  s! R; [- C
  34. ;;; 简化教程的定义$ i# x5 h% r- S6 q( ]6 d
  35. (defun string-last-line (content)
    . X8 A8 W7 [6 |6 [" p
  36.   "多行内容组成的字符串中的最后一行"7 j0 ?3 D5 L) n, d4 o% n
  37.   (with-temp-buffer! N  f! V; |/ t1 \1 D2 }5 K* z; C, O
  38.     (insert content)# W9 @1 R+ l* d- l
  39.     (buffer-substring (line-beginning-position)) ^) r3 S- ?" U. g
  40.                       (point-max)))), U0 H% S0 G+ G

  41. 9 a$ c, n! f% Q; I! H8 g- X
  42. (defun def-tutorial-room (prompt)
    & w5 Y% J$ [. n4 ]1 t6 E1 |
  43.   "根据提示语自动生成教程房间。) `. l( b7 Y" ~
  44. 1 i3 A( D1 B' W1 a7 w; k1 l
  45. 1. 提取最后一行作为问题;9 w- ~; z  N3 }3 Z+ L6 p2 l7 x5 o- D$ S8 n
  46. 2. 分析问题,获取期望用户输入的内容;
    % o. J& k9 l: G7 g
  47. 3. 定义教程房间和重复提问房间。", i& g2 d5 r# r
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    9 E- d( O. A! ^6 d1 s" w
  49.          (repeat-room (concat room-name "-repeat"))
    3 L. y) }' A4 f" f) v
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    ; I* t" K" J/ q2 B; ?
  51.          (question (string-last-line prompt))5 V, I1 G# l8 T3 w
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
      p. D. [! {: X4 n9 N
  53.                      (match-string 1 question)))6 n- L$ A- W7 o# {8 e2 `% [
  54.          (doors (if except
    . m4 o6 U/ ~! Y; H0 d+ _6 V
  55.                     `((,except ,(intern next-room))
    4 N; m9 z9 Z* \5 C. s
  56.                       ("q" living-room)
    6 I5 E; k, b3 A$ S2 P
  57.                       ("Q" living-room)8 Q0 C& a  z. \
  58.                       (t ,(intern repeat-room)))+ s+ |7 n8 J! D. ^- k
  59.                   '((t living-room)))))5 m: R! j* q% W9 G& w4 W* y# [6 ]
  60.     (def-room-raw (intern room-name) prompt doors)" h5 |7 \: L' w
  61.     (def-room-raw (intern repeat-room) question doors)))6 G4 [5 g" W1 x+ ^" G1 s2 K
  62. . O# w" A5 \# i, U$ d
  63. (defun def-tutorial (&rest prompts)
    % Y- K8 }; ~0 _; x0 }7 t3 _; ]
  64.   "批量生成教程房间。"
    , u) v- k; |8 k4 A, E
  65.   (dolist (prompt prompts)
    / y6 [9 m: p( q
  66.     (def-tutorial-room prompt)))  \9 p: b! r* `, x- p% k5 H( D
  67. 7 Q) ?! b! ?" A2 g7 j: u4 C
  68. (def-tutorial* _% w+ T. c8 K1 j1 z- c% t
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。6 d) d3 }% s6 K  C  l
  70. 1. 教程% A" i) M( s+ p
  71. 2. 入门(3x3)
    2 o0 Q7 N0 F9 F% a0 U
  72. 3. 初级(5x5)7 T4 `) @+ U5 a6 i# ~  J7 q& ]" T
  73. 4. 中级(7x7)
    " ^; z& L; C- D- q) R/ b% k/ L' c2 Z
  74. 5. 高级(9x9)& v; l4 g/ X& h
  75. 0. 关于作者
    9 ?0 X# Q% R3 L
  76. 请选择1-5开始新游戏:
    0 X$ d" O; D. D+ F; l* s$ f. `
  77. 您现在正在游戏大厅里。
    - x3 `1 r( p; |& Q4 X
  78. 请输入“2”进入入门级房间"
    6 f& J4 N5 Q9 I$ ?
  79.   "  ①②③1 C; A- e  w7 p. c& b* P
  80. 1 ■ 1 l2 F5 S7 p2 v" c: T5 ?' j
  81. 2■■■
    ) {% G) g8 `) B/ y  ~+ I
  82. 3 ■ & S$ y3 Q5 _" _; ^* v
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    ) z9 Y& c8 ^, q
  84. 请输入“22”来关闭第2行第2列的窗户。") z* t' v& I+ n% A3 X3 r. ~
  85.   "  ①②③
    7 [3 X) J& U2 C" L5 g9 }7 \
  86. 1   
    ; x/ z& D3 L) f" Y2 \& K
  87. 2   4 ^- @" |; ~; h" f9 T' a/ w  ^
  88. 3   
    # o) s. X; x" R
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。; Q. O" w, B0 f; n
  90. 请输入“11”,试着开启左上角的窗户。"" a# |. ~" X& [: h* F" H% |6 j
  91.   "  ①②③4 s6 V' Y2 D3 v! r+ c0 D+ c
  92. 1■■ 0 B) W- D0 C& v$ t! ^9 a
  93. 2■  
    " q: q# @, f2 j  i/ i+ q5 d
  94. 3   
    * L, b# m4 H; \/ R5 Y9 S
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。$ C9 I5 g& G. v& Q- Y2 V
  96. 请输入“13”开启右上角的窗户。"
    , z/ W/ z8 V8 W3 V- H8 j
  97.   "  ①②③1 m! B9 P' V' f! y2 K
  98. 1■ ■8 D0 x0 N, `" {
  99. 2■ ■
    6 s0 [0 u& J3 M
  100. 3   
      K1 [- u0 G7 P% d5 I
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    * f- h) R+ J4 h0 Z- w, p
  102. 请输入“31”开启左下角的窗户。"& C2 K8 |+ v2 L$ F* \; S* R
  103.   "  ①②③. n( \8 U5 K+ s8 ~5 C: A
  104. 1■ ■: U9 U' {& ]( m) f' l
  105. 2  ■
    ; Q7 m  C: S# s, C
  106. 3■■ 1 p$ w& K0 A) w) Y) @
  107. 此时,总共有5扇窗户被开启了。7 J' {6 g1 s4 z7 e& y7 x( j8 C
  108. 请输入“33”开启右下角的窗户。"' e3 ]! X% N8 k2 [- [" k
  109.   "  ①②③
    ( X. `) d4 X9 e1 [
  110. 1■ ■
    3 x; n0 Z- R. ]
  111. 2   
    & Q, |) S5 |5 c2 r7 Q$ Y! m
  112. 3■ ■
    * a$ u( k+ w" A9 [  A/ |
  113. 现在,只有四个角落的窗户被打开。, b5 C: G/ f- S0 \- {
  114. 请输入“22”完成最后一击!"
    " R3 W0 L" Y- i, o3 U3 x  I) s# ?
  115.   "  ①②③
    4 o" o4 e- z; C8 l- ?  \8 K& h
  116. 1■■■
    ; Q0 w3 a" f: h" u
  117. 2■■■. Q' t# B8 V: W9 N
  118. 3■■■
    - X6 }- c3 d' _" `
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    - X( {3 F7 Q% d: N7 P( ^/ d
  120. ' u& ?  w8 u# o, z7 q
  121. ;;; 棋盘( q$ x9 g/ Y; _! G! R
  122. (defconst *wechat-5x5-white-chess* 12288! t: l3 E* k* l* d& H  ~- {1 \0 S
  123.   " ")/ s  n. P; `+ U
  124. 5 l! J/ d9 P  H2 l2 j- e
  125. (defconst *wechat-5x5-black-chess* 9632
    7 `7 ~+ {- f; Y4 @4 w0 m
  126.   "■")3 ~) p$ Q, M: C: u' b- U% x

  127. ) y. n" u7 g1 K  U! u
  128. (defmacro with-board (&rest body)
    & R: N$ G# F& Z; Z0 ^5 r. Y
  129.   `(with-temp-buffer
    % Y- q7 t# m6 |6 P5 `' c! U
  130.      (unwind-protect+ K8 ?% l, s* Z6 X# d+ B, k/ q
  131.          (progn3 F6 \- h/ D& B; Q
  132.            (if (session "board")
    0 |) w/ F! _- X9 O, D
  133.                (insert (session "board"))): u9 Q% D' A- u- X- U
  134.            ,@body)
      L6 g; |- o, D. y9 t
  135.        (session "board" (buffer-string)))))/ l2 o5 G6 b! d% @+ \
  136. 3 R/ ^: n5 U; l+ n
  137. (defun board-init (size)
    0 ^8 P& P- g0 J1 c4 K3 v6 X
  138.   (session "size" size)' s  u1 ]0 @) J5 u4 E. v
  139.   (session "step" 0); n2 f8 p* y3 ~# H- d: B
  140.   (erase-buffer)
    ; G1 S, n' b  W
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    * A4 R3 M. |' O0 L; r0 Y/ H
  142.   (dotimes (row size)
    6 i' L; N' b) j: s% I" M. D
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
      [' h( T1 f2 r" o* ]

  144. % f  t% x% K1 V# d- G
  145. (defun board-contains-p (y x)
    $ y5 v2 X( {6 b' p$ U; [1 q
  146.   (let ((size (session "size")))& l+ z3 V( I4 f6 A, G3 Z
  147.     (and (<= 1 y) (<= y size)
    $ H) [! w) [4 w9 z' a, I
  148.          (<= 1 x) (<= x size))))
    # x* E* x4 v" I. w4 I  q$ N

  149. , n' c7 q, @: \. `9 O8 u1 r! Q
  150. (defun board-toggle (y x)
    - I6 k! e: ?$ Z9 I1 w+ @6 @2 n: Q) q
  151.   (when (board-contains-p y x)* x$ a' K0 G7 V0 |- o* ]- p0 M
  152.     (goto-line (1+ y))/ U) q, D+ n8 n2 Q
  153.     (beginning-of-line)/ _9 X: H4 m: R, [: f" x9 s% `
  154.     (forward-char x)3 ^& ~8 @7 }& }
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    4 n: P; T; C6 d( \" U/ l! w' Y
  156.                 *wechat-5x5-black-chess*) w6 @# R# U/ e
  157.               *wechat-5x5-white-chess*))
      B! k7 d! X+ O; @
  158.     (delete-char 1)))
    , N7 c! |4 R' v9 _. U! {

  159. 1 U( v1 ?2 ?, L" _5 C
  160. (defun board-put (y x)5 t: r5 B; v& n1 r+ i
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    # B4 }7 K3 }: I7 M) b4 D
  162.     (board-toggle (+ y (first dir))7 M0 z2 v! B, P  O3 B5 l  G# s
  163.                   (+ x (second dir)))))- Z! n5 \$ l/ a* e* }  \
  164. ' W8 b2 u/ I& x- C8 N& p* X
  165. (defun game-over-p ()/ s. ?4 |  n9 \# E  D+ d6 f
  166.   (beginning-of-buffer)/ X( ^; ~: i" S% ~1 P
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    & m3 d( Z7 I2 w/ \

  168. 7 l9 o1 E4 R, O4 Y$ V
  169. (defun board-show ()
    , ?5 B5 Y( V+ ]5 t$ U8 `
  170.   (with-board8 |2 M) h6 |5 H5 }
  171.    (concat (buffer-string)
    4 g- j. Z3 j6 r! Z9 M/ J8 F
  172.            (if (game-over-p)
    * b7 W! |7 Z2 {3 `5 |7 T8 }& x
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    - g5 f- Z; L! B& j
  174.             (format "第%d步" (1+ (session "step")))))))( s. {/ h4 T4 ~- t9 |( K2 `6 R
  175. & ?& _/ K* Y" B" z- l
  176. (defun board-position-parse (cmd)
    $ K5 O) |: Z. y% u, E/ T- n6 s
  177.   (if (= (length cmd) 2)
    8 g1 V: P2 B9 l8 o- j4 N8 ^
  178.       (list (string-to-int (substring cmd 0 1))7 U# ?+ G4 e2 K1 H, c
  179.             (string-to-int (substring cmd 1 2)))
    " M( ~: M, R+ g3 o4 E4 S- K% {
  180.     '(0 0)))$ Y2 V9 p+ v4 M; [& s) g( K

  181. % Y/ ~" V, v0 @* p8 m* V
  182. ;;; 游戏房间
    6 `2 p: `4 V0 M* V2 N- O
  183. (defun game-room-init (cmd)
    , z8 J8 g4 o; ~2 g# M
  184.   (let* ((middle (string-to-int cmd))( C. B$ M5 `& ~0 M* D
  185.          (size (1- (* 2 middle))))8 Y, W  {0 h2 D
  186.     (with-board
    3 ?7 O, }% D2 W. P9 X$ ?' D
  187.      (board-init size)' U7 e+ a* s  }. {- P& f
  188.      (board-put middle middle)))
    ) V) ~$ a3 r$ f
  189.   'game-room)
    . [+ x' v5 q) X5 C- p# j" g$ A$ c  W

  190. - {, Z' \$ y! w" k2 H' M
  191. (def-room game-room$ y( M/ s3 K0 y7 q* U
  192.   #'board-show& R6 j. M$ f8 {
  193.   (t (lambda (cmd)
    ) b6 z; _+ ~# _, H, t' l
  194.          (with-board- ~$ X, U. s# E" Q0 s: k/ @7 _
  195.           (if (game-over-p)( {3 a6 l. q$ V  Z! `9 C
  196.               'living-room& S+ T" y0 P# M: j9 O2 o
  197.             (destructuring-bind (y x) (board-position-parse cmd)6 L/ u- l8 N8 W8 h/ Z; @9 K: w
  198.               (when (board-contains-p y x)
    8 w2 X. {: J0 _2 X5 q, K
  199.                 (board-toggle y x)7 }" o% |9 j7 A8 N
  200.                 (session "step" (1+ (session "step"))))
    % k& G9 @3 T- m% K4 a. h. c6 h
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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