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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
  l# _: Z9 L2 @  W( }' ?) J/ {+ G借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. . m. Z7 T) K. D- Q  r3 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;">;; 定义新的游戏地图  a1 C% v/ i  A
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL/ h. `% i- g. |" ^3 ?/ G( |
  4.   'tutorial-room-0)                     ; 默认的入口$ a" O7 g) T" T

  5. ) S- B1 Q/ w. P2 {8 w
  6. ;;; 游戏大厅
    . Z# U4 a9 Y% F. T8 m
  7. (def-room living-room4 m' ~4 n6 b3 t" N6 q+ l5 @
  8.   ;; 进入该房间后的提示语% O7 Y+ J; a9 w$ q
  9.   "1. 教程
    . v' O# I+ x7 L  D' H
  10. 2. 入门(3x3)" }5 ]. R- F( L( Y- l
  11. 3. 初级(5x5)
    % q' S5 c- \- r
  12. 4. 中级(7x7)) ~# L3 M! c0 j1 H' P
  13. 5. 高级(9x9)
    5 A3 I& t: J* `
  14. 0. 关于作者7 M- }0 m8 I, o1 W! C
  15. 请选择1-5开始新游戏:"- u; o& ^4 T( C2 A
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    # M: I3 G  r( j
  17.   ("1" tutorial-room-0)
    ; ]0 S0 j3 x  o% X+ Z
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配" k, C& l" O1 p
  19.                                         ; 相应的返回也可以为函数,动态返回房间名7 T3 ]  e8 o: V6 O1 I
  20.   (t living-room))                      ; 如果条件为t,为永真/ l! }9 v! h! v1 I8 L, H' M9 ~

  21. # i2 R8 c# s. o2 g
  22. ;;; 作者信息  c& H8 a! T7 K' P1 y& F) g2 j
  23. (def-room about-room5 V& U! h3 w/ {
  24.   "作者:redraiment6 b! x' _# F7 q% j
  25. 微博:http://weibo.com/redraiment4 X0 K+ l4 L0 o8 `/ x
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    4 x3 C4 @( U7 l0 I: h
  27. 请输入任意数字返回游戏大厅。", Z* s( I* H( _. a
  28.   (t living-room))1 G) }; T+ r- L) m2 i  X

  29. ! f; ^7 c  ]- g- A" w
  30. ;;; 教程
    * y9 t" t+ \9 v# d% |' p7 {
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    . a; ~: @9 R7 u' x# e
  32.   "The number of tutorial rooms"); h+ C+ x4 h0 V" X

  33. : A! [5 c" x$ z6 G; R
  34. ;;; 简化教程的定义6 v9 _: n0 b, |4 n2 V/ v' m
  35. (defun string-last-line (content)* u/ c0 [# V% m
  36.   "多行内容组成的字符串中的最后一行"
    $ _( M8 Q! `1 g! m( w5 E$ Y5 Z& p: b
  37.   (with-temp-buffer) h8 S* C# |( `0 [6 y2 ]) P
  38.     (insert content)/ {, W! h  N3 V" c% L
  39.     (buffer-substring (line-beginning-position)( N. r9 H' V% ], W% X- I
  40.                       (point-max))))
    " n2 {9 f; s3 A, F' T/ k

  41. ( ~! U" v# l# ?  [" r8 e
  42. (defun def-tutorial-room (prompt)
    & J9 q9 N! V, j! \3 N( A# X
  43.   "根据提示语自动生成教程房间。" {( ~6 c. Y& L% c" h
  44. ! i- n4 b; f$ l
  45. 1. 提取最后一行作为问题;" W* N+ h2 t5 D; k1 X6 H/ `( @+ U
  46. 2. 分析问题,获取期望用户输入的内容;7 ^' r9 p: y& ]/ A) M
  47. 3. 定义教程房间和重复提问房间。"
    ; d$ H0 R! M7 G* q! h2 R
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))0 M. t, w0 o& i) E) F
  49.          (repeat-room (concat room-name "-repeat"))- }1 E: @3 }) y( [/ w6 q
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))$ ?+ M" C, m3 A. ~+ n4 w: e
  51.          (question (string-last-line prompt))
    & ]( A! J8 H) u6 p
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)2 `7 @- O8 _! R7 {& ~! G! d
  53.                      (match-string 1 question)))
    & w! F8 X' n  N
  54.          (doors (if except
    , f4 h" F9 M( F
  55.                     `((,except ,(intern next-room)): K# ~$ f" N* h% |
  56.                       ("q" living-room)! I2 ~) q! C) p. ]; I
  57.                       ("Q" living-room)
    & |4 S/ e. S4 l
  58.                       (t ,(intern repeat-room)))5 X, h* V; B8 I3 A. R% E
  59.                   '((t living-room)))))$ g  O" _2 D0 {9 r
  60.     (def-room-raw (intern room-name) prompt doors)) b; D# T1 o5 U  Z2 ]
  61.     (def-room-raw (intern repeat-room) question doors)))
    ! a& V  _5 Z: |+ c3 W* U
  62. - O$ R/ I6 x. _7 }" P
  63. (defun def-tutorial (&rest prompts)4 o' O( r1 v2 G6 u. O
  64.   "批量生成教程房间。"
    & K, w# n, E. k" U
  65.   (dolist (prompt prompts)+ w% Z3 p' h0 e4 |6 [
  66.     (def-tutorial-room prompt)))
    $ X/ j# X8 a1 p  y6 N, f/ W/ d
  67. 2 h4 s* D9 j! i! N4 r  r/ u
  68. (def-tutorial+ L8 b' I) [" X" r
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。: Q/ K9 P5 ~2 B2 S5 O( u- O* a
  70. 1. 教程0 |2 L7 B" A0 Y9 w7 M* q: O0 E
  71. 2. 入门(3x3)! W- t* W( L4 E* N
  72. 3. 初级(5x5)
    2 r# N2 {0 M- q
  73. 4. 中级(7x7)  l8 A8 Y0 c6 H( [# C
  74. 5. 高级(9x9)( v. E; v, Q6 q5 d6 i. s- ~
  75. 0. 关于作者. {9 M/ `6 e0 Q$ i
  76. 请选择1-5开始新游戏:. p) B: h8 O  M# l) |# j) o
  77. 您现在正在游戏大厅里。) L+ y. @6 e5 X" h2 h  ~! n2 w
  78. 请输入“2”进入入门级房间"9 H# s5 c) J+ e% l! ~+ [  X
  79.   "  ①②③9 S/ h! ?  k0 \# t) n" [/ q( Y* v
  80. 1 ■ 
    # ^, R" E0 T5 O- Y* _
  81. 2■■■
    ' R  k" p. q+ q% ^) B- b  w
  82. 3 ■ / V  \( p3 ?7 X8 k
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!6 O! d) H& O* L* _: e. N! Y' l
  84. 请输入“22”来关闭第2行第2列的窗户。"  H; {4 G$ |8 }6 o6 z6 F
  85.   "  ①②③
    / c5 n* X: A% M, G) R  u
  86. 1   . L7 c4 y: d) `# i
  87. 2   
    7 u4 V$ t2 `# G
  88. 3   
    + [8 w" R( [" a/ I* p, J
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。! ], U. ^5 e; r
  90. 请输入“11”,试着开启左上角的窗户。"+ N! x1 ^; V/ g
  91.   "  ①②③5 N. [8 B( g  E
  92. 1■■ ' R; D+ |* A( C( l- E1 r% F" H
  93. 2■  / k0 \; V0 x, ^7 g! A$ m1 g2 {) U
  94. 3   : ]% x8 B& D6 x* k- b
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。8 H: ]1 n- S+ ]$ Z) Q) x
  96. 请输入“13”开启右上角的窗户。"- e3 s- W/ B4 y* v) A$ `) y
  97.   "  ①②③( ]) w5 w  K) A* n- g
  98. 1■ ■- [  u, ]8 u7 c' h" i7 c! j
  99. 2■ ■
    % s1 f/ h% o& l6 f4 j/ m6 P
  100. 3   
    5 X( s3 D; h* J) I+ t  S7 n
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    + ]" V/ m& J  b5 X: M
  102. 请输入“31”开启左下角的窗户。"6 @- {$ ]7 J) W
  103.   "  ①②③
    4 q0 }3 n8 X2 x: W: h; z+ ?) [
  104. 1■ ■; P9 x* c0 A* k* h7 X! L$ n
  105. 2  ■5 y( B$ `& ]5 z
  106. 3■■ , U& U/ P  n& O# I/ K3 x( Z
  107. 此时,总共有5扇窗户被开启了。& H% a7 O. ^# i6 I* F  H
  108. 请输入“33”开启右下角的窗户。"
    0 l% N4 v  G# O8 d1 ~" s! T7 k; G
  109.   "  ①②③
    + E' \/ v( x9 t& R2 d5 A. B1 {
  110. 1■ ■, H  u  d8 r# S. Z0 O% {, W3 R
  111. 2   & H& U" P2 Y* d' H+ R% I: U
  112. 3■ ■- e% Y$ {. k1 {+ E) {5 U
  113. 现在,只有四个角落的窗户被打开。: Z/ h% I  ?) A8 [: x5 A9 x& K
  114. 请输入“22”完成最后一击!"
    ) X0 L- g" @- i  X7 X2 a
  115.   "  ①②③% D( n7 w# {+ _% i" [4 u, U2 m
  116. 1■■■
    % b* m' T% B! a
  117. 2■■■/ |: T! Q' u6 d
  118. 3■■■
    5 H4 x# P( l5 y- o
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    / x) z& o' V! q: H
  120. , U( q' d/ @" u2 v
  121. ;;; 棋盘0 p/ V8 ?* S0 K. K7 Q% [
  122. (defconst *wechat-5x5-white-chess* 12288# T/ N  Z# B, n7 z2 Y# w  c
  123.   " "). L1 }3 Y3 b: X1 |

  124. & F+ I3 l4 d5 r( I) @; y
  125. (defconst *wechat-5x5-black-chess* 9632
    3 E& b: I! I* R! Z) ?
  126.   "■")
    ' h5 J% r' t% Q! L2 I

  127. $ T# t  W4 M" x* X6 o
  128. (defmacro with-board (&rest body). T1 v3 G1 E6 c) _' {2 p/ v) P7 l
  129.   `(with-temp-buffer
    - d5 }$ Z% Q( ^/ E. i% A
  130.      (unwind-protect2 C% T1 V1 `3 E2 _
  131.          (progn
      F' k! g5 _  e1 X9 Q( T! Z: o) d# Z
  132.            (if (session "board")1 w! H" M3 Z$ z$ o
  133.                (insert (session "board")))8 T# N( _- O& z9 M9 R, `. i
  134.            ,@body)
    / V/ ?6 {; X% G; v5 ?
  135.        (session "board" (buffer-string)))))
    ( u& o' E/ o  b  `  L  x
  136. : c5 _8 z6 s7 z7 A
  137. (defun board-init (size)2 t2 U( t) G% d, \" t& I
  138.   (session "size" size)$ ?' `1 x' e+ \: |2 }. P& j
  139.   (session "step" 0)
    ! Y$ \4 q4 f. F2 K: |, `
  140.   (erase-buffer)
    $ d# {* P. H2 j3 q+ G* F
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    2 O' o6 [8 e& p
  142.   (dotimes (row size)
    6 n# u5 e& e/ w
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    ! X4 E) M' i1 B% D( Q( B3 ^

  144. 6 `8 U3 E2 V- S( U* q# _
  145. (defun board-contains-p (y x)
    + l8 {/ ], u! T9 e: f+ _
  146.   (let ((size (session "size")))  q7 c, E. K' k, x2 k3 y
  147.     (and (<= 1 y) (<= y size)% ^: O7 s" c$ g' t7 E- _% \
  148.          (<= 1 x) (<= x size))))6 z* ^. S+ i" s1 h0 S
  149. $ y6 j: N3 d8 m
  150. (defun board-toggle (y x)
    * H& l$ o4 F3 B& C
  151.   (when (board-contains-p y x)
    3 c* f! Y! f' i# ^6 k
  152.     (goto-line (1+ y)); x  F( g5 K% d: W: M$ r4 }6 J
  153.     (beginning-of-line)5 w" j# h# H/ n
  154.     (forward-char x)
    & l1 i1 j5 J; v! {
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))$ V2 w' ?9 w7 q4 }
  156.                 *wechat-5x5-black-chess*/ ?8 _9 Q4 u* ?* g; E$ W
  157.               *wechat-5x5-white-chess*))
    / }3 X1 J' N* H( a
  158.     (delete-char 1)))9 R6 I4 z; z0 a" v; {
  159. , a4 T" o* s/ X: i! m  P" \
  160. (defun board-put (y x)4 d, B" W* o2 {
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))# }6 j: _0 N  W
  162.     (board-toggle (+ y (first dir))
    " C1 W6 J; m) C+ z+ [- ~' G
  163.                   (+ x (second dir)))))' m( G' [$ ]6 B8 A8 N. Y/ R6 |

  164. $ ~1 M" u. w* V/ t
  165. (defun game-over-p (); m' v/ b' Q' \  ], w: P1 i5 W
  166.   (beginning-of-buffer)
    4 p0 r( a+ T- p
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
      M0 a8 j7 I& x4 {/ R3 u' s1 I

  168. % \+ P8 c, a+ ]0 j) r
  169. (defun board-show ()
    ' _; h! j& o9 q. x3 b  ^5 H$ ^
  170.   (with-board
    " Y6 T; M! R" \; X4 j4 r) t0 G: [
  171.    (concat (buffer-string)- q2 z$ C0 M4 l
  172.            (if (game-over-p)( E/ q" N! |: Y' j
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    & J5 p+ \1 w# ], n8 Z4 e
  174.             (format "第%d步" (1+ (session "step")))))))% C& R, Z* A" S' M. O0 X8 y
  175. 7 M. X# V! `' e# J1 ~/ s- _
  176. (defun board-position-parse (cmd), n% `/ j' P! y' ]
  177.   (if (= (length cmd) 2)
    ( D2 [3 e8 F' a2 `" k; Z$ V& ?7 S
  178.       (list (string-to-int (substring cmd 0 1))
    9 o3 J* X/ s3 }* ?2 G5 [  |
  179.             (string-to-int (substring cmd 1 2)))
    4 \/ N" W' e% g
  180.     '(0 0)))
    " b& z( ]- p5 n8 C& \& |! W& M
  181. 6 W/ o" E- R; ^" p/ X3 K% s2 J
  182. ;;; 游戏房间
    ) O( I8 S6 p* J  O
  183. (defun game-room-init (cmd)7 \' `0 q: Z/ ^3 |; q' p4 C
  184.   (let* ((middle (string-to-int cmd))
    $ H) c4 g3 j9 S! D  h; |
  185.          (size (1- (* 2 middle))))
    " B" r/ Q4 U. Y' B
  186.     (with-board
    # \1 y5 i$ r6 t1 a$ ^5 k$ a7 W4 |* z
  187.      (board-init size)
    9 D' ?5 d' L4 H" ?6 d2 R5 k
  188.      (board-put middle middle)))# L; k; |! Z, F  z* b5 ]" q8 G/ E
  189.   'game-room)! l# Q5 S% E2 ~; L3 U
  190. $ n  J2 ]2 }; e$ W8 D% _
  191. (def-room game-room
    7 R9 c$ d, ~+ q: h( a( F$ U6 T
  192.   #'board-show
    * `3 D. g2 j6 {8 {& p
  193.   (t (lambda (cmd)+ L( \/ Q( O- R/ u! W
  194.          (with-board1 z# T! K4 K2 J, U( A. ?" j' `
  195.           (if (game-over-p)
    7 `( i: U  ]5 a, h" B
  196.               'living-room
    1 t* Y: H* S. {5 ]4 m( k. |
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    5 v0 J  G6 Z$ b
  198.               (when (board-contains-p y x)
    , T4 D+ D: x& }$ j* i& L  c
  199.                 (board-toggle y x)
    0 B( Y8 R+ U  {; ]  |  R; Z9 Y6 E
  200.                 (session "step" (1+ (session "step"))))3 t; W0 w2 A% {$ h
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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