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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
, c  I8 I* f+ g借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. , R  ]. F& E% E+ f/ T1 F* {" o
  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% L+ a+ N6 |
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL. U. Z3 ?+ z8 `5 K- l
  4.   'tutorial-room-0)                     ; 默认的入口1 H4 v  P; S3 T3 \+ g/ D. W7 J  j

  5. - U* V* Y& l: N
  6. ;;; 游戏大厅
    : D, Z) a% {' ^9 a/ [$ U1 ?
  7. (def-room living-room
    # z, s0 E- y4 ]( Q
  8.   ;; 进入该房间后的提示语
    9 }0 Q& y1 Z, N
  9.   "1. 教程2 f. @: t* l  M' l
  10. 2. 入门(3x3); _; F$ h6 A9 ~: `
  11. 3. 初级(5x5)  Q4 a7 l2 a+ s- Y; `, V: ]
  12. 4. 中级(7x7)- ~3 K3 V  n; ~
  13. 5. 高级(9x9)+ y4 j3 D& E- a  o0 Z; o6 t
  14. 0. 关于作者, W* I( y1 X% |7 e
  15. 请选择1-5开始新游戏:"
    - r' J6 j$ d' E3 Z" R% ^
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    7 i! v/ J$ [8 u: M: q
  17.   ("1" tutorial-room-0)
    1 {, q' S/ J% I7 {
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配1 m+ y8 I  i0 i
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    : H+ {  C7 ?4 k% j9 d1 n( }( N; m
  20.   (t living-room))                      ; 如果条件为t,为永真8 g& O2 M1 i& C, v% }$ d$ U
  21. $ V( f9 I+ O; F4 o8 m% z- C+ k0 b& e
  22. ;;; 作者信息
    3 h% F6 _' F) l" b( d) a9 n) z
  23. (def-room about-room
    3 p/ w1 E( l/ U
  24.   "作者:redraiment5 D" z, B) a! m$ s, |8 N+ T# g
  25. 微博:http://weibo.com/redraiment
    ( l- o" N9 n  @3 X
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    8 a0 {# E' M+ ~! w( A" q
  27. 请输入任意数字返回游戏大厅。"
    0 b7 x- |# b2 p) K$ E
  28.   (t living-room))
    + v" ~$ C/ g6 t- t4 U: y& a: T

  29. & S/ V1 \7 o: ]( @- l0 t
  30. ;;; 教程9 Y/ h7 ~- b% p7 g4 W, F+ d7 J+ H
  31. (defvar *wechat-5x5-tutorial-rooms* 04 r7 h5 O5 j5 M2 @: A7 ]
  32.   "The number of tutorial rooms")- i5 z7 _7 ^, ^* \4 h" U

  33. ; i- o6 |# @7 w! m1 C: b
  34. ;;; 简化教程的定义
    # t& }( J7 N5 x" j0 E
  35. (defun string-last-line (content)
    5 o( O# a! M, Q0 n' ?4 a
  36.   "多行内容组成的字符串中的最后一行"
    4 k# q; i  l% |* U
  37.   (with-temp-buffer! t9 c2 M4 ~; D5 K) ^
  38.     (insert content)
    8 m" w7 s3 ^, J" g& s" W) x
  39.     (buffer-substring (line-beginning-position)0 u, s) ^# u9 I1 a0 ~
  40.                       (point-max))))" q6 Y9 y) P' {% d- f

  41. ' Y. _: F9 b/ |. y1 j
  42. (defun def-tutorial-room (prompt)
    0 u  {8 @# t2 r# s, C5 \
  43.   "根据提示语自动生成教程房间。' U) t* H5 V; ]" G# _
  44. 8 k* C: F' C* I. m+ i
  45. 1. 提取最后一行作为问题;
    : u4 \- q! I6 K! Y- l, r) u
  46. 2. 分析问题,获取期望用户输入的内容;
    * O; E, {- h2 u+ Z5 P6 Y2 s: z! d( V
  47. 3. 定义教程房间和重复提问房间。"
    3 E9 }* ?1 u7 s6 P
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    ; }0 A. o; J0 H) M  O
  49.          (repeat-room (concat room-name "-repeat"))/ g  S' H) \8 g2 S7 D
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*)))). q0 S1 q2 U4 l9 X9 N
  51.          (question (string-last-line prompt))
    % z4 `- [. m' q$ _+ t5 i7 J
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)3 B5 n! C6 c- |; Q& `  j7 n' @
  53.                      (match-string 1 question)))
    % o; W6 F# T1 Y' m
  54.          (doors (if except8 r* [+ v8 [/ }
  55.                     `((,except ,(intern next-room))
    ) @) F' g7 {) F: `. ^' g
  56.                       ("q" living-room)
    ( o0 N4 D" k4 K! l% @+ d
  57.                       ("Q" living-room)
    0 b% r' d0 O* L# o3 A. j, W
  58.                       (t ,(intern repeat-room)))
    6 k( U4 D( o- `# H
  59.                   '((t living-room)))))' Y4 V6 n8 s6 o2 e/ Z
  60.     (def-room-raw (intern room-name) prompt doors)9 p' P& g, B  E
  61.     (def-room-raw (intern repeat-room) question doors)))
    , S; J$ ?/ k9 i

  62. 7 R2 J6 V, l9 x% T4 j0 R
  63. (defun def-tutorial (&rest prompts)$ a! ?1 H8 f7 I/ h
  64.   "批量生成教程房间。"
    . R5 e0 q+ x$ H# \4 f$ a$ Q
  65.   (dolist (prompt prompts); {. f' k. Z7 F& Q! {
  66.     (def-tutorial-room prompt)))
    / [. U- n8 E" H+ W# G/ b
  67. # x2 F5 {( M  x. H* y0 A* D% n
  68. (def-tutorial; w! t! K$ q4 c+ ^$ n: B$ Y) @& e8 k
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    / ]6 J, v9 d6 N& I, u% _: B
  70. 1. 教程+ z; V# }0 V, w
  71. 2. 入门(3x3)
    0 a, F0 p$ |! f, y9 m
  72. 3. 初级(5x5)1 A& z7 E# M3 c( J
  73. 4. 中级(7x7)9 k9 [. q( y' e8 c: g7 K. U( |
  74. 5. 高级(9x9)
    ) _$ f3 @4 n% \2 ~( L+ ~
  75. 0. 关于作者
    " ^$ a5 m: C6 D; c. }
  76. 请选择1-5开始新游戏:0 @) p* v& z1 V, H
  77. 您现在正在游戏大厅里。8 ^, F8 _" p) C" ]  {3 w7 }. T2 ~/ X
  78. 请输入“2”进入入门级房间"
    + J- k8 k1 D* f; t/ V9 V# ?  E! Q8 ?
  79.   "  ①②③7 P8 ]% j  [+ P/ d  z6 f( p; k
  80. 1 ■ 
    0 Y3 S: R6 C: B! y( P" R
  81. 2■■■  P, Q7 U0 y7 G: h3 {4 G
  82. 3 ■ 5 y( e" t, w5 v0 N) S8 ?7 i
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!: Y* u& }% a9 T6 c
  84. 请输入“22”来关闭第2行第2列的窗户。"# x# f9 f* }! m6 v
  85.   "  ①②③
    5 `* r6 C2 }2 U1 Y) b, a
  86. 1     F  t8 s. C7 F6 Y1 m
  87. 2   0 D1 W. @. `  R' j/ o
  88. 3   * P4 O" Y9 x( @
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。/ {  v2 m; M# N
  90. 请输入“11”,试着开启左上角的窗户。"
    9 Z+ @# T3 r3 a1 K4 }
  91.   "  ①②③
    + E8 U8 g5 p' I( J& X
  92. 1■■ / a# s" Z7 h4 q
  93. 2■  & `% p( Z6 t& P2 T/ l
  94. 3   1 C" w$ i: ^! i
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。, l- x; K# B+ q$ L( @3 A$ t0 v" ]- R- s
  96. 请输入“13”开启右上角的窗户。"
    0 u4 ]' K7 b) b, C
  97.   "  ①②③
    / o& C( [& f1 A# w0 ^/ S
  98. 1■ ■
    % O+ I8 I' H% y, s. O) c' e
  99. 2■ ■
    % E; u# {3 `. b( R( p5 Y7 M. y
  100. 3   ' F' _, I$ t' N
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。# B( N, P# {$ ^$ p/ {
  102. 请输入“31”开启左下角的窗户。"
    4 X/ T1 m8 k  X% o" a1 `+ E- j) D
  103.   "  ①②③2 C' _, K3 v; Z& G/ O9 p. i2 C2 n, R
  104. 1■ ■
    . G" a: o# L; ~
  105. 2  ■
    $ l9 S" x0 d: ?% i$ O& R
  106. 3■■ 
    * }7 N! I. `- h
  107. 此时,总共有5扇窗户被开启了。
    6 j+ b: I+ C( N' w+ P5 t8 Z5 g8 g
  108. 请输入“33”开启右下角的窗户。"
      ^2 r: F! r5 V" A' C
  109.   "  ①②③  s; v. a0 O( F* D! `6 W5 L2 W$ z
  110. 1■ ■& y% [/ d; L+ a& a2 s
  111. 2   
    7 L: M& W5 ]! Q& L$ ~0 z( z
  112. 3■ ■
    ; I$ W1 n1 ]' ~. k- j1 U
  113. 现在,只有四个角落的窗户被打开。1 t0 d9 _: {% c& u/ \) g
  114. 请输入“22”完成最后一击!"
    5 C( p0 o7 b; {0 H/ u% c9 ^3 m
  115.   "  ①②③1 ^& e9 R4 s5 ?3 d4 `1 n0 r
  116. 1■■■
    / s  Z; T. M3 A- G4 i6 ~; O9 N
  117. 2■■■
    7 m, [. j$ r8 l
  118. 3■■■( P) v) s$ x: `7 f
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!"): J3 P4 W* n  c% t' R

  120. / T. I+ y% p; N
  121. ;;; 棋盘
    ; Q4 I- w$ B/ R; Q( J! j% _0 Y
  122. (defconst *wechat-5x5-white-chess* 12288% H6 k$ B) s! t7 f+ Y" ]! [, |
  123.   " ")) g" {% X$ r4 o+ |# {3 R0 M
  124. $ E4 H: x" k) y1 e/ P6 `" L
  125. (defconst *wechat-5x5-black-chess* 9632  o7 _, ^6 |0 M9 D  W" j% s( t
  126.   "■")+ v0 ]; B( D$ u0 T. h

  127. - x4 ]. ~, g) i: {  i! y+ \4 r
  128. (defmacro with-board (&rest body)
    : n4 \$ r; Y9 U- Q4 I* C; H
  129.   `(with-temp-buffer
    ) \" P! A' I4 p( Y
  130.      (unwind-protect+ L2 P* U- {  {
  131.          (progn% K7 a6 p: }' p' t; q. T
  132.            (if (session "board"); H4 M7 q4 u2 p; {/ K' R' J
  133.                (insert (session "board")))0 A: W. g- @& q' m
  134.            ,@body)
    2 B  C$ S; b) G) d
  135.        (session "board" (buffer-string)))))3 E  Y5 n0 z! o; w& P& |

  136. 7 |6 R/ Z+ Z' H# U0 I9 B
  137. (defun board-init (size)2 {* R3 P0 M% m
  138.   (session "size" size)
    ( u% e, R; P+ E; T# s* Y& ?
  139.   (session "step" 0)
    : _# t9 j- [# ]$ t) H2 F: E5 W
  140.   (erase-buffer)& r: S7 L/ u; |7 V
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    + y! s, e) m! Y" @1 G/ b( y+ z; ^
  142.   (dotimes (row size)
    . g/ t. g5 R# U, }! T& p+ a$ G; J
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))! K: |. _3 ~( E5 `! }! O. i8 t1 N

  144. " W& t( M5 R; p7 a, x- G
  145. (defun board-contains-p (y x)
    - ~- {/ m- M. R$ h( Y
  146.   (let ((size (session "size")))
    % d' ?! ~# d0 I7 s
  147.     (and (<= 1 y) (<= y size)
    * C5 A* D2 q" Y  L! x
  148.          (<= 1 x) (<= x size))))
    , V( Z. S" T. k' k: g) J

  149. 4 j8 I; \+ g9 a
  150. (defun board-toggle (y x)
    4 }5 ]6 X: b$ Z( n3 K
  151.   (when (board-contains-p y x)0 k# X0 }$ J: ^9 t# o
  152.     (goto-line (1+ y))& O" d# F! M9 b7 V# W& N
  153.     (beginning-of-line)9 [2 ]' o# L- m$ A9 S( m2 U# ]
  154.     (forward-char x)
    ; P4 L6 j2 S# D0 K! m
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    ) A% Z' }. F' r
  156.                 *wechat-5x5-black-chess*
    " L$ G& i; c/ N+ J& X  l9 y* {
  157.               *wechat-5x5-white-chess*))# M6 R& `; A9 i0 F7 a7 A
  158.     (delete-char 1)))
      q; B: v/ w; n5 _& _

  159. 8 e% w* Y' S- U- ]! k
  160. (defun board-put (y x)
    ' m( Q/ {0 B) L" y- I$ G
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))  R) r3 ~* |7 \9 ]! M9 i& A
  162.     (board-toggle (+ y (first dir))
    - H& |& N6 z& N/ B: h0 b
  163.                   (+ x (second dir)))))0 E9 |- v1 ^9 L5 R6 T, J* l
  164. 8 g1 w5 G0 ]% s7 `! Q
  165. (defun game-over-p (). V4 Y9 [6 x& s7 H6 v$ y
  166.   (beginning-of-buffer)
    8 q6 k, C, L6 J6 {* ~8 R- \
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))& @0 n0 D: V. }/ H9 S: \1 b

  168. 8 F# O- N/ `/ F% M9 x# k# W. ]
  169. (defun board-show ()
    9 |6 [5 }9 |, B7 a) j) n
  170.   (with-board, F0 c  |; \, B# f0 I1 L
  171.    (concat (buffer-string)9 E6 C: ~" j  n6 K
  172.            (if (game-over-p)& V" w5 s& r6 X5 C$ z
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))) R, r( E0 h4 J$ M6 D
  174.             (format "第%d步" (1+ (session "step"))))))); m/ X  g( \$ `" J7 J$ a/ }# z

  175. # O6 y( C: Q0 L6 ^0 d0 u& ]% ~
  176. (defun board-position-parse (cmd)0 l1 x( S* `' I  e
  177.   (if (= (length cmd) 2). B" \/ q0 L9 T6 n8 h/ X# O
  178.       (list (string-to-int (substring cmd 0 1))) {4 N/ s5 q  p6 r6 ]; K" R
  179.             (string-to-int (substring cmd 1 2)))2 Z5 Y* y9 x! [8 Z" C0 }
  180.     '(0 0)))
    $ l3 M$ q8 Q% X

  181. ) i- U8 n+ _% z+ Q; k) Y
  182. ;;; 游戏房间
    & R6 f% Y9 V/ W% f4 p: {
  183. (defun game-room-init (cmd)
    , s+ i) q: I" I$ ]9 u: }
  184.   (let* ((middle (string-to-int cmd))2 N& }1 ?+ [; |0 n4 u( E
  185.          (size (1- (* 2 middle))))2 X" X. B0 w5 N/ V3 {; H! `
  186.     (with-board& x; |/ @$ \& @  R# r9 k, Z7 N* y" \
  187.      (board-init size)
    0 ]5 _6 Z! Y4 c1 R, y3 D) |0 M
  188.      (board-put middle middle)))+ t7 y- D0 Y" H4 X5 s
  189.   'game-room)  y! z# i6 r/ C5 N" ?6 j5 B: i

  190. , c5 m. f# w4 O3 Q
  191. (def-room game-room
    ( C, `, P' a0 E9 S
  192.   #'board-show
    6 f: g. L4 U5 x: e4 i( r% j
  193.   (t (lambda (cmd)& L  c: I9 V( n+ D' g1 x
  194.          (with-board
    " r: z3 j2 @* v! D
  195.           (if (game-over-p)
    * n4 C* p4 c" ^. T9 _
  196.               'living-room  e  _4 F9 ^* m- N$ U2 \
  197.             (destructuring-bind (y x) (board-position-parse cmd)+ M" k( E% P* z- G4 S: W- [  U5 I6 Y
  198.               (when (board-contains-p y x)- r' T9 ^$ D" s: K
  199.                 (board-toggle y x)
    0 b" ?- d$ P. f1 I. x  h% b( m7 \: {
  200.                 (session "step" (1+ (session "step"))))
    & o* z3 A8 q8 k3 E. ]; C! y
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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