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

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

  1. ) `! B7 d$ w# p' A" \
  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 @  C8 S$ U. Y- a. M, `( [
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    9 @/ J7 S) D5 g' `' K( |  H* B
  4.   'tutorial-room-0)                     ; 默认的入口# T6 a5 Z! Q! K; Y. w& E$ }, y

  5. ! @3 W7 M' ^" b, T1 u& O/ c
  6. ;;; 游戏大厅8 s( f; B4 q7 M' S" ^+ [% r
  7. (def-room living-room0 H0 ^- u* E  Q, {  W
  8.   ;; 进入该房间后的提示语; Z0 s/ \# U% D* J7 y
  9.   "1. 教程
    " q* l7 _& I0 E, v4 j" o9 \
  10. 2. 入门(3x3)
    " ^& V1 ]( `/ @# O- \
  11. 3. 初级(5x5)7 J3 {- I) B8 ~+ r" [$ Z
  12. 4. 中级(7x7)2 W! z- P: d+ P# M/ K' n
  13. 5. 高级(9x9)5 R, s+ e' s9 T5 t8 q3 p- M
  14. 0. 关于作者6 f2 V3 s. J2 W$ s# b" y% A
  15. 请选择1-5开始新游戏:"
    8 E% ^7 Y) m' w
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    . _/ h0 E" O. O! ~
  17.   ("1" tutorial-room-0)
    # I+ k0 ?; A" J9 b* @$ l, O
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    * p/ ]0 h  Y) X, v
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    8 J/ y3 S/ q( y$ `
  20.   (t living-room))                      ; 如果条件为t,为永真* x7 u+ b% e: J" D1 z( `; K& ?

  21. 1 f1 H! Q7 I& F5 \3 D" R
  22. ;;; 作者信息0 V! N# L6 R/ B; ^
  23. (def-room about-room$ }  Q1 W0 ]2 Z6 H$ l& J# K
  24.   "作者:redraiment
    # y1 \& V  |+ x- @" U$ h/ L
  25. 微博:http://weibo.com/redraiment1 W& H5 B8 B( T! b) e3 ]
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    , \6 F( z3 A& Y* S: `0 }, `
  27. 请输入任意数字返回游戏大厅。", p6 ^# b: l8 {
  28.   (t living-room))
    " F; i( f+ V. q& w+ I
  29. * N  l" Y7 _9 }& P! L4 B
  30. ;;; 教程
    , X# @$ {4 U1 S% z) e
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    , k1 {8 S8 q/ z4 N& x& H* T  J
  32.   "The number of tutorial rooms")0 l9 M& ~) U" ?1 I% H
  33. 6 S4 R5 c" q) V9 J( l; {
  34. ;;; 简化教程的定义
    ; ?. S0 Z% O9 c' I, N
  35. (defun string-last-line (content)& B5 x, V# C" i" A* x9 e) `
  36.   "多行内容组成的字符串中的最后一行"# }9 @" B5 C7 X2 X: ]. u+ V% e
  37.   (with-temp-buffer$ n# B5 o, s) {; H
  38.     (insert content)6 H- g5 {8 _% J% f8 d7 S
  39.     (buffer-substring (line-beginning-position)
      d3 v+ ], M; x8 ^: y
  40.                       (point-max))))  S/ g6 v$ ]1 G" A
  41. ; k7 A+ v# ]  y6 M2 B  f! ~1 f
  42. (defun def-tutorial-room (prompt)
    4 v7 j- Q1 J8 e0 W
  43.   "根据提示语自动生成教程房间。
    * I  _6 _/ T* h2 t) f2 g, x. h

  44. 8 I1 U, y* u% V; X
  45. 1. 提取最后一行作为问题;
    4 d1 C- C, G4 x/ L: d  A* A- e. Z
  46. 2. 分析问题,获取期望用户输入的内容;
    ; p+ ^7 i  i' w* m% b
  47. 3. 定义教程房间和重复提问房间。"  }; Z5 f. ~- }/ e1 O
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    3 y4 P5 X  w9 U4 _, e1 r
  49.          (repeat-room (concat room-name "-repeat")), A8 W0 ~' w" n
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))) |% f1 N% w$ I) U  ]; h
  51.          (question (string-last-line prompt))# m0 l4 N6 F$ _; q( x
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    ( S3 E. h, Z3 {* }4 h7 L/ y5 \
  53.                      (match-string 1 question)))
    + |1 ~: @$ r; w- D
  54.          (doors (if except
    $ n& d0 g7 m$ L2 A% c* z' N
  55.                     `((,except ,(intern next-room))6 ^# t& [0 @1 \" ]) o+ Z) b( @
  56.                       ("q" living-room)
    * d6 F4 |; }  b1 U/ C- Q% R
  57.                       ("Q" living-room)" s/ V. U( _9 Y3 a6 c
  58.                       (t ,(intern repeat-room)))
    & i* j: u2 _- ]
  59.                   '((t living-room)))))- j5 N% a- C: s0 Y/ d' a
  60.     (def-room-raw (intern room-name) prompt doors)$ G5 y% D7 X- c" |
  61.     (def-room-raw (intern repeat-room) question doors)))% ]5 d& H2 |' Z  s0 b

  62. ) w! M3 V( k8 W/ ]( q; ]5 d
  63. (defun def-tutorial (&rest prompts)+ X) X  t+ F- N! E/ j, f) E
  64.   "批量生成教程房间。". ]2 {0 h% f* D9 x" K- ~- }
  65.   (dolist (prompt prompts)
    7 S. y! {/ F* w' V2 Z* L
  66.     (def-tutorial-room prompt)))
    7 `( ]. D% M9 E$ E9 X( G, `

  67. 3 F  B2 J4 i1 n( i6 _
  68. (def-tutorial$ x  \& D$ N( X- r
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    ) O: X& G4 M' I  D, k
  70. 1. 教程
    / o! g" C5 ^8 n, Q, l. O7 ?2 _9 L2 y
  71. 2. 入门(3x3)- ~9 G0 @. i/ S0 H
  72. 3. 初级(5x5)* C4 Q) b4 s' z! D
  73. 4. 中级(7x7)
    ( t! U2 ~* S1 ?
  74. 5. 高级(9x9)6 u; f4 ?, ~8 U: z4 d0 [* w1 g
  75. 0. 关于作者: O4 }' ]' ]- E. k  |& i* P
  76. 请选择1-5开始新游戏:1 |9 T: v/ P2 {7 K* y: Q
  77. 您现在正在游戏大厅里。+ [8 I4 X) `1 A5 D' n2 ^
  78. 请输入“2”进入入门级房间"
    / n5 u' q  I6 E2 Y+ I. r
  79.   "  ①②③
    ( O- A; w/ p5 l3 ^: E5 w
  80. 1 ■ * _+ d# k' ^- v. M0 d* y
  81. 2■■■
    9 j  L+ _, g% R$ q; L! `. Z- \
  82. 3 ■ 2 M7 J# ?. j7 n7 V! ~7 _
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    9 h" ?; i! a* m2 t2 y, d
  84. 请输入“22”来关闭第2行第2列的窗户。"
    1 C9 l; }0 H' ^' W4 K% @+ h7 Q
  85.   "  ①②③
    , B7 x+ U  H# C( p5 t! J
  86. 1   ) t5 b4 V) F6 V/ @% v# S
  87. 2   0 M  Q$ a9 s1 U2 N/ [% B3 M
  88. 3   
    ! @9 |$ r) z) O1 D7 `
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。4 Y- u2 g6 l5 U/ n2 c3 s6 p
  90. 请输入“11”,试着开启左上角的窗户。"6 e" F0 T0 ?- X! w: L- A
  91.   "  ①②③
    ! }: e2 C# o1 c
  92. 1■■ 
    1 u  T; b1 O7 m& n% `1 u
  93. 2■  * \$ C5 `. ]2 b; m% y' P3 b9 T
  94. 3   
    5 [6 E* a& U% i3 z: o, O
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    / l" |* }  @" a& [
  96. 请输入“13”开启右上角的窗户。"& n5 i& J9 _1 B9 t9 f2 ^) ?
  97.   "  ①②③7 ^/ Q( w% f  d( z+ s, a) Z" W
  98. 1■ ■
    3 P0 @! H" C  w5 y& a3 m
  99. 2■ ■
      m" U" u7 M* h6 ?! l. q
  100. 3   
    / |4 U  h8 s( D3 a. f. Z( m4 r
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。2 D+ m! {# c9 k4 y; B% j
  102. 请输入“31”开启左下角的窗户。"! C+ X# l) {2 l* z- F
  103.   "  ①②③/ i8 ^2 j) T7 f
  104. 1■ ■
    + G  c4 j" P9 P% [! j) B: k7 H
  105. 2  ■3 x! j) f& K( d* M  }# l; L
  106. 3■■ 7 k6 E, F( c; h4 S% S
  107. 此时,总共有5扇窗户被开启了。# r0 N5 U$ F# G/ y% Y
  108. 请输入“33”开启右下角的窗户。"
    2 e) I( Z: v9 O. ^. C
  109.   "  ①②③
    # ]: d& C6 A+ j9 U4 _
  110. 1■ ■' l" Z2 A4 W6 }* e
  111. 2   / ]- A+ b# j0 ~! t% e! r
  112. 3■ ■
    $ F5 E2 D/ W8 d& M  w
  113. 现在,只有四个角落的窗户被打开。
    6 z2 l8 R9 b& P5 D: R1 L4 ^
  114. 请输入“22”完成最后一击!"
    4 p' m  p+ B9 l" i
  115.   "  ①②③
    , A5 @5 m. k% \
  116. 1■■■
    - a0 ~5 r6 W1 T% g9 W
  117. 2■■■
    ! n: s" O" Y2 n. O
  118. 3■■■% I0 \& H( ]! T" q  E
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")3 V( ?* X9 c7 R' `6 i
  120. 3 ]0 _% G8 G' A) s( r; X3 q% I& b
  121. ;;; 棋盘1 I( C6 N7 A2 Q3 h) r: e" ]
  122. (defconst *wechat-5x5-white-chess* 12288
    6 D0 u5 ?! h9 G% x
  123.   " ")
    1 m- K9 y& x4 }6 S$ H$ ?4 M

  124. ( J; H; |0 E: v. P
  125. (defconst *wechat-5x5-black-chess* 96324 q8 S+ t. B2 M6 {. N: m
  126.   "■")6 M2 O6 d1 D: M- B7 h' Q" L
  127. ' ?' Z* t# j  m$ T& O
  128. (defmacro with-board (&rest body)5 T4 l2 P8 Y# N" V) c
  129.   `(with-temp-buffer
    5 |3 X- r: _$ l
  130.      (unwind-protect
    & \% n- I9 d  j
  131.          (progn
    - d- }* q/ d! G5 ~; B+ U" j
  132.            (if (session "board")- f7 G. f+ D' V
  133.                (insert (session "board")))+ R3 x# p" Y0 c& J
  134.            ,@body)
    0 F2 ?8 k& L' L4 I: P1 B
  135.        (session "board" (buffer-string)))))
    " q5 Y$ W) F! [6 p( g" p( _# H0 w3 M
  136. # k# H2 \/ x- V+ G
  137. (defun board-init (size): ?) {/ u: R' K$ v0 O
  138.   (session "size" size)7 n2 ]; V9 m$ H+ I# h
  139.   (session "step" 0)
    * e5 L+ V3 C9 k2 l5 u" V
  140.   (erase-buffer)% w1 \$ H) i( a# k! Z
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨")); P6 @, a# s3 M# i0 z2 ?6 O
  142.   (dotimes (row size)- L3 W, E, |) Z0 E
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    7 X! |# y; j" ^; U. a7 f

  144. ( M9 b1 ~' f" }
  145. (defun board-contains-p (y x)
    . R# i! T9 ?: t+ }. x' L
  146.   (let ((size (session "size")))
    * p8 r" u8 G& K. p; R" S
  147.     (and (<= 1 y) (<= y size)
    . f* j; G/ ~9 _0 f& I0 J
  148.          (<= 1 x) (<= x size)))). R" C7 J6 V, p! O: G4 d' z

  149. 2 Q  `+ E( i- e% L6 f
  150. (defun board-toggle (y x)
    ! k, y' f  y7 d6 n
  151.   (when (board-contains-p y x)
    / }7 G( _# ~3 A
  152.     (goto-line (1+ y)); I0 C- t) _. Z0 f3 S) @$ v
  153.     (beginning-of-line)' \" r5 r3 C" }" Q" g
  154.     (forward-char x)
    9 w" ~8 d7 H- [& I' {" i$ ]
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    7 @. x. p- c5 ]% t8 j7 T; ^
  156.                 *wechat-5x5-black-chess*1 G7 ~/ N% ^+ t2 s, k
  157.               *wechat-5x5-white-chess*))9 R8 u- n; }- d9 h
  158.     (delete-char 1)))" J" P! x$ P6 }' o

  159. ( [7 T9 O# h0 ]: s
  160. (defun board-put (y x)
    / V3 v( T5 f' W, t) z6 W- ~
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    ' x, v1 Q5 r1 q( W. h8 I
  162.     (board-toggle (+ y (first dir))
    8 T" ?" g$ y7 D
  163.                   (+ x (second dir)))))
    * W* S" w0 E' i6 G8 K# U  F0 W

  164. " ]6 d! K' I) q) M2 A1 T" H
  165. (defun game-over-p ()
    2 f7 y8 R' g* A, H. d  n" {
  166.   (beginning-of-buffer)
    , {5 G6 T$ C% i
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))2 s4 n) d. r3 h! B" @8 o( y
  168. ; f  k, W- P. c$ B5 x9 _
  169. (defun board-show ()
    2 E8 M0 |# D+ e# b
  170.   (with-board
    ' ]/ \& j1 K( z! \$ n% k
  171.    (concat (buffer-string)9 ~* {; y; K" t
  172.            (if (game-over-p)
    / M" x. C. g* ^8 i9 F$ Z! s' i% r9 N
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    / V" t( Z; b, `3 {
  174.             (format "第%d步" (1+ (session "step")))))))* `) N' A0 \8 c9 `

  175. + P: o0 i5 {5 C0 Z
  176. (defun board-position-parse (cmd)
    - Z5 n9 c' X  b% }; V* E" C
  177.   (if (= (length cmd) 2)
    / X9 Q: h  x: h2 V
  178.       (list (string-to-int (substring cmd 0 1))7 C! I* V2 O6 h% o) _$ f# {) @
  179.             (string-to-int (substring cmd 1 2)))& E  h3 @! {5 x4 J+ t& e8 Y, }
  180.     '(0 0)))
    ! R  q* _& `) @, h6 b$ w

  181. # j0 g/ ?# t& H( L
  182. ;;; 游戏房间- [0 K) g8 ], ~) Z, u7 w/ C( y' y
  183. (defun game-room-init (cmd)
    3 `; V, r, @/ u# I9 I, N# \) O3 @. S6 K
  184.   (let* ((middle (string-to-int cmd))
      k9 R9 ~' L& K' }
  185.          (size (1- (* 2 middle))))) Y, \& }. _9 o/ B- v9 r' \( i4 R
  186.     (with-board
    1 v% C( O0 }, f/ Y, m+ p: S
  187.      (board-init size)+ I: F6 X7 U; C8 G  P
  188.      (board-put middle middle)))
    ) w+ y5 `3 d, M/ L: }% O( h
  189.   'game-room)
    " G$ A  M' c* Q7 L0 [& r" n0 A3 F

  190. + W& [5 w1 ?9 N8 s7 E
  191. (def-room game-room
    5 G1 z  J3 W8 c- M! m# t8 {
  192.   #'board-show) t; e' v, g$ D* u' Q# Z) I- A& N+ i
  193.   (t (lambda (cmd)4 P, d5 s  s0 e& H
  194.          (with-board2 w* X8 p- c3 B% E& G6 `
  195.           (if (game-over-p)8 Q) X& l' N. k. y* t6 r
  196.               'living-room5 N9 i% C( O# K9 ?" d
  197.             (destructuring-bind (y x) (board-position-parse cmd)  I% |# @/ B# z
  198.               (when (board-contains-p y x)3 x0 `! J8 M/ n2 Y
  199.                 (board-toggle y x)
    $ p/ C- C$ o! s2 J' h: H
  200.                 (session "step" (1+ (session "step"))))
    6 v1 L3 T+ H4 l) F9 O. j% d
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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