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

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

  1. 6 N  G  J/ R9 \% W0 f
  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;">;; 定义新的游戏地图
    ) k4 ?- \) A, L
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL" G0 [- F/ c+ |' J( r* b; z
  4.   'tutorial-room-0)                     ; 默认的入口
    & l+ p3 p7 {% n* l

  5. ) a1 z: u/ r& H- H: i, P
  6. ;;; 游戏大厅! ~5 l9 |# ?2 Q$ U3 `- j" d
  7. (def-room living-room- v2 h$ ?3 s+ U; U" s, D  [
  8.   ;; 进入该房间后的提示语
    9 M7 y$ Z. z0 |( l. O) O
  9.   "1. 教程
    ( v; ]! Q; n" F' B8 k  V1 l# }
  10. 2. 入门(3x3)
    7 a# |6 M- Y& N) n  M# o7 i
  11. 3. 初级(5x5)
    6 |2 k" n6 N' |
  12. 4. 中级(7x7)! F8 a' L/ p" p/ D! D
  13. 5. 高级(9x9), z5 P# X. D: B; x+ q
  14. 0. 关于作者
    ! S( k" x% S4 y/ _# w+ m! H9 E+ \" q
  15. 请选择1-5开始新游戏:"
    ' P+ b, q' A9 L2 }+ }% Y6 @8 `
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    ) u# {- z3 Q/ L3 C( Z
  17.   ("1" tutorial-room-0)
    $ {! L, z/ m, \: q0 v2 {) l( t) j
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    8 l9 U7 b6 Q8 f- L; U$ M" O
  19.                                         ; 相应的返回也可以为函数,动态返回房间名1 g6 Q" K5 D8 N. \1 ~
  20.   (t living-room))                      ; 如果条件为t,为永真
    : u* \( Z3 V/ U' @1 X! s* }
  21. - X, V8 g1 L! K7 F3 U9 o
  22. ;;; 作者信息
    ) K; G( y: T% b# f/ U
  23. (def-room about-room
    7 y4 V6 h& _& T
  24.   "作者:redraiment2 m2 T7 J( H# W8 v; K3 u
  25. 微博:http://weibo.com/redraiment
    + J$ ^. `+ @8 Z1 c# A! D
  26. 有任何建议,欢迎在微博或微信上联系redraiment。& W9 i$ X- C6 L3 l8 H! Q
  27. 请输入任意数字返回游戏大厅。"
    . L+ h$ C' a6 l5 F3 |
  28.   (t living-room))9 l" r; A4 U/ R8 Y. F" a

  29. 2 i% S! M2 k6 ?, o" x
  30. ;;; 教程2 a0 ?6 U3 {( z  i2 Q
  31. (defvar *wechat-5x5-tutorial-rooms* 08 ~- y, W/ V" Z/ U
  32.   "The number of tutorial rooms"): I9 M% A6 q; D6 u
  33. : [' m3 G) R) n% W
  34. ;;; 简化教程的定义
    6 h9 \8 d- q. Z  T" T
  35. (defun string-last-line (content)
    7 t3 Q9 |  R5 S2 ?3 c+ Z' h2 ^
  36.   "多行内容组成的字符串中的最后一行"$ m9 T+ C& m' q7 N  K5 l
  37.   (with-temp-buffer% |" q) g  d0 Z
  38.     (insert content)
    * F, A  F- D$ W0 e3 {$ K- l
  39.     (buffer-substring (line-beginning-position)9 C- ?4 W( s/ {1 R# g
  40.                       (point-max))))4 |7 {7 H1 `6 H) E- ~) {
  41. ' `" n/ Q; c- x4 W/ R2 k4 V+ E
  42. (defun def-tutorial-room (prompt)9 O3 D; z7 ~% o1 U6 ~& Z4 t
  43.   "根据提示语自动生成教程房间。
    ) V  B) o& x0 ]: J. D2 t' I2 A
  44. / y6 k; `. N& D8 q
  45. 1. 提取最后一行作为问题;
    + o; u1 K' y) y) }0 l. `
  46. 2. 分析问题,获取期望用户输入的内容;
      \' d) S" T0 Q1 K) c8 G; U
  47. 3. 定义教程房间和重复提问房间。"2 v) c' U" N& }- P/ K
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))1 G0 l9 h8 i, h. r0 |, x
  49.          (repeat-room (concat room-name "-repeat"))" w9 G5 o/ j# U& \$ \, H
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    " v% r! |( j8 i1 D( q8 h% Y. m
  51.          (question (string-last-line prompt)): j9 b* l6 S$ h! l3 t! O3 q7 e: h- p
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    - Q' R; F0 L# p% O% @
  53.                      (match-string 1 question)))0 H* H. n9 a( y/ v% q8 y; o" s
  54.          (doors (if except
    ' I" H  i1 q) Y4 Q, Y
  55.                     `((,except ,(intern next-room))+ I3 a* ]' @3 F5 E
  56.                       ("q" living-room)' Q4 K  I# K- h2 U7 }4 _3 A0 b
  57.                       ("Q" living-room)9 |0 l; @: ]) `8 E( G% U: V" S: x
  58.                       (t ,(intern repeat-room)))
    6 r  d7 Y+ ]! r. m) u- o) e: f
  59.                   '((t living-room)))))
    5 t8 c9 P0 b) J
  60.     (def-room-raw (intern room-name) prompt doors)+ h( n$ j& D! j0 w
  61.     (def-room-raw (intern repeat-room) question doors)))
    7 J1 H5 v, \" t
  62. , \+ e% K2 w9 g# R$ a3 t* J4 G0 |
  63. (defun def-tutorial (&rest prompts)
    - x: H  D  O, g5 a6 F4 h% h& s( W
  64.   "批量生成教程房间。"
    $ b0 n, C5 U6 C% M! r  G! H
  65.   (dolist (prompt prompts)/ D6 O! R- M5 G( j) }
  66.     (def-tutorial-room prompt)))! ?' [1 X; @% Q- o2 T( C
  67. 8 c7 q3 n) B7 x
  68. (def-tutorial
    4 D0 A  h# _; R2 r
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    ( ]6 J5 F$ j8 ?' I; V
  70. 1. 教程
    7 W8 y+ x4 i  s
  71. 2. 入门(3x3)* }# D  C1 o9 a' Q5 `! g
  72. 3. 初级(5x5)
    8 X: M! T& f) X% C
  73. 4. 中级(7x7)9 d& L+ p+ l) _) f% s7 _
  74. 5. 高级(9x9)
    7 l/ ^! R8 ]; S4 |1 u
  75. 0. 关于作者; O# X7 Z" G% F3 L
  76. 请选择1-5开始新游戏:
    3 ~: n3 X4 g/ V4 A: a# c1 x/ h7 _% M
  77. 您现在正在游戏大厅里。9 g5 F$ V7 M: [( z! Z) \/ J
  78. 请输入“2”进入入门级房间"2 j. @$ r7 H! V- @6 d
  79.   "  ①②③
    . b. R( z* [6 r8 W9 ?( F, b# F
  80. 1 ■ 
    1 Q' j: Z" U# y. `/ ~
  81. 2■■■: j. g6 z' @2 A" J/ ~4 u4 D
  82. 3 ■ 9 t  P3 r5 c7 A' Z+ _# l9 {  K
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!/ P( p' Y/ @' w: l9 \. F+ J
  84. 请输入“22”来关闭第2行第2列的窗户。"
    ) f5 u8 B5 m0 e8 O+ d
  85.   "  ①②③
    4 n# g5 |! l; l4 l1 N
  86. 1   
    " Q8 d5 {% I5 U6 _9 e
  87. 2   
    / C5 q' c) X' p  p
  88. 3   
    , T: j# |' i9 m/ c( k
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。* F5 P! E. @/ m# `! ?4 g  \
  90. 请输入“11”,试着开启左上角的窗户。"# \2 p( L1 w3 J4 Q) Y1 H4 B/ ?
  91.   "  ①②③
    2 N& f2 P8 I6 O2 ^5 t
  92. 1■■ 
    # e, u' ]6 [  ^1 p& o" {" g7 p
  93. 2■  
    - N! _% L6 A% i& t$ h
  94. 3   5 e5 g- z0 q( U" a6 a
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。+ v1 j9 v$ K) O, T
  96. 请输入“13”开启右上角的窗户。"
    # |& D* G3 M, z8 s5 ?
  97.   "  ①②③
    $ @; f+ ^' C! Z' ^+ G& F4 z
  98. 1■ ■4 u, D' u) ^3 A; L
  99. 2■ ■! b8 `. y) c/ E
  100. 3   * v6 F. z1 C' n1 O
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。* v4 w' b! ~7 o9 }. U! `  {
  102. 请输入“31”开启左下角的窗户。"
    6 J. D( b$ f' T+ {
  103.   "  ①②③
    ( p0 X% n7 h, M9 S6 Q2 H0 @- [$ M
  104. 1■ ■
    / K% M, Q4 G* N( P
  105. 2  ■
    ' y1 j' a3 |3 R3 R" ^7 f5 k
  106. 3■■ 
    : q$ @5 |  n# F
  107. 此时,总共有5扇窗户被开启了。
    ; e% q1 P& @% k- [8 R& s# j8 J
  108. 请输入“33”开启右下角的窗户。"
    1 i; f, D' h5 r2 u
  109.   "  ①②③
    9 \3 m) t" a5 I- C! s- \+ X
  110. 1■ ■
    3 I* h. q0 m: W, t& Y& |
  111. 2   / V! z9 v5 W* s" b
  112. 3■ ■
    $ B+ [- i& ?3 ~* C; y
  113. 现在,只有四个角落的窗户被打开。( H2 g& ^# D6 O1 f2 g# p# Z
  114. 请输入“22”完成最后一击!"
    , Q) r8 I& G$ ]* f0 c4 j
  115.   "  ①②③
    - T& a+ z6 Q$ [) V
  116. 1■■■
    $ ?) l: Y% i, G# M0 s2 _! D
  117. 2■■■( o4 a3 z1 Z7 T$ P) |8 K/ a
  118. 3■■■
    7 L* T  y& N* _1 N& X: `2 L
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    $ ^0 g' w$ A( }* [6 }

  120.   R; f8 l( q: X) {
  121. ;;; 棋盘
    $ o8 M7 E. \5 T9 [! k* B1 a; a. L+ N
  122. (defconst *wechat-5x5-white-chess* 122885 T) }5 V  [/ l. d& q
  123.   " ")- N3 e3 J  W1 ^

  124. ) I0 N9 i  g; S$ y  k. R1 r
  125. (defconst *wechat-5x5-black-chess* 9632
    # k7 I3 O- l% _" c" ^2 e
  126.   "■")
    3 c; s1 L. z. T1 ~: K4 @& R0 L! {

  127. 5 k- ]* |( a% x
  128. (defmacro with-board (&rest body)* D( x- ~7 i% W. y$ t! A5 n
  129.   `(with-temp-buffer/ y5 y7 p: R2 y# s$ ^7 A, Z9 B
  130.      (unwind-protect0 h" `, p6 [7 w& _& g
  131.          (progn" T  D5 S4 j1 s% ~2 F
  132.            (if (session "board")
    * e  c8 B0 N/ b$ E1 d
  133.                (insert (session "board")))7 M0 M! u  y7 @/ a9 M/ W% {
  134.            ,@body)! e9 J2 H* G* E: v/ P
  135.        (session "board" (buffer-string)))))* s2 ~6 E$ R5 q. j# d) x6 I$ L

  136. % c# Z1 ^! H' @4 q# E5 Z2 h# c+ E0 y
  137. (defun board-init (size); W/ o0 y  c+ j1 _8 O. a1 U3 p
  138.   (session "size" size)
    + H( V* C4 ^7 x; D+ u
  139.   (session "step" 0)
    4 v" m" W; }* k. Y1 _8 D0 ^1 j
  140.   (erase-buffer)
    3 p* o* O: x% v7 W9 D' d+ L
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    / o' U* ^% S$ G# g
  142.   (dotimes (row size)
    2 F- T' D5 I/ l* A! y
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    / `$ w" V3 B4 R8 x

  144. : _7 P( ^: X" m6 g* v  v, P
  145. (defun board-contains-p (y x)) s8 r/ }/ K  O0 J
  146.   (let ((size (session "size")))
    7 q- ?( ^  i& C
  147.     (and (<= 1 y) (<= y size); ?! J, a$ i! e8 W3 K: S$ S
  148.          (<= 1 x) (<= x size))))" a3 o/ N, g% N, @4 K" c4 ^0 r1 T3 a
  149. # @/ j5 H+ ~% c" F1 X4 f) w
  150. (defun board-toggle (y x)
    5 z4 p9 M9 L8 P1 `! o2 f
  151.   (when (board-contains-p y x)
    , s9 g' x0 O( B; O
  152.     (goto-line (1+ y))
    / U7 Z' V# E+ H3 h2 e, k
  153.     (beginning-of-line): N5 n' Q) o, I; Y
  154.     (forward-char x)
    / z! @1 Y1 _* k
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))) t! O& |7 Z/ N7 X7 {
  156.                 *wechat-5x5-black-chess*
    + t" c) `( l9 q% A
  157.               *wechat-5x5-white-chess*))6 `' W0 _; p# ~3 u
  158.     (delete-char 1)))
    % o( l( Y' V5 B# M

  159. : j' {1 w: e5 A& G3 M
  160. (defun board-put (y x)
    6 n* m2 U1 {1 u1 X6 k8 [& [
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))6 D; e, n2 i7 Y) u2 Y: D2 E
  162.     (board-toggle (+ y (first dir))
    " a5 ~+ j" T3 Y. B# l6 ]$ d
  163.                   (+ x (second dir)))))% ~2 Q9 z$ \2 W8 r2 q8 ^3 J; o7 h$ ~

  164. 3 R' E3 o# R! {2 l& E/ f" }  O
  165. (defun game-over-p ()
      D- x, K, W' n" V1 ^5 X2 h0 v
  166.   (beginning-of-buffer), q  W2 x2 {' [
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t))); j2 }# Z5 s- G
  168. 4 N6 i' r- T7 R! G9 Z& P) f
  169. (defun board-show ()
    + P; N5 o8 |( v- S! a& I  X. x; k
  170.   (with-board
    1 a. F6 Y, b. U* B# G( P$ a, P5 C, W
  171.    (concat (buffer-string)
    : V8 R0 Y1 J, J' |
  172.            (if (game-over-p)8 H5 f) t  X6 P2 X; h
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))% n' ~# j0 S( P
  174.             (format "第%d步" (1+ (session "step")))))))' q. D- }/ T) J! e+ [

  175. & a4 _* I0 x$ ~) j( c: ?% [
  176. (defun board-position-parse (cmd)! @8 E3 P5 @$ J2 @
  177.   (if (= (length cmd) 2)
    8 ^. N  m8 E) l3 z
  178.       (list (string-to-int (substring cmd 0 1))
    ( X, ]- q! `& D# z& k$ q8 y
  179.             (string-to-int (substring cmd 1 2)))
    9 D! q: v, W0 B! ?6 U
  180.     '(0 0)))
    $ Z- v8 J% N* e; c2 F
  181. + s0 w0 J1 ^. R+ y' t$ y
  182. ;;; 游戏房间+ Q0 r0 W% U. G, p9 r6 w* [
  183. (defun game-room-init (cmd)
    # e: m0 D2 E6 y3 l
  184.   (let* ((middle (string-to-int cmd)), K7 W+ r# c- s2 \0 A
  185.          (size (1- (* 2 middle))))
    9 K9 u7 X  G$ K, q
  186.     (with-board" _8 R# Y3 J! j% ~& c( g  ?' E
  187.      (board-init size)  ^( ]5 W6 q: J; W5 h3 z
  188.      (board-put middle middle)))
    ; x2 p- f& M$ S0 |) V. R
  189.   'game-room)9 U" Y- n- _( Q: x/ {8 J# R

  190. 9 P: J5 {6 n# X
  191. (def-room game-room
    - z. i  w4 K# i0 a' N$ V/ C) |
  192.   #'board-show
    ! J& `5 @: ]/ Q4 h9 |
  193.   (t (lambda (cmd)
    + \: J2 p7 q0 [9 d# d2 @
  194.          (with-board9 m: I  I. I: O) s
  195.           (if (game-over-p)  W+ N8 J8 F( U( w/ p& c
  196.               'living-room
    5 S1 `. ^+ @. N4 _" }3 y4 N$ U( [6 O
  197.             (destructuring-bind (y x) (board-position-parse cmd)6 ]+ u2 V  m  ^! J6 v" P' {
  198.               (when (board-contains-p y x)
    ( j1 s9 ?! E3 @$ w) I
  199.                 (board-toggle y x)+ d- D3 S* ~4 A0 ]& t9 l" W
  200.                 (session "step" (1+ (session "step"))))
    + F6 q4 T: C! g! ]$ O$ p  `! t1 `
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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