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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
5 Q" h( e$ |7 z( E7 _& q借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. 5 Y+ N, ~7 E% p) W4 F* C6 Z* j
  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;">;; 定义新的游戏地图
    6 W- N- I* V! C- j0 k
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL* k' H. [- u. L
  4.   'tutorial-room-0)                     ; 默认的入口5 \' H1 a9 C5 Q- s

  5. 5 i  {* J0 I$ S6 g
  6. ;;; 游戏大厅
    # g: W, U9 x; d) A
  7. (def-room living-room, I8 Y1 ]. c0 z* N, Q
  8.   ;; 进入该房间后的提示语
    . y( J- J- t. L9 C2 [
  9.   "1. 教程/ ]: A$ x. J/ L; o3 J
  10. 2. 入门(3x3)
    " c6 V) O+ _2 E3 |$ _
  11. 3. 初级(5x5)
    5 i6 ]9 H- B5 Z7 m, C! [
  12. 4. 中级(7x7)
    ! ^3 y! {& t5 c: F  Y2 @
  13. 5. 高级(9x9); e- O" U7 V/ t7 O  H) x% k
  14. 0. 关于作者/ a8 Q9 G' I. ]2 \2 h$ \
  15. 请选择1-5开始新游戏:"
    - K1 f  N8 o( R+ \" M* h  k0 ~% b
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名. m4 A* U, ^, y* S5 x
  17.   ("1" tutorial-room-0). j5 |7 b+ y# a" c  D/ K1 R
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配: J' @# E/ A  t1 ^' p
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    ! V+ B7 ^% O7 `3 _4 h2 ?7 z
  20.   (t living-room))                      ; 如果条件为t,为永真
    . g$ W6 Z( S3 c) I

  21. $ I/ W% t6 v7 h
  22. ;;; 作者信息
    ; @' c9 c+ Y8 B1 h" W& F
  23. (def-room about-room4 R4 K* v+ }4 O  F6 L
  24.   "作者:redraiment" J+ ]5 E4 t4 d& `5 W
  25. 微博:http://weibo.com/redraiment
    4 N/ Z4 K" S" v; K
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    / P& |1 |9 g  A6 _3 T3 h$ r9 _  b3 F
  27. 请输入任意数字返回游戏大厅。"* ~. g4 f  ^. ^9 B
  28.   (t living-room))
    # I9 I$ c. O* o  c  V- w

  29.   i8 G- o& X3 s
  30. ;;; 教程
    # R1 ^' K3 [3 E2 }
  31. (defvar *wechat-5x5-tutorial-rooms* 0& g$ H) f* ^' i; P& a3 c3 z
  32.   "The number of tutorial rooms")
    1 A) m" J# Y5 r6 P6 X! _! S

  33. 1 t- I2 d) x- ?' ], N" c
  34. ;;; 简化教程的定义
    ! {" O$ F  o* g" U
  35. (defun string-last-line (content)
    ; Q' _$ p2 M! N, [* a  v1 ^
  36.   "多行内容组成的字符串中的最后一行"
    - n4 h$ ^4 Z. x; d' L# i
  37.   (with-temp-buffer" X. g, A9 t- p+ J1 N" v% K
  38.     (insert content)
    4 [; x, W" W: V
  39.     (buffer-substring (line-beginning-position); ]1 z2 A& c3 G/ P; b
  40.                       (point-max))))2 j  M/ Q+ Q5 K, O3 D

  41. + M% K' A7 d% x
  42. (defun def-tutorial-room (prompt)! N0 A8 h: ?) H
  43.   "根据提示语自动生成教程房间。: e2 s- o/ ?, ?3 G

  44. ! D* [2 T) y' B/ M. Z
  45. 1. 提取最后一行作为问题;* }! }' n, B, d8 z* Z
  46. 2. 分析问题,获取期望用户输入的内容;5 U  [& f! z; d
  47. 3. 定义教程房间和重复提问房间。"! W4 l6 F9 L/ v
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*))); R6 B7 p1 H+ y( k+ g
  49.          (repeat-room (concat room-name "-repeat"))6 f. Y' B% D; E! [/ V1 E5 u' C* J0 ~
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))( B. f1 Y3 @) I0 ~0 x6 X
  51.          (question (string-last-line prompt))" {% N* v' O* w0 a
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question), }2 F- F$ m) N  X( i6 B5 }# B, D
  53.                      (match-string 1 question)))2 O$ t" Z8 ~3 l: A; M
  54.          (doors (if except$ }7 B: Y, _: n  e! \: t
  55.                     `((,except ,(intern next-room))
    ! I+ F. W8 k5 T( S7 k( U0 }
  56.                       ("q" living-room)
    ' L$ u) @6 e, G; F1 C
  57.                       ("Q" living-room)
    * G5 [2 e0 i6 E/ Z1 H- |
  58.                       (t ,(intern repeat-room)))' M0 K/ |; R6 V5 b, v. Q
  59.                   '((t living-room)))))+ K& E1 Y4 }7 B) {. @0 ?( o
  60.     (def-room-raw (intern room-name) prompt doors)  z8 [4 r0 {; u) j4 ?% S
  61.     (def-room-raw (intern repeat-room) question doors)))& D2 Y3 v$ _% B2 O2 Z
  62.   c( s, ?& |1 _3 b  A
  63. (defun def-tutorial (&rest prompts)
    2 ]8 O# T! j/ E
  64.   "批量生成教程房间。"4 M- F4 @/ {  w6 c, R: `# j) U
  65.   (dolist (prompt prompts)+ d6 T# g* d. o, ?, f
  66.     (def-tutorial-room prompt)))
    * A0 L+ e/ i5 V, G, ^: k" v

  67. 5 w2 @# Z$ [1 J9 ~3 t
  68. (def-tutorial
    ( z- l# b! w/ p7 g
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
      [( Z6 z3 z7 _6 g* x* q
  70. 1. 教程
    ! ?: Q% Z. }7 C/ o6 T% H7 U
  71. 2. 入门(3x3)% m6 Q+ {1 v- t: y; D
  72. 3. 初级(5x5)' t' v% U; t  @6 G+ m
  73. 4. 中级(7x7)$ l- M0 S5 V( S( d4 E8 c0 u
  74. 5. 高级(9x9)
    % W% ^: z8 x& E7 `( u
  75. 0. 关于作者
    - K$ v4 y% {, ]/ H- _$ u
  76. 请选择1-5开始新游戏:
    3 N5 p: n! i0 Z# @& {* I
  77. 您现在正在游戏大厅里。
    # [$ O1 S- p2 D  I( a5 F. \" O
  78. 请输入“2”进入入门级房间": m. ~2 Q  h% g4 B6 @
  79.   "  ①②③
    " \1 Y: w; f' o/ _6 O* _+ h4 y  I/ W
  80. 1 ■ & x' a* V1 r/ U  W
  81. 2■■■
    ! \% S- L  N) E
  82. 3 ■ 
    , d1 X( p  x1 L9 z3 j4 G9 n
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    ; b( i: U4 K1 w5 {; `/ y' J
  84. 请输入“22”来关闭第2行第2列的窗户。"- J' [' S( r& \" p1 C/ V& y5 P" M
  85.   "  ①②③
    ; u3 I+ ]+ f* s
  86. 1   
    ; n* P7 G/ I& _; l
  87. 2   
    9 B& T' X  Z: y6 ]( T0 Q& `6 X3 {
  88. 3   2 X6 n( }4 e; d7 g' V+ Y
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    ) @" Q4 y. O: Q7 t
  90. 请输入“11”,试着开启左上角的窗户。"2 {5 H1 q, k# ~8 @* q* o
  91.   "  ①②③
    0 U; p' R4 J9 m; i
  92. 1■■ 4 {$ x# s' h+ t5 G+ k* O! H
  93. 2■  , ^* b5 P' ]( _" I. f
  94. 3   
    / I/ Z+ ]7 D+ S, H3 s% S! ~
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    % x! W0 J) o6 I7 _. M* K9 G
  96. 请输入“13”开启右上角的窗户。"
    1 O2 b( T7 R' \$ N2 c. j
  97.   "  ①②③, W" t9 B/ i: ^: r$ `
  98. 1■ ■: l; h4 R0 d( D3 m/ k
  99. 2■ ■
    ' H: k6 u5 s( k8 r( E* x, u
  100. 3   
    " Z6 T- B& C3 k
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    ) y" s" J# U+ W: \  `
  102. 请输入“31”开启左下角的窗户。"$ l& V2 k2 |: r, c* h7 f
  103.   "  ①②③  s* k# y7 @: q
  104. 1■ ■
    + ?2 M+ n1 Q/ E) V3 F) j. h0 H' D
  105. 2  ■% R! B' c! ^4 |) {% W. R+ S
  106. 3■■ 
    4 t% E1 G1 Y; y# B! |
  107. 此时,总共有5扇窗户被开启了。$ j  ]- Z6 a4 b6 L4 X9 C/ I( X
  108. 请输入“33”开启右下角的窗户。"; ?" ?/ ~/ S' @6 v$ q9 f
  109.   "  ①②③9 f9 [) [1 G/ Q4 u0 u
  110. 1■ ■' u4 ^& l/ @) l
  111. 2   8 r1 m. l5 t- l  T0 G4 D
  112. 3■ ■: M4 e  o# `8 O% \$ R" Z% p
  113. 现在,只有四个角落的窗户被打开。
    9 K  ^  w5 y5 ^% c* Z
  114. 请输入“22”完成最后一击!"2 k/ T% K( ?1 a
  115.   "  ①②③
    - |8 y. Q% |3 V; J  i) p
  116. 1■■■
    ( o9 G$ ?$ N  G. `+ X1 L
  117. 2■■■
    8 L' W: s% m5 |
  118. 3■■■2 Q: e+ a. W* i! {- Q' G
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    # [  J& w/ ~" d
  120. . P( b1 p4 x1 G# S7 M
  121. ;;; 棋盘2 r  w. l% z/ Y4 C, G
  122. (defconst *wechat-5x5-white-chess* 12288" [* T8 t0 ]4 R" U  F# e
  123.   " ")
    2 b. X. Z& `" @% f

  124. ' {% ~; W8 k) J( x' p" i% O1 I
  125. (defconst *wechat-5x5-black-chess* 9632
    7 }9 N7 k! B2 y4 I. u: F" h  g
  126.   "■")
    + W8 T4 S6 G+ W# J4 {1 `- e4 Q

  127. ! h+ Q& p# ~2 x) p
  128. (defmacro with-board (&rest body)
    ' L0 a& ~; t; q+ s1 C% D
  129.   `(with-temp-buffer
    1 p- c9 p- S4 Z- G" Z) Y
  130.      (unwind-protect
    : }1 J5 f" X7 ?
  131.          (progn
    3 c' e( q  Q( D- \; J6 ~9 [
  132.            (if (session "board")
    . U( A0 z% d5 P9 Y8 v' U
  133.                (insert (session "board")))9 U& Y( f- L  {
  134.            ,@body)
    - o$ p4 R+ q& O# J* b0 B$ L
  135.        (session "board" (buffer-string))))), p. Q0 C+ R. U

  136. . l, Y+ ~7 O3 ?. s. O. |& {- t
  137. (defun board-init (size)
    % D; \  D* w5 d, q; s. d8 _
  138.   (session "size" size)
    ; ^+ [/ `4 f! ?0 X" Q! r7 E( m
  139.   (session "step" 0)8 F. n/ o# Q6 Z* x$ g2 q& m9 q
  140.   (erase-buffer); j5 _) j) [6 \
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))( \1 c$ i$ r- C
  142.   (dotimes (row size)! T& F, h1 `$ y0 C
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))# X' c/ N" z' V4 u9 f
  144. 6 z* ^$ x7 S5 k, u
  145. (defun board-contains-p (y x)
    8 D3 h& c9 ~8 `7 q
  146.   (let ((size (session "size"))). f. C, k4 c( L: Y5 D8 A  |
  147.     (and (<= 1 y) (<= y size)2 X1 ?' k2 v- t+ c' ^( z
  148.          (<= 1 x) (<= x size))))
    6 }- J5 f8 e+ R' \* D  [. g: W& d  L

  149. * _& @% D& Z- B- o( U
  150. (defun board-toggle (y x)
    & i& Q; g2 ?6 c' i% {( ]
  151.   (when (board-contains-p y x)
    ! w3 Z& ]6 z, {1 C& ]+ ?
  152.     (goto-line (1+ y))
    7 C# k% c- V7 D/ m) ~
  153.     (beginning-of-line)
    5 `+ B( v7 J9 M5 {. b3 a: q' g* \
  154.     (forward-char x)
    5 C3 O' n5 R! i$ ~! R, I0 c$ ^
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char)), |% u" h1 \, L$ w0 p3 {% o
  156.                 *wechat-5x5-black-chess*; L; i# s0 S3 n, z0 Q) n
  157.               *wechat-5x5-white-chess*)): {- }' r& H; y& N# q" I
  158.     (delete-char 1)))6 G! l+ b7 G% {& S7 k

  159. # C, D/ @5 x0 j- \6 t/ j2 E2 t
  160. (defun board-put (y x)- P1 {( b% s0 c7 V0 z
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    - n' `3 U" i3 I# T+ y- P* M
  162.     (board-toggle (+ y (first dir))
    ( b! {8 R# y9 z+ \, w9 B3 f$ G
  163.                   (+ x (second dir)))))
    6 M) j  h0 h+ v
  164. ) v/ ~" N% @% f" M: R" A
  165. (defun game-over-p ()  q8 `4 H$ ^0 j* }0 c
  166.   (beginning-of-buffer)
      U; i: ^2 {- V
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    1 Z; _) G( B: u1 \6 p
  168. 1 i* x' [' w# A- s* i; c
  169. (defun board-show ()  j8 e4 q- [. d
  170.   (with-board
    7 l8 \4 B1 R5 y6 P2 _2 v  z
  171.    (concat (buffer-string)
    2 a0 c9 V) e. |+ {
  172.            (if (game-over-p)
    4 q8 P7 [8 l# j" k
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))& o$ m/ t5 l  I- `8 W/ h
  174.             (format "第%d步" (1+ (session "step")))))))
    / G) ^) M) Z/ K, b

  175. 4 L( O. w) K, o7 T& q$ _
  176. (defun board-position-parse (cmd). E8 U( G- F; q$ A/ |2 @
  177.   (if (= (length cmd) 2)$ O3 C7 [- \$ Z: R' y* i
  178.       (list (string-to-int (substring cmd 0 1))* }" {3 O) R, d
  179.             (string-to-int (substring cmd 1 2))): g+ x, @; }; `3 i0 |
  180.     '(0 0))). y- M% [1 |" u$ L) B1 T6 U' E
  181. 3 Q' v  u1 Z6 b6 k  K3 ?
  182. ;;; 游戏房间5 V$ i0 w5 z- o( R3 l5 r( V
  183. (defun game-room-init (cmd)7 x7 A& R* j% ~2 _/ w  {. \5 o  g
  184.   (let* ((middle (string-to-int cmd))5 R/ P2 E8 k$ d% L) t+ ?1 ^+ x
  185.          (size (1- (* 2 middle))))% u( y' o  ~/ }" V. H
  186.     (with-board9 `; \( E% W( X, p+ V: g
  187.      (board-init size). @1 p* b4 V5 F; A; {
  188.      (board-put middle middle)))) q" S) D/ T+ y4 \7 y
  189.   'game-room)4 d! p! m( Q2 p) M* w: i, U" G' H
  190. 7 f* h% f  B( i( r' t( w" |
  191. (def-room game-room' `9 W$ k0 X: {/ r) k: K& x
  192.   #'board-show6 A: k: l5 g" a
  193.   (t (lambda (cmd)! \6 _# W! M% t7 f- P, H  {% ~3 k" o
  194.          (with-board# F2 M- J. @8 _- H; v' ?5 F
  195.           (if (game-over-p)1 t: w: Z$ G4 [7 ?
  196.               'living-room! V' m5 N- V4 D6 W% R* |1 ^
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    6 F& R: ~% f/ s$ ?: M
  198.               (when (board-contains-p y x)* x0 s$ ^1 k0 A+ M& z# q
  199.                 (board-toggle y x)3 i) _/ s+ [; Y! [& k" W# _
  200.                 (session "step" (1+ (session "step"))))+ r2 o$ Y: n- Y
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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