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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
$ ]+ \+ H/ F1 f4 ~" m借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. ( ?; c3 |) g+ d) B4 Q$ o5 ]# Q
  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 T7 T+ K$ g( }6 Z2 n
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL. p. ?' F4 O# K2 O+ S
  4.   'tutorial-room-0)                     ; 默认的入口
    6 G2 C6 Y' O, o0 R
  5. 6 d& e* R0 ^9 Y, T! ?1 C7 `( u8 e; Q
  6. ;;; 游戏大厅" h' [3 T* A  W
  7. (def-room living-room  Y! G4 \. p8 Q) o* i8 ~2 X$ [
  8.   ;; 进入该房间后的提示语
    " Y* `) O; }# _/ F) t
  9.   "1. 教程: x" v1 i! t+ w$ ~/ |' U" P' m
  10. 2. 入门(3x3)0 L& T/ k- e/ J& C& l/ u
  11. 3. 初级(5x5)
    ' M1 }5 S$ T. o9 R' ?
  12. 4. 中级(7x7)
    2 C4 G8 y& S7 r8 L
  13. 5. 高级(9x9)
    0 A8 f" m4 Z2 t7 V/ I
  14. 0. 关于作者# l9 r* i/ r+ ~9 @; o
  15. 请选择1-5开始新游戏:". b: R( ]% X! [6 n( k
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名! W3 R8 W! _1 n  v1 A7 t
  17.   ("1" tutorial-room-0)
    ( W2 k7 d; P* w' D+ P
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    ' M0 l/ y1 o7 D9 S" k" p
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    ) i2 _! Y2 i; W+ {6 v2 b
  20.   (t living-room))                      ; 如果条件为t,为永真
    3 q' n8 G& j$ s2 U4 S# L4 l+ c, [$ Z
  21. 7 Z1 m$ i# Q& ~1 W# l
  22. ;;; 作者信息
    4 y( g& }. j  K8 u8 a6 b
  23. (def-room about-room1 h' S* @# n$ ^. g, G& B4 O* _
  24.   "作者:redraiment, N: w! g) Y/ t& l9 |! o) O
  25. 微博:http://weibo.com/redraiment: _3 E- S. @! `  l* s
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    ! ~  e3 J+ G& v
  27. 请输入任意数字返回游戏大厅。"
    8 G5 X3 ?$ o* I
  28.   (t living-room))
    : K# v& F! A4 k' C, B/ |; N
  29. ! c; N3 W- e- X& ]0 ^& e! H1 P: {
  30. ;;; 教程, {% ~5 t( {9 ^8 E( }1 y1 w
  31. (defvar *wechat-5x5-tutorial-rooms* 04 x; W5 r( F# T/ N' Q0 P
  32.   "The number of tutorial rooms")
    1 B1 O2 _4 p& Z: S; u0 E

  33. + [$ F2 k/ \2 M9 K9 _' D
  34. ;;; 简化教程的定义2 D2 X7 _7 v8 \! `) g
  35. (defun string-last-line (content)# ~4 q# @9 ]  h+ L" Z. |! F
  36.   "多行内容组成的字符串中的最后一行"
    6 O% C6 V# E. q" f  B  Z' a
  37.   (with-temp-buffer& f/ d( Q5 p! j+ F) a  |
  38.     (insert content)
    * w, c7 _! ~) i1 z/ z7 H
  39.     (buffer-substring (line-beginning-position)& Q) {# j  ^+ [3 @1 {
  40.                       (point-max)))), n7 }! {! f( ^/ J
  41. 8 v7 k8 a4 p, h4 U' b
  42. (defun def-tutorial-room (prompt)& `3 d7 }1 r9 a+ t* u( H
  43.   "根据提示语自动生成教程房间。
    4 f6 o5 [2 q4 _: Q% V1 o
  44. 7 j: q: d$ N+ L, I5 ]) h/ A5 }# n! N
  45. 1. 提取最后一行作为问题;4 K+ @  D& A9 x; Y* m3 |" Z- z
  46. 2. 分析问题,获取期望用户输入的内容;9 D0 `+ y9 J' U' m1 G
  47. 3. 定义教程房间和重复提问房间。"
    ( g+ d- P  v; n( t
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))8 t" Y$ C2 v+ |: g' k
  49.          (repeat-room (concat room-name "-repeat"))
    ) a6 G* J" p4 E. I" S% W! v
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))1 v# F. Y! {# n2 N* Z  c) Y  g
  51.          (question (string-last-line prompt))
    & W! b0 S3 K) q1 N
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    7 ]7 g+ g( ?2 O# b' }
  53.                      (match-string 1 question)))
    ' B4 m/ q: J% r0 z/ [
  54.          (doors (if except
    7 V. i; `; ~! Q( r. [: B; E
  55.                     `((,except ,(intern next-room)): `8 ~. r- q3 I; F1 s9 \
  56.                       ("q" living-room)
    % @* U: A  x3 l4 R" j  O: K+ n
  57.                       ("Q" living-room)& g& Z4 ^8 Z6 l4 U2 V
  58.                       (t ,(intern repeat-room)))1 w1 S  W. W4 n+ C! |
  59.                   '((t living-room)))))
    * ?+ v1 U+ R# H8 q1 z4 R
  60.     (def-room-raw (intern room-name) prompt doors)' {1 U% C! n; w
  61.     (def-room-raw (intern repeat-room) question doors)))
    ) p; X+ A+ i) q* A- s- s
  62. 8 U& a8 m0 A' N7 u6 X
  63. (defun def-tutorial (&rest prompts)  j# _. y5 U7 T, D  _% n
  64.   "批量生成教程房间。"
    ! ^7 H& @' b5 a; D/ |. ], `3 e
  65.   (dolist (prompt prompts)
    " G  w+ c* y  E9 ]4 Z9 l
  66.     (def-tutorial-room prompt)))
    ; @, t5 P8 Z& A. A+ i8 z4 O

  67. # \/ M! F. C2 n' {5 T# q" [* z
  68. (def-tutorial! _8 Y5 p; b" K; U0 }1 |' N
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    ( S( i* _8 o7 j8 s, C! d
  70. 1. 教程
    % I4 d/ A! Y7 _
  71. 2. 入门(3x3)% Y0 k! v: B7 H8 O, Q9 r! S
  72. 3. 初级(5x5)( G+ j2 Z" _& M7 s  E3 p& N8 i6 L
  73. 4. 中级(7x7)
    1 V( I% F( S) e3 \8 V" z# g
  74. 5. 高级(9x9): x$ L$ {) I% h0 V0 P) m1 d
  75. 0. 关于作者) g& i+ R% e+ p
  76. 请选择1-5开始新游戏:
    - b9 u% D3 p+ M
  77. 您现在正在游戏大厅里。
    6 W. E4 e! a" w. R" ?: N2 e0 [
  78. 请输入“2”进入入门级房间"
    * {4 `# E+ U' p
  79.   "  ①②③
    & {4 F# b( I/ b; ^2 m' {' |
  80. 1 ■ ; H! b0 z/ o6 c# X
  81. 2■■■
    " {# z$ ]$ M" T' |4 C; k
  82. 3 ■ , ]/ X8 F8 d! k1 U0 ^
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!8 v3 o3 ]. B% X% b
  84. 请输入“22”来关闭第2行第2列的窗户。"1 m: a: ^3 v0 }
  85.   "  ①②③3 Z, B  i  X4 T. J" Z! c* A
  86. 1   
    ' G+ A3 _. t" P* I) s
  87. 2   : `1 ~, [' R7 d5 K! ]( c6 e
  88. 3   ) S9 _3 q+ Y4 g4 k) h' ~6 T- a  }/ D
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。7 E# B- Q- q; D! C
  90. 请输入“11”,试着开启左上角的窗户。"& ^* W* B7 @( G$ d  p, s6 w
  91.   "  ①②③
    ! O+ [" Z) n. k
  92. 1■■ ; m6 z* k' r% ?+ w: m* a6 |# Z
  93. 2■  
    2 c8 U1 [) _4 m
  94. 3   1 c4 Q4 x! V( ]8 @
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。6 K1 |. V# }2 v1 ?
  96. 请输入“13”开启右上角的窗户。"
    $ z& k7 Y; y% z- ]
  97.   "  ①②③
    0 R& s1 T1 @0 Z' I
  98. 1■ ■
    2 p7 J0 Y$ v) ?4 `3 J6 a* {
  99. 2■ ■
    1 {7 Z. Y5 c- h: `' n7 k7 K
  100. 3   
    ! [0 X: x% |. u. T' n
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。9 C5 h" @# @# V8 y6 i" ?1 y
  102. 请输入“31”开启左下角的窗户。"
    5 A4 R5 h7 c) b
  103.   "  ①②③( ^* Z, j- z( a' ?$ K& v& p
  104. 1■ ■
    6 q) `4 t: i$ |3 s6 v
  105. 2  ■9 e$ S7 N8 ~( u
  106. 3■■ 2 [; ^0 p* Y) W8 A$ j8 {8 @: e
  107. 此时,总共有5扇窗户被开启了。. k' m3 s7 {0 S; @" T. o0 M
  108. 请输入“33”开启右下角的窗户。"0 x4 d0 o2 v. K$ ~* e1 F) X/ n9 N- }
  109.   "  ①②③0 n: d' e& x$ R  X
  110. 1■ ■- O+ x8 w/ r' {$ o+ \  i
  111. 2   . ?7 p0 g$ A5 `' ^* t( G2 B
  112. 3■ ■; j6 K. p# Z$ @' n3 N! n( ~
  113. 现在,只有四个角落的窗户被打开。5 {1 T2 A) F" w) S' m$ J4 s
  114. 请输入“22”完成最后一击!"2 d: t: \' B3 _* w7 i
  115.   "  ①②③
    ! C# u. c. k4 o" b& j
  116. 1■■■
    1 ^. ]+ L0 ~( H) y
  117. 2■■■
    2 ~" Z1 }/ @" \! |
  118. 3■■■$ z( b+ N0 `$ [4 \! x' `
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
      C/ o* L' T% q; |

  120. / x/ q6 z  r2 E1 X
  121. ;;; 棋盘6 a: [* J6 _' \. c( U0 x% B0 w6 t
  122. (defconst *wechat-5x5-white-chess* 12288
    " B/ O1 ]5 E# i9 x8 e0 }2 O' C% L
  123.   " ")
    6 H+ {9 V! Y: N5 `3 ?

  124. : N3 N' R) N1 e& f# `
  125. (defconst *wechat-5x5-black-chess* 9632
    " w3 [8 o" _% N8 j
  126.   "■")* z- `2 X# b, a3 E9 ~) |0 P
  127. - [% P2 d* _8 L8 s/ a
  128. (defmacro with-board (&rest body)
    7 A! Y% a8 h: o; w; ]; d' t+ V$ {
  129.   `(with-temp-buffer
    $ Z: n/ }) E. L7 m( c
  130.      (unwind-protect
    & }+ g: [" k* D
  131.          (progn
      I8 d9 n$ a5 A* k: J3 o
  132.            (if (session "board")
    . T. W+ r0 M# B3 E
  133.                (insert (session "board")))
    " l1 f( F+ r2 u$ ^
  134.            ,@body), y! ]/ @3 D& E" N0 |
  135.        (session "board" (buffer-string)))))( |  E$ O3 u$ R8 p$ d
  136. 3 {! p' m. a; H1 @$ T: r; l
  137. (defun board-init (size)
    * x" I9 I% {1 a7 `. x: X. u
  138.   (session "size" size)& f4 A; e9 T  Q+ d4 s3 J
  139.   (session "step" 0)
    : d2 U" U3 f  x% c& h
  140.   (erase-buffer)1 d0 D: [( F! A  n5 D& N$ C5 s
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))9 t. ?/ s3 k* k% S3 D( i
  142.   (dotimes (row size)
    2 p8 W  t0 P! D* p, c0 B
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))" Z, G! [" T7 _# I# a6 t

  144. ; V# Y% z, Q2 H1 C1 a" H
  145. (defun board-contains-p (y x)
    1 k' z/ g0 ~9 f$ l. L
  146.   (let ((size (session "size")))5 ~+ g$ r$ G/ }8 _0 X# @2 r4 I( e& X7 H
  147.     (and (<= 1 y) (<= y size)+ ]+ q" L! l$ R. Q
  148.          (<= 1 x) (<= x size))))
    . S( o; i4 b8 {- a
  149. 2 b. W) S9 g& A. o% G2 E2 K7 s1 j$ ~
  150. (defun board-toggle (y x)
    $ m8 Y0 V4 @$ r: @* \) N8 q  `3 g) [
  151.   (when (board-contains-p y x)0 \4 C4 @& x# p! Z( S2 T& Y, e; x$ r+ ~
  152.     (goto-line (1+ y))
    ) }; R! A# b; G! s( M. X- E
  153.     (beginning-of-line)) G' q7 S9 ]7 H/ n, F0 G' O& E
  154.     (forward-char x)
    ! Y- A% M* l- w" F2 S
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    " f+ s- \% u1 B5 S* T
  156.                 *wechat-5x5-black-chess*. O! T3 @5 A" V  Y- Q
  157.               *wechat-5x5-white-chess*))  k" k" f) X  j1 U! T0 C3 j
  158.     (delete-char 1)))
    0 _/ ]4 j  L# i% d! K: r5 p
  159. 2 I* {' A1 a; ?3 c' m  T( h
  160. (defun board-put (y x)
      r9 j- Z0 E% D1 y6 y6 S
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))2 k5 Q. e" G% h' X5 Q
  162.     (board-toggle (+ y (first dir))
      O/ F0 z7 e4 Q- \6 {% n
  163.                   (+ x (second dir)))))
    5 r9 s# k4 H' x" v2 ^3 G! e) F

  164. ! j3 c* ?  I! g/ C
  165. (defun game-over-p ()8 J  I& s* T4 ]+ D9 \3 V% U- e
  166.   (beginning-of-buffer)
      M7 x) Z, t6 O
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    ! t- `% S1 ^& F  q7 ]

  168. 8 K& ~: R6 J* q. G
  169. (defun board-show ()4 F5 D9 A" e9 H9 Q8 B
  170.   (with-board
    ( \2 E; j: |) J5 f/ i! p
  171.    (concat (buffer-string)
    ' y& Q2 _# ~! \# E
  172.            (if (game-over-p)2 E- D2 }. ]6 K  L1 F: m) d# M) h
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    4 v3 B8 m5 k. T7 o1 [
  174.             (format "第%d步" (1+ (session "step")))))))6 T5 s- X0 h$ E! A" s/ _% G

  175. * f, \* ?7 f1 ]( Y2 M  D0 C% R0 |
  176. (defun board-position-parse (cmd)
    7 ^9 N1 P6 Z7 D
  177.   (if (= (length cmd) 2)
    & A  _( G% Y' `) ?  m$ ~
  178.       (list (string-to-int (substring cmd 0 1))
    4 w3 y) u3 p1 ~% n
  179.             (string-to-int (substring cmd 1 2)))& y5 e& M& }% ~) \4 r
  180.     '(0 0)))
    9 V& Y  y1 E- U/ E! [
  181. 5 x  Y& m( W4 u- M. [+ p0 ?
  182. ;;; 游戏房间& L8 x4 y- \: I9 H: O/ ?
  183. (defun game-room-init (cmd)
    $ I3 o, ~5 D% l
  184.   (let* ((middle (string-to-int cmd))- n7 g3 W" l8 @
  185.          (size (1- (* 2 middle))))
    8 @) ^: ~8 @1 U/ G8 t6 l' ?
  186.     (with-board7 U" D/ x( i2 [! K' `" K. r
  187.      (board-init size)
      ]* }% _. O3 n$ b* s# N; F
  188.      (board-put middle middle)))
    3 N6 B5 M8 {9 ~0 |( c; m
  189.   'game-room)( v. x$ ^4 d7 t5 x& R

  190. 5 a3 m/ \8 \4 V4 p
  191. (def-room game-room! _* `4 @1 n& ^4 W  F% @7 r- r
  192.   #'board-show
    3 {0 A4 m4 q3 _$ K: O
  193.   (t (lambda (cmd)5 {: e2 R! `7 T: E) V+ ]' W& I, s0 t
  194.          (with-board
    6 _! ?% E7 L0 n8 d- F" h  Q
  195.           (if (game-over-p)
    " {9 @9 [1 M( U# c; r- |
  196.               'living-room' R' i/ i; }: ?. J" _
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    ' y6 Q: e4 E) H& d+ H# [  {
  198.               (when (board-contains-p y x)
    9 I$ Y/ T0 O' e" s9 ?* `% Y
  199.                 (board-toggle y x)
    ! }( \& u8 k3 l' y" g% B6 ~
  200.                 (session "step" (1+ (session "step"))))
    ' b: e  c% l" W; H% p
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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