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

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

  1. 3 A) }' I0 S* k* t2 b
  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;">;; 定义新的游戏地图
    2 u* t0 e8 t# G
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL- V% k4 ~* Y! w' o0 e
  4.   'tutorial-room-0)                     ; 默认的入口
    # I! n) [' q. }+ D- z* `

  5. 4 {" d' i5 |! ?1 }$ b* u
  6. ;;; 游戏大厅2 \# i0 K* f% Z& |5 Q
  7. (def-room living-room
    1 w2 O$ A, q3 U: U7 t
  8.   ;; 进入该房间后的提示语7 w0 G3 q7 S$ E9 P  n
  9.   "1. 教程
    : ]( e  X/ o; L7 Q
  10. 2. 入门(3x3)
    * Z9 ?1 P1 ^5 [! L) h& P2 Q
  11. 3. 初级(5x5)+ j1 ^% W) \, r4 l
  12. 4. 中级(7x7)2 e( |& `  n+ ?
  13. 5. 高级(9x9)
    ! |. E# c* R) ^) w3 m
  14. 0. 关于作者) K$ f# O% L4 P% O
  15. 请选择1-5开始新游戏:". T0 E2 j' K; X# W/ n+ h: f4 L
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名1 p3 t7 |7 F" ]0 Z1 l# a
  17.   ("1" tutorial-room-0)
    0 z) z2 Q2 |2 z4 \5 s  q
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配/ v( I" ?2 c! _5 m7 H* x1 L
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    ' X0 f1 I/ }! {! C0 Q- i
  20.   (t living-room))                      ; 如果条件为t,为永真. L3 s$ I* Z; L( {+ v

  21. * l: }& L6 i- Y3 W
  22. ;;; 作者信息8 [  [3 |, l; m5 [1 T
  23. (def-room about-room
    $ x' B! q4 i2 D1 ?
  24.   "作者:redraiment
    ! G6 I9 u! g2 p( T+ i2 e9 y
  25. 微博:http://weibo.com/redraiment
    6 k% i) v) x# k+ P, C
  26. 有任何建议,欢迎在微博或微信上联系redraiment。$ w2 W2 z$ e8 C+ f+ X$ _3 s
  27. 请输入任意数字返回游戏大厅。"
    ! y, [3 f1 s8 `
  28.   (t living-room))0 y; G( Q5 `9 P: Q$ F

  29. & u8 `  q+ @, I6 o0 ?  m* B) ^5 o
  30. ;;; 教程
    ! U3 i9 t. n* {3 T0 _# [
  31. (defvar *wechat-5x5-tutorial-rooms* 0" g3 |7 e' J7 Y- ]6 v  B
  32.   "The number of tutorial rooms")5 g+ v+ u% V) O( h% `5 f, T
  33. : B! W  E' r  I0 y- R: i1 d
  34. ;;; 简化教程的定义# _4 n( G- S# A' C. L7 ^
  35. (defun string-last-line (content)2 T$ `5 l5 M- s: q* J; f
  36.   "多行内容组成的字符串中的最后一行"
    + h; S4 w- D/ r" @+ c/ [
  37.   (with-temp-buffer% K7 |% S" L* x* {; a' [+ D
  38.     (insert content); j; l5 E5 S9 x8 b
  39.     (buffer-substring (line-beginning-position)1 g$ L% G% F. j  m  s
  40.                       (point-max))))7 t3 d$ _5 n. \' {( N9 t) v

  41. ! {6 M6 j. Q1 b! G
  42. (defun def-tutorial-room (prompt)0 m2 y3 [; G: b7 y% T0 X0 s9 c9 T
  43.   "根据提示语自动生成教程房间。+ H9 S, C& l0 h1 `, @3 N

  44. 0 p0 u- P1 h0 C$ H9 n  C+ U
  45. 1. 提取最后一行作为问题;& n2 i' {' a0 q# N- S
  46. 2. 分析问题,获取期望用户输入的内容;
    3 l) W; f1 f8 i% D9 n
  47. 3. 定义教程房间和重复提问房间。"
    6 p  h! k% B8 n
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))! [+ |) I3 Z. @9 t4 v/ D8 W8 o
  49.          (repeat-room (concat room-name "-repeat"))  U- k: V$ \5 Y# x% T: ]- B# t
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    1 N# S# e, n5 |8 l8 Q. ^
  51.          (question (string-last-line prompt))
    5 [6 Z! M/ ^4 A& i$ ~
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)& Z" T( ~4 Q1 m; ?6 o7 t: q
  53.                      (match-string 1 question)))/ k  v4 V. i8 M6 K1 `
  54.          (doors (if except
    6 r/ I; G2 g# s4 h
  55.                     `((,except ,(intern next-room))
    5 Q- z/ b/ a' _! [3 X/ Z
  56.                       ("q" living-room)" |  g  |9 Q$ e  S% V) l
  57.                       ("Q" living-room)+ I" t/ }: o2 n8 H0 ^2 s+ H4 k2 |2 X
  58.                       (t ,(intern repeat-room)))
    4 X" K! B9 D, u+ _1 n) s, ~
  59.                   '((t living-room)))))
    / i$ X$ [- w! U9 I
  60.     (def-room-raw (intern room-name) prompt doors)
    , k. [) P$ c/ E( v7 a; ^3 F
  61.     (def-room-raw (intern repeat-room) question doors)))$ Z9 J7 l* P5 A: @6 B
  62. 9 ^) G! b' Q' I# S; t( C& T
  63. (defun def-tutorial (&rest prompts)& x# ^/ b' [% s% w1 @' \/ r
  64.   "批量生成教程房间。"
    0 Z9 S2 O( y4 s4 ~; W, D* ]# y
  65.   (dolist (prompt prompts)
    $ g  m  j* v1 R. \8 _; _) ]% g" \
  66.     (def-tutorial-room prompt)))/ g; ?# k% p% s# A: L
  67. ! D2 f8 E# Q- L. @! C2 W  ?
  68. (def-tutorial
    9 a' O/ z4 L( L' b: E" X
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。6 z. G+ F- [0 j* ?  p( L, C( d
  70. 1. 教程
    : n1 W0 c6 _7 P8 f7 k. u6 t0 D2 Y
  71. 2. 入门(3x3)
    ( \9 I3 Y4 K5 r9 |/ p# Z
  72. 3. 初级(5x5)
    ' D3 ?- r( T) L9 g2 t
  73. 4. 中级(7x7)
    0 R! E" s  J+ i1 q. A, x
  74. 5. 高级(9x9)
    , V, D# q8 [! k* ?
  75. 0. 关于作者0 _5 k& l2 R8 M( o
  76. 请选择1-5开始新游戏:$ g  c5 c% Q; |1 A' E- X/ y
  77. 您现在正在游戏大厅里。
    / z* }: c3 b; I" n6 c( U6 ^
  78. 请输入“2”进入入门级房间"
    7 ^, z$ _$ o% a/ J
  79.   "  ①②③
    % t9 F; _. `3 |& s9 d( ?
  80. 1 ■ 1 r) d: v% W, M9 e6 E
  81. 2■■■2 i8 R+ U/ Y1 l8 k3 A
  82. 3 ■ 8 E: \  ?, p/ ~
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!) Y- J% S2 L3 H( F; N
  84. 请输入“22”来关闭第2行第2列的窗户。"# a: a1 D/ G$ u1 f; C9 x; i
  85.   "  ①②③
    5 a3 T% D6 u* j6 O+ s! n, G
  86. 1   
    5 t& q1 `0 h$ {4 K$ P& k4 O$ M( ^4 G
  87. 2   7 g7 O- D$ v7 r3 ~6 O  H; @  l
  88. 3   
    ( W2 w( ^) d; b
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    & e' ^0 H$ @1 _) i" o+ U" @
  90. 请输入“11”,试着开启左上角的窗户。"
    + X$ r9 F& k4 N6 y) O* U
  91.   "  ①②③2 ~2 A- p( I0 [1 w) I: V( t6 G9 ~* d
  92. 1■■ 
    7 ^' O% ]& v- M- B2 h
  93. 2■  9 F4 }& [! A6 {! c
  94. 3   
    $ D& {6 u( t. X" \  u
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    " y  W+ W2 e9 k
  96. 请输入“13”开启右上角的窗户。"
    2 U7 Z$ B5 M0 [! [
  97.   "  ①②③
    & l: ]! S) F5 \8 E' Q8 I$ B: M
  98. 1■ ■. C0 l4 s) c. F
  99. 2■ ■
      \( V0 p. U% n! V. p
  100. 3   
    & _  O: b" C7 y8 d( X3 c
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。2 T7 J) q7 u1 j, i
  102. 请输入“31”开启左下角的窗户。"
    ( L8 K+ k, D; L1 ~4 A  P( j2 O( P
  103.   "  ①②③
    ' E& Q8 e' u" R  J/ x
  104. 1■ ■
    6 H* v0 n7 ^) q2 c
  105. 2  ■, I0 x$ Z0 Z4 F, P% C
  106. 3■■ 
    2 A1 H; j! v( `1 }
  107. 此时,总共有5扇窗户被开启了。- m5 Y6 I: L% v# v5 G2 ]
  108. 请输入“33”开启右下角的窗户。"
    6 a& X* M: P: r1 w
  109.   "  ①②③
    # \$ h2 W3 o6 @% F. M% s
  110. 1■ ■& a5 F2 Y; C% r# I# |
  111. 2   : S& Z9 p* N$ N' v+ S. I: x/ M
  112. 3■ ■
    " ?, ~( A7 g0 _( G" R* r) z5 {
  113. 现在,只有四个角落的窗户被打开。
    . \- }# S; F5 x5 C8 _& G. Q3 S; G: ]
  114. 请输入“22”完成最后一击!"
    ; W' w* j7 |7 Z3 u  Y
  115.   "  ①②③& l& z9 k; l1 q' x+ J) z1 Y4 u, K; h
  116. 1■■■* }& S# N$ t- j
  117. 2■■■
    , J9 p- R& o8 z! w( u8 N
  118. 3■■■
    $ X# y) L) N. a4 [9 R
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")1 A& h; Z! N0 @6 L  `

  120. ! H( {$ ~. ^- E+ Z1 O1 F" E1 G; W
  121. ;;; 棋盘
    + j: ^) \; b7 ]3 {& M
  122. (defconst *wechat-5x5-white-chess* 12288
    3 y" e5 I# j( p( D8 {5 Q
  123.   " ")% d& x. M) G- P+ K& ]( H( N8 {% m

  124. - ^. C6 j1 T! I
  125. (defconst *wechat-5x5-black-chess* 9632/ A" b: Z$ o8 v1 n
  126.   "■")
    1 G5 }( C. i& ^1 n6 U

  127. # ?+ M4 Y, ~1 }( }
  128. (defmacro with-board (&rest body)
    , E' G3 I/ Z6 i2 Y
  129.   `(with-temp-buffer# T( P# f) b3 v) k6 j. y
  130.      (unwind-protect
    ; J+ A2 @4 D4 S  q: t  u
  131.          (progn/ I" i+ k2 {& z" h$ Q0 B7 k. M$ X
  132.            (if (session "board")
    - W* a1 O- b/ n' \& |# U7 @
  133.                (insert (session "board"))), t  w1 ^6 q! h; x" W: O
  134.            ,@body)
    8 F! C+ u+ U3 T" X+ U6 V, Z! O! d/ @/ E
  135.        (session "board" (buffer-string)))))+ p) D$ J' \# E! {

  136. ( ^+ _. J5 o- T
  137. (defun board-init (size)8 |4 _% o" Y2 v2 F) X
  138.   (session "size" size)4 X- p4 m. [# ]. S, U1 h: o6 Z
  139.   (session "step" 0)6 D+ S- D# d% |/ M6 i+ ~' N, {
  140.   (erase-buffer)
    ; c. y; ?& B& p: h0 y* t8 @7 }
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    : h% \) l" G& B: @
  142.   (dotimes (row size)& q% A3 b0 u8 v
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))1 A3 o' X! p- e3 Y; V

  144. ( E- q0 z0 Y9 I+ o: a
  145. (defun board-contains-p (y x)& |, |7 u# S" V+ Q: `
  146.   (let ((size (session "size")))( L$ q* A& K6 v
  147.     (and (<= 1 y) (<= y size)
    & ]9 p; X1 f/ |
  148.          (<= 1 x) (<= x size))))' G- H5 E$ g* Q

  149. . c. j5 e2 S2 n  s5 b
  150. (defun board-toggle (y x)+ j2 {6 f1 f& M
  151.   (when (board-contains-p y x)
    1 r" n- g5 P3 R1 h0 V. [
  152.     (goto-line (1+ y))
    - z" c  b* X( q
  153.     (beginning-of-line)" v% o0 R1 ~+ s4 z+ {* }9 |
  154.     (forward-char x)" ~( W, v( j0 n' Y. D
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))% F  u* @7 Z' |' p
  156.                 *wechat-5x5-black-chess*
    5 ~+ Y- h8 Q7 {
  157.               *wechat-5x5-white-chess*))! m1 z1 ]. g" ^/ _8 f; s
  158.     (delete-char 1)))0 b- [# w1 J; D+ h

  159. ( O0 a& g) i6 o; K  }* L( I) V
  160. (defun board-put (y x)
    ) k! C, P+ \" P5 E2 Y2 s
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))+ v6 @8 ~9 I: A2 _
  162.     (board-toggle (+ y (first dir))
    ; g3 I; m$ p$ f1 D
  163.                   (+ x (second dir)))))
    $ }: V  }$ B5 }

  164. # Y# \) A: ~1 h8 x7 ^; ^
  165. (defun game-over-p ()' w% h5 \- ~0 `# D$ k# ]
  166.   (beginning-of-buffer)& n+ P4 |+ g$ D* U: `7 N2 P! h
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))- Z0 Q. m4 G) y$ Z$ j

  168. / E6 O6 e% S1 ~. i) P, ~
  169. (defun board-show (). p* k5 f+ I* J& |( |/ D
  170.   (with-board
    $ O3 y! I" L5 F+ B0 \5 U9 T
  171.    (concat (buffer-string)* J3 H. c  r( [1 ?5 F
  172.            (if (game-over-p)
    & `0 D1 O& i+ I' K
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))/ M; ~9 S/ M* n* S
  174.             (format "第%d步" (1+ (session "step")))))))
    1 S, a* \$ B0 m4 O; _- v
  175. ' v2 S# g" A1 X& i: _, z
  176. (defun board-position-parse (cmd)
    3 S2 f. l( h! m  q; }9 ?/ b9 ~
  177.   (if (= (length cmd) 2)1 |: \& P- X( D- x7 Y  w2 `! J
  178.       (list (string-to-int (substring cmd 0 1))  g4 p$ s8 r3 K( j5 N, r7 P
  179.             (string-to-int (substring cmd 1 2)))$ P! i4 k- u9 Q/ i" V
  180.     '(0 0)))& {# B+ r7 C3 f2 ]& X
  181. ' u1 M7 A9 M& u! K6 x
  182. ;;; 游戏房间
    ( h( A+ J9 f3 D& Q; y
  183. (defun game-room-init (cmd)
    * V) M* G. M( e, }# B' g+ B
  184.   (let* ((middle (string-to-int cmd))
    ! j+ F  ~! b  V; |& ?& }' b, M
  185.          (size (1- (* 2 middle))))4 T$ h$ j  P( [- n5 @# \9 }1 M( X
  186.     (with-board$ A; U& O9 C* s( U
  187.      (board-init size), c& o. G2 n' H' O( d
  188.      (board-put middle middle)))
    ! N7 g$ V$ s, C. g( p
  189.   'game-room)3 N$ A$ V5 Q* J6 T$ I7 W, Y

  190. / E7 e" F  C9 V) v1 `! D, }
  191. (def-room game-room( h8 l4 L" a/ t* a% V' p
  192.   #'board-show
      Y6 {8 ?& m2 f' `$ x9 K+ _0 O, g
  193.   (t (lambda (cmd)
    ; g- e) G4 H, I- C: f5 W
  194.          (with-board
    ! _( H: \5 Q  R
  195.           (if (game-over-p)6 H& G' d! i1 E: J
  196.               'living-room
    / A/ g" {8 A( u8 O& u
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    1 @' r6 g' q1 p* i
  198.               (when (board-contains-p y x)& H2 T* c9 B9 R' G& g# S$ E. e
  199.                 (board-toggle y x)
    : S1 y; ^- X( h0 u9 [( `, U, `
  200.                 (session "step" (1+ (session "step"))))
    . a# v: y, T$ J2 o2 X3 u
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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