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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。8 j5 M$ P9 a0 z' O
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. / [% G2 l, P& J% S4 S4 g2 \
  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;">;; 定义新的游戏地图* W; j4 S# n# ~0 n* V9 Y# _  k" e
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL. J  l, Y2 ]$ p/ R7 \$ K
  4.   'tutorial-room-0)                     ; 默认的入口, \7 R$ o1 g1 [! l

  5. / s% l$ y1 D: b- u
  6. ;;; 游戏大厅6 }' F' Q7 A$ A- ]" B) n7 m
  7. (def-room living-room
    ( ~" m8 h9 d/ g/ e( g8 K
  8.   ;; 进入该房间后的提示语, J% u7 }5 G+ S8 L7 Y8 Z
  9.   "1. 教程
    3 O$ S4 D4 \- c) X
  10. 2. 入门(3x3)+ X+ b# T9 }/ Q  H. |) {
  11. 3. 初级(5x5)
    7 f1 J: F  f4 }; h
  12. 4. 中级(7x7)4 ?% @$ W# k9 D) ?+ P* M! R6 _+ X
  13. 5. 高级(9x9)7 R7 ~7 u8 Y) v! [9 p# L/ ?6 _) D; V) |
  14. 0. 关于作者
    + D( j' s* m- c  R' |' O- b
  15. 请选择1-5开始新游戏:"+ ^2 _, x0 m* m8 H7 }6 z% I, z" [
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名: J0 ~9 t/ g* X- [3 j1 z
  17.   ("1" tutorial-room-0)
    + H) s/ }6 [0 Z! b
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配# D/ F% {" O: E* N' @
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    # Y6 t# n# Y( p9 S# z7 g& [
  20.   (t living-room))                      ; 如果条件为t,为永真
    2 U. b" y9 Z, B* u8 D
  21. 2 V) E0 N6 [3 @3 Y2 ?
  22. ;;; 作者信息8 o! ]8 Q( U8 l
  23. (def-room about-room2 r4 a: ^" s% T# n6 h
  24.   "作者:redraiment
    7 m6 u+ k$ M6 l
  25. 微博:http://weibo.com/redraiment
    - h. w9 b+ d: e. ?" m
  26. 有任何建议,欢迎在微博或微信上联系redraiment。) z9 ?- s, p! j9 _
  27. 请输入任意数字返回游戏大厅。"% ^- p3 o4 y' a- M1 K8 g" k
  28.   (t living-room))
    , H# E( {) s" N- C' x
  29. 5 R! r0 R( S$ @
  30. ;;; 教程
    " Y/ y; n. w% [( f1 W. {, ~6 ]) q
  31. (defvar *wechat-5x5-tutorial-rooms* 05 T2 W7 p' ~' \. {2 j7 b7 q2 E. ^# h
  32.   "The number of tutorial rooms")
    + h; Y% Z* U0 o# @- I, v5 O

  33. % c8 f: ]* c+ Q5 j8 m$ d1 x
  34. ;;; 简化教程的定义5 V% i. Z( R; V, K4 [3 z
  35. (defun string-last-line (content)
    ) W9 y. O% \& l) t% l
  36.   "多行内容组成的字符串中的最后一行"
      {+ N; H& S+ L/ Q; k; \  P6 i
  37.   (with-temp-buffer
    ) N5 \) y- l8 G, A
  38.     (insert content). P- G6 ^3 p! F2 \7 O+ A9 {0 w# j4 [
  39.     (buffer-substring (line-beginning-position)6 J# b+ q9 S  o! ^; k, r
  40.                       (point-max))))9 u3 _$ b2 l" b6 ]0 h4 w+ T

  41. ( E# k3 S7 c7 }0 X( q9 p3 F
  42. (defun def-tutorial-room (prompt)
    ; y+ L0 A, b# p$ x
  43.   "根据提示语自动生成教程房间。
    6 s2 Z5 }  a; \# p& Q
  44. 9 ?; w, g3 o  M" k: k3 i
  45. 1. 提取最后一行作为问题;/ q! L4 B" c7 ?% k, }+ o
  46. 2. 分析问题,获取期望用户输入的内容;
    ) `* q2 t% N  h& G: s
  47. 3. 定义教程房间和重复提问房间。"* K3 j: t( f/ e! S% C, S) E
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    6 s) f% r0 Z6 r) r! G! f( }
  49.          (repeat-room (concat room-name "-repeat"))+ u) ~: j+ V' T
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))# |( G9 Z% M$ M% `
  51.          (question (string-last-line prompt))
    , M* j6 S2 P6 C: l+ I) r
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)6 Z! l  {2 Q+ A
  53.                      (match-string 1 question)))
    4 ]+ c! r. }9 p: a0 k
  54.          (doors (if except0 S% V! \' a! o) j
  55.                     `((,except ,(intern next-room))
    * J- J1 X& k5 |+ A" V
  56.                       ("q" living-room)
    4 h+ H& M- [$ F5 `# i5 J
  57.                       ("Q" living-room)7 ]* u# \+ l. y; f& `2 f8 c# e$ {
  58.                       (t ,(intern repeat-room))); w+ x& k+ G; E/ n$ }( o" r5 e
  59.                   '((t living-room)))))
    1 F+ u6 ~; V: K! ]4 q
  60.     (def-room-raw (intern room-name) prompt doors). q( x8 j1 R1 X3 q* ]
  61.     (def-room-raw (intern repeat-room) question doors)))! ?7 ~& v, x8 ?5 `
  62. ) a, X* t) e6 ~6 ?- G
  63. (defun def-tutorial (&rest prompts)
    - T& f8 c* c" l2 L: t
  64.   "批量生成教程房间。"6 N. ]- {$ N. h; r& p2 W
  65.   (dolist (prompt prompts)9 o4 B- F0 u- N' b7 `3 M0 H
  66.     (def-tutorial-room prompt)))
    6 r, ~: }4 h9 X1 x2 z

  67. ' C9 u; n2 ^3 G$ _* X  i( B
  68. (def-tutorial* }5 a6 F" }; O5 C0 i/ t
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
      G/ I" a0 `7 J& L
  70. 1. 教程
    + D; d# T+ ]  o# `
  71. 2. 入门(3x3)
    * l$ W- Z" m2 H# Z) _  Y" ]
  72. 3. 初级(5x5)
    * H1 s2 V  r1 u# L7 ]7 J
  73. 4. 中级(7x7)
    5 v0 G7 V, p3 v3 A
  74. 5. 高级(9x9), l- k* {8 ^  I
  75. 0. 关于作者& R; c4 P5 N& K2 W+ n9 f
  76. 请选择1-5开始新游戏:; [/ l  T* A# H* _6 M
  77. 您现在正在游戏大厅里。
    / K( }5 O6 }$ d, Q6 w# ~. C% E
  78. 请输入“2”进入入门级房间"+ v5 D) I0 x. G$ D
  79.   "  ①②③
    ' H( Q' Q8 s5 n- ^- S
  80. 1 ■ % l6 B3 q/ ]1 V9 u1 f* \
  81. 2■■■4 m5 K- n! W8 ?1 K
  82. 3 ■ 
    ! C4 V3 E5 m& D9 p0 j
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    : [  C* O5 D# _6 \8 A) N
  84. 请输入“22”来关闭第2行第2列的窗户。"
    % e( P' U  c) Z: W8 R
  85.   "  ①②③8 @' r! T7 [5 H' t; j
  86. 1   
    8 P, r/ h% X/ x3 A" E
  87. 2   
    % ]4 P0 h  F+ ]  E1 j
  88. 3   
    " t5 l* w+ M3 E
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    ) X$ \4 X! [) Q" {2 @: X4 O2 c& A
  90. 请输入“11”,试着开启左上角的窗户。"
    7 @1 c: _4 v' z+ N3 i
  91.   "  ①②③
    : {/ @1 \! |$ I2 S6 {4 L2 y: U
  92. 1■■ & L, y  t1 T) n" p
  93. 2■  ' w* j7 `/ a6 Y
  94. 3   & X  L8 o: v3 u  z* c( K
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。7 A( E, u& `6 e4 v& M1 s' |. _
  96. 请输入“13”开启右上角的窗户。"
    ' H- d3 f. m! l  v
  97.   "  ①②③( w: y% s  |! z  G. b
  98. 1■ ■0 }) w; H& u2 ?/ d0 `" q- i
  99. 2■ ■
    1 V/ E6 B4 N- p5 S6 ?* w4 }
  100. 3     A$ A, I, D* u% N
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    ! {2 K" t2 `' m- r3 ~+ p" _0 }
  102. 请输入“31”开启左下角的窗户。"
    - U8 e  ?8 u6 O; D/ v  c% a: j
  103.   "  ①②③8 X" i! b6 p, b7 D6 ^
  104. 1■ ■. H& x9 H* S* n' d3 ]
  105. 2  ■
    & y  T! ]/ C3 \" v7 H% T* Z( r
  106. 3■■ * P" t0 c3 U: C5 @+ v5 c- Y
  107. 此时,总共有5扇窗户被开启了。5 x& N- ^. \4 X$ R- S& I1 d
  108. 请输入“33”开启右下角的窗户。"
    ; N* T8 t# S- |6 s4 R  l
  109.   "  ①②③
    9 i5 _. }0 n3 N9 l
  110. 1■ ■
    & r1 e% z; H/ H3 n; I
  111. 2   5 ^2 b0 M/ a5 ?, R; a
  112. 3■ ■9 z7 V' W3 v5 u. f* J- M7 ]
  113. 现在,只有四个角落的窗户被打开。! x; N" |$ C9 G( S/ F( ]
  114. 请输入“22”完成最后一击!"7 l  ]2 p3 e5 w. F$ s& w* k+ G+ I
  115.   "  ①②③
    6 k! U5 o( p. n! @: n+ ~3 t8 d& A& l
  116. 1■■■: f1 y# e0 n3 c. k+ |3 S
  117. 2■■■
    ; L+ \+ ]9 b! o+ @
  118. 3■■■* d+ u3 y" x; C0 X7 z) J
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")7 C8 m5 s; j4 x3 Y, X

  120. 3 O; F8 O# {# P" m' P
  121. ;;; 棋盘
    0 p( Z- h2 u+ \/ E, \, c- b$ z# x) @1 B
  122. (defconst *wechat-5x5-white-chess* 12288
    # [- V# T+ a3 z4 `- \
  123.   " ")- D3 a; X+ O1 U+ C. T
  124. 2 x. m! `  p5 N' \# o) \/ s. c" J
  125. (defconst *wechat-5x5-black-chess* 9632
    ) W9 G" N$ ^2 i$ ^5 \  b: x7 o
  126.   "■")) {( B! C6 u( B2 e3 J% b
  127. # U: g$ ?% R& w6 ^( v
  128. (defmacro with-board (&rest body)! K: K6 u$ a% L% M$ N% @+ n, w
  129.   `(with-temp-buffer& |; S" ], z) U7 {) h
  130.      (unwind-protect* ?! C9 ]1 @. H$ f! f
  131.          (progn
    ! A6 H, p5 g. P6 }" w( X
  132.            (if (session "board")* H+ F, j, @6 f3 t) |
  133.                (insert (session "board")))0 N3 N( p- |, a$ J  m7 r
  134.            ,@body)
    ' _9 }( S( c+ \8 W& m' \
  135.        (session "board" (buffer-string)))))$ a& d+ m) ^% B- Q( O

  136. - ^" O% d4 D$ h! t
  137. (defun board-init (size)
    5 v! X5 J3 @& T" [0 l) P
  138.   (session "size" size)+ H5 E' Q+ N5 m4 ^1 \
  139.   (session "step" 0)( N8 H8 V. w- Y7 j5 D- A& W4 @: E
  140.   (erase-buffer)
    - _& C; {7 x- z& a, s$ r* {
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    ; b" n* d; F2 H
  142.   (dotimes (row size); }1 O6 \6 {. }/ _8 w4 K
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    5 d3 v& z3 C/ n' v! ]

  144. . R" o5 {/ o7 Q9 _  R) @& M; O6 c
  145. (defun board-contains-p (y x)
    ) R) g" j2 N( ]9 L& Q
  146.   (let ((size (session "size")))9 p, ?- V: c1 f0 a( O7 ^
  147.     (and (<= 1 y) (<= y size)
    ; O! r! |4 n; e7 @: l) H; l: H
  148.          (<= 1 x) (<= x size)))), W# v" `, Z0 p/ \7 Q
  149. 8 p2 J1 c; O1 a. W
  150. (defun board-toggle (y x)
    3 m4 r0 m7 R7 C% ~( u
  151.   (when (board-contains-p y x)
    $ _2 X! b* t2 J! r& ^
  152.     (goto-line (1+ y)): d  [  B0 t& ^$ B9 N: M# R$ i
  153.     (beginning-of-line)
    5 S  Q5 t1 g2 M" L  b2 Q
  154.     (forward-char x)# W6 B+ X1 m- [  T( ~6 K
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    # p3 C' T- }9 U
  156.                 *wechat-5x5-black-chess*
    % _: s- |4 B9 w
  157.               *wechat-5x5-white-chess*))5 y; e, w; w8 D8 G/ b# ~: H
  158.     (delete-char 1)))  J) s: J8 [1 f2 U+ a" D

  159. . S* O/ L# T  r" h9 m
  160. (defun board-put (y x)
    9 O, D6 b( @, `9 P
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    . e0 X, Y: Q7 g& w. D
  162.     (board-toggle (+ y (first dir))  ^+ Z! K, L4 R& z& N7 s1 H1 [
  163.                   (+ x (second dir)))))
    + U1 T! N+ n* y& S
  164. # \- F2 o* V# S: w( @
  165. (defun game-over-p ()
    1 b5 e* h6 L) @- }( n* v  y' @: X
  166.   (beginning-of-buffer)0 E, v2 t4 W  V; w
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    9 n6 S) ~7 V% l1 a* Q5 i

  168. 3 Q" f5 e7 p& G8 _( L3 j6 x3 t0 p0 {2 ]
  169. (defun board-show (): O9 ~% P, ?7 z
  170.   (with-board
    2 T9 r# j' f& f+ T% _
  171.    (concat (buffer-string)
      n( H) `% u5 ^0 h8 V
  172.            (if (game-over-p)7 O2 M- @' \( Q/ S0 Z' |
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))! C% m, G" [9 M4 g/ j, w
  174.             (format "第%d步" (1+ (session "step")))))))
    ' r0 \) N: }" y9 `
  175. 4 n. P' V7 [- b: Z- d  q0 N
  176. (defun board-position-parse (cmd)
    & n+ ^8 J8 O7 D, y; G+ t
  177.   (if (= (length cmd) 2)
    * v+ x( C2 [8 y. h2 G# n+ d7 e! }
  178.       (list (string-to-int (substring cmd 0 1))0 p; v- K  ]8 H4 s3 U
  179.             (string-to-int (substring cmd 1 2)))2 T1 H7 f; E, J5 A# j7 J  n
  180.     '(0 0)))6 B+ I- H9 I8 X# p" O

  181. " g! ?& u" r( g* [* Y9 G; o1 T
  182. ;;; 游戏房间  _2 j0 U) D9 m7 G# q) N
  183. (defun game-room-init (cmd)2 C! I) R( A& t% P+ m& ~
  184.   (let* ((middle (string-to-int cmd))
    * Z3 q) _5 k3 i) f4 e( i3 Y7 j
  185.          (size (1- (* 2 middle)))): d( w4 ~$ ]6 K3 q/ Q8 ^* W: o5 \
  186.     (with-board
    4 N* j) M. h& {, _
  187.      (board-init size)5 w2 I0 m$ Q! j0 ^
  188.      (board-put middle middle)))
    ! |, `0 q9 @& j- H
  189.   'game-room)
    0 I5 M0 y7 z8 G' e) s$ ]( \& N2 k5 b
  190. $ x' O+ ^. v, N% d) y
  191. (def-room game-room, P+ ?5 \6 S/ F$ L( O
  192.   #'board-show4 e1 }( q9 j+ t% d2 `8 `$ I
  193.   (t (lambda (cmd)# o+ U  _) e$ @* F  i% U' a% G( }9 v
  194.          (with-board
    1 Q7 s7 Y3 a$ p- O% |9 G3 Z1 [. q
  195.           (if (game-over-p), v: G2 @) x2 r0 j: P
  196.               'living-room& W% D; R' A9 V% ?  C
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    " a4 z/ `9 h% `2 D" B+ p& \4 ~
  198.               (when (board-contains-p y x)% {7 _3 D2 A- k' K; U& S  [
  199.                 (board-toggle y x)
    2 K4 ?0 A) G) c% V4 [% e4 x
  200.                 (session "step" (1+ (session "step"))))
    1 g$ H- }  q5 e& a( G
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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