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

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

  1. 2 V! K2 t. \2 {- D
  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;">;; 定义新的游戏地图
      C# }* E: A5 [$ t" U$ }
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL% U, ~" W3 I! r% R
  4.   'tutorial-room-0)                     ; 默认的入口. d0 J+ [+ P* h) g- J1 l
  5. 0 {4 |& a0 Y3 m+ D" P+ c
  6. ;;; 游戏大厅
    - q6 J* h: }+ P& }; ~. \- X
  7. (def-room living-room- Z4 F( A+ }$ C1 B& L
  8.   ;; 进入该房间后的提示语# l8 w+ \, p1 F9 d; p3 |/ o% f+ a
  9.   "1. 教程
    . i' O* q" ]2 u! `2 E( V
  10. 2. 入门(3x3)/ G# a4 X$ [5 b- U: o& y
  11. 3. 初级(5x5)
    : {3 P6 L: t" ~. v1 R
  12. 4. 中级(7x7)
    # G. H# c2 W; L4 k( h# d
  13. 5. 高级(9x9)
    8 h$ q5 Z+ a4 Z4 I5 V/ k0 F
  14. 0. 关于作者
    0 x/ v$ C% _0 J( E. G% N' B
  15. 请选择1-5开始新游戏:"
    & J- |; A" |; d* V. r
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    6 B' s7 r$ ?) m
  17.   ("1" tutorial-room-0)
    - V. q) M) O; \% k
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    . m; E2 F  ]( f! I$ t
  19.                                         ; 相应的返回也可以为函数,动态返回房间名# g% e4 |: f/ z
  20.   (t living-room))                      ; 如果条件为t,为永真
    ; H0 s7 A; }5 Q' S+ N

  21. , P: R! O( A: C1 \7 c$ g
  22. ;;; 作者信息
    2 W; l3 P$ y) E- m& H
  23. (def-room about-room3 E' j- k' _$ R7 I& p  n
  24.   "作者:redraiment& E* [( E( ]% p
  25. 微博:http://weibo.com/redraiment
    $ \4 `: s( G$ @& B+ N$ X1 t- Y. m2 y
  26. 有任何建议,欢迎在微博或微信上联系redraiment。. G  E! D( n- c" \
  27. 请输入任意数字返回游戏大厅。"5 Z, q) G; L6 ]! `: p
  28.   (t living-room))5 l' ]8 i5 |0 p% k
  29. + u9 b/ f( i5 a( K3 G
  30. ;;; 教程( O' K: _) t: j
  31. (defvar *wechat-5x5-tutorial-rooms* 0, k$ f9 t8 g4 x) Y) v( v2 w1 K
  32.   "The number of tutorial rooms"), W8 d+ X8 \9 h% v4 E/ I+ b+ w
  33. , g# ]' G- C4 q; c& N8 _: C& o' n
  34. ;;; 简化教程的定义
    " n5 p, a5 r- R1 }4 N- n
  35. (defun string-last-line (content)6 C7 J& H/ W; p+ U
  36.   "多行内容组成的字符串中的最后一行"
    + Y0 h! @& Q+ F4 U1 T- S# }
  37.   (with-temp-buffer
    6 Y+ D% \, ]6 b; j9 s* t& I
  38.     (insert content)( O  y7 t0 q. Y/ o
  39.     (buffer-substring (line-beginning-position)" l6 o% ^' P3 T& o- `) W) S
  40.                       (point-max))))% V& M: D7 N  C4 e# \

  41. ' @: K  G  E# g
  42. (defun def-tutorial-room (prompt)* M6 x% d4 k2 ]& f( \2 d
  43.   "根据提示语自动生成教程房间。) T9 ?6 p' p/ j- Z5 E- `
  44. $ i7 r' T& B( C3 V2 i' P, T% z
  45. 1. 提取最后一行作为问题;* I7 c& M1 \9 @/ U3 b' |
  46. 2. 分析问题,获取期望用户输入的内容;/ E6 D! Y1 z' w9 i! v( n% ?' |/ k
  47. 3. 定义教程房间和重复提问房间。"9 A# H6 K6 M" z& F
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))* ~3 E: I* _& g! w# ^
  49.          (repeat-room (concat room-name "-repeat"))& K) B  w& x) W, Y
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    ( J  E9 v3 n  c! g! F$ k' |3 q  j
  51.          (question (string-last-line prompt)). G" a/ h4 o4 K; Q8 ?$ o/ r, O$ l, |
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)5 z) p+ R- I7 j4 W4 ^  O
  53.                      (match-string 1 question)))
    - i( R7 U+ r, Q& o* c
  54.          (doors (if except
    . o1 D4 \0 F, n( J; u: U. w7 W
  55.                     `((,except ,(intern next-room))3 T( R( ?7 t. v2 l
  56.                       ("q" living-room)8 o6 \5 G+ N. U
  57.                       ("Q" living-room)1 }5 |8 u5 S( B) g
  58.                       (t ,(intern repeat-room)))
    % p8 Y3 c1 J1 }& }
  59.                   '((t living-room)))))
    0 T0 m) d3 [: j+ K1 Z& x( d
  60.     (def-room-raw (intern room-name) prompt doors)1 l4 r( l0 f; e4 g' X
  61.     (def-room-raw (intern repeat-room) question doors)))
    6 x9 p# u! K" p; K

  62. * k, w! E  u/ e/ ^1 p
  63. (defun def-tutorial (&rest prompts)
    # c2 b9 k6 M3 T
  64.   "批量生成教程房间。"2 b' \  g7 a, Y/ e8 E# m3 ^. {
  65.   (dolist (prompt prompts)
    $ w$ G4 O5 e3 C# |9 D. ?( }
  66.     (def-tutorial-room prompt)))* t% \) m& f& }6 {

  67. 5 R9 N( t! t8 Z/ ^5 @
  68. (def-tutorial
    / r/ e! Q. |$ l  Z# Y
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    & |$ Y5 g9 {$ s: T) ^5 V
  70. 1. 教程% t& ?, z4 h9 ?: B; O
  71. 2. 入门(3x3)
    ) n" S3 d0 D5 v4 t. ?2 v) u
  72. 3. 初级(5x5)% ~% t, Q8 o2 {3 C
  73. 4. 中级(7x7)2 `" |5 J( z" R# E6 g
  74. 5. 高级(9x9)
    ) Q( \. s0 A" O* V  J
  75. 0. 关于作者# ]$ Q+ A' }2 S& J, \+ m
  76. 请选择1-5开始新游戏:
    : N" [" P) }& z8 _. W' u; f
  77. 您现在正在游戏大厅里。
    ! u- ]% D1 P9 i* s7 h
  78. 请输入“2”进入入门级房间"
    : Z' _* c7 o7 m7 J) A& Q
  79.   "  ①②③( q, a5 |7 ~+ G3 G% F
  80. 1 ■ 
    * s5 t0 H, r) A+ i7 I
  81. 2■■■
    % Q% B- G* D& y" o
  82. 3 ■ 
    ; F5 B7 p2 T' f  f( K# _' X6 w' F
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    2 f4 ^+ b# @1 Z
  84. 请输入“22”来关闭第2行第2列的窗户。"
    ' ?: h# t. z: s. v
  85.   "  ①②③. B0 }; C, ?6 B: W1 Q
  86. 1   
    " L; c: C( N& z+ n: d0 M* @  d0 E- j
  87. 2   
    " X2 T+ G6 I# D; o6 r
  88. 3   
    % l* \; c/ E7 u; W
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。2 y, z7 V; l, \6 j# l* G
  90. 请输入“11”,试着开启左上角的窗户。"
    8 Q: W6 t# i" g% _
  91.   "  ①②③9 p  U* _. I* F1 E
  92. 1■■ ( x0 |/ P% _6 d6 v
  93. 2■  
    , `  h9 s7 c; O+ m* S- n4 E: m+ o* o
  94. 3   5 u) ~! [' i: D& p$ Z3 ~  u) U% j
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。+ i3 ?+ @* X- f4 d% @, \1 o1 O
  96. 请输入“13”开启右上角的窗户。"2 f1 C$ X/ A7 v6 @) h8 b
  97.   "  ①②③+ [7 O4 _# [9 J: N* h% `9 W' s- V" M
  98. 1■ ■
    8 i. x4 U) P, q7 f2 o! g
  99. 2■ ■
    7 _5 @# j& F2 J, U. g+ E: C/ N6 _
  100. 3   ! J7 m0 ~1 P/ V: B
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    - V/ d1 [% A) y2 {: h4 W
  102. 请输入“31”开启左下角的窗户。"
    ; y+ @0 W5 O9 O
  103.   "  ①②③
    * Y) @/ O9 M9 f9 I* d4 j' J1 n
  104. 1■ ■
    : e9 m5 D% V3 b1 N9 K! X
  105. 2  ■
    + B! i7 Z6 [" A4 `+ d
  106. 3■■ + K6 t6 ]; ?% N) r
  107. 此时,总共有5扇窗户被开启了。
    . _: @. C, i. D2 K% Z
  108. 请输入“33”开启右下角的窗户。"+ B/ a% h  S4 r
  109.   "  ①②③+ `+ o) r1 ~* s$ n9 X- U; ~5 U
  110. 1■ ■4 i, C3 q  B* R& }; t
  111. 2   : A& E( [2 p/ r5 P; r' r
  112. 3■ ■, p# q: j/ a  H  J% ?+ A
  113. 现在,只有四个角落的窗户被打开。
    6 r; V, i4 A. N! o. f. D
  114. 请输入“22”完成最后一击!"2 J$ V; f. o- W& B, z. L# Q" J
  115.   "  ①②③
    0 ]8 E. Y1 \, u6 }) c
  116. 1■■■
      T4 ~% l  u& e
  117. 2■■■
    7 Q3 E* c4 _0 O$ P- k
  118. 3■■■
    & J0 L3 \3 l, H* F6 K) r
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    5 _" t3 ^( M: o
  120. ) j2 e+ Z. o8 F! {
  121. ;;; 棋盘* ^+ f/ I; e& M& Z0 b& b% Z2 t
  122. (defconst *wechat-5x5-white-chess* 122888 Y8 ^0 g' B9 a! e) F
  123.   " ")
    7 o' t  m& R8 o1 r

  124. 4 m  a* Z) v. m, [. f8 s- M
  125. (defconst *wechat-5x5-black-chess* 9632
    5 q7 S" i- s+ l3 w
  126.   "■")
    9 ]9 y2 g& @* j# b% _. i1 t' T

  127.   r" V) f" W; ~) a" M  G
  128. (defmacro with-board (&rest body)' E1 p& b" Y! X( @2 `
  129.   `(with-temp-buffer7 y. {6 O* L, B$ N' M. t
  130.      (unwind-protect
    6 d& j5 u; ?0 I$ {6 z
  131.          (progn
    ; v. B: i: ]3 k; r% z
  132.            (if (session "board")
    : Z1 Z9 E, \) S$ A0 f
  133.                (insert (session "board"))), h! d" I/ ]% g& {" f% _
  134.            ,@body)" Q4 B- D5 Q! q: A$ T7 N4 p
  135.        (session "board" (buffer-string)))))
    - {6 R% L, K( G7 T( I
  136. ) s1 E7 T2 f1 `+ G; p5 T0 h% G
  137. (defun board-init (size)
    0 `* n+ ^& N3 ~7 R1 P: O( e, A
  138.   (session "size" size)/ `& C+ r% D+ O- f4 k$ {
  139.   (session "step" 0)/ K( `/ z7 }+ `) I5 ^3 K3 R
  140.   (erase-buffer): B* n  {" [& @& g$ b3 v4 t
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))+ O, X0 u% f! a1 ]7 {
  142.   (dotimes (row size)
    # }  V- A8 e  I8 B0 b) x# C
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))- f8 S& y* G  _' d

  144. ! R! X- g% q. R
  145. (defun board-contains-p (y x)8 V/ r' m) B" `6 L
  146.   (let ((size (session "size")))7 R; f! g9 J: ^* z
  147.     (and (<= 1 y) (<= y size)
    , e( r3 {/ f2 ~3 g
  148.          (<= 1 x) (<= x size))))  @4 X3 b" P2 H3 k
  149. , `9 y% {" p) F: a
  150. (defun board-toggle (y x)* N: Y: B% o- k3 C4 j) w: @
  151.   (when (board-contains-p y x)
    " A! a, H$ g2 k4 x
  152.     (goto-line (1+ y))
    - e) z, B/ ]/ d9 P" X# j9 b$ m
  153.     (beginning-of-line)* d1 c1 q0 o; l3 J0 W
  154.     (forward-char x)7 u5 v2 C5 E4 C# m% W' t& G
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    ' Z: r' q* \: h: T
  156.                 *wechat-5x5-black-chess*
    & ]7 Q3 y; H" a
  157.               *wechat-5x5-white-chess*))
    " x! B: W9 ^; `: I  A9 y
  158.     (delete-char 1)))6 T3 {( C& E: [8 Y( W; i) @0 y3 P

  159. 4 m1 }/ B' }- }1 h* _( o
  160. (defun board-put (y x)0 c$ }2 i$ ?2 a1 s" e+ ^
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    7 x% y; d; F7 n% w
  162.     (board-toggle (+ y (first dir))) t1 y$ C/ I& O3 i4 m
  163.                   (+ x (second dir)))))* P, N# _& K% i9 P* _* r
  164. + E) }( a4 [4 ^4 u6 k; s
  165. (defun game-over-p ()
    " c' P0 z4 B; U/ M3 [# N
  166.   (beginning-of-buffer)* @2 N- o! I# ?# b) X1 G1 M
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))! w. `5 V" k8 a; q' w/ _

  168. $ m- O* \# r" h- X. y4 i. `
  169. (defun board-show ()$ f" |0 d0 Y+ W& E. V
  170.   (with-board
    4 v7 P0 }7 L' o0 p) _; T/ T
  171.    (concat (buffer-string)5 k  a3 w5 F/ X! b, y; {) n4 L
  172.            (if (game-over-p)" [% d: ]+ u9 h1 G
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))% u. s; s, M* X. @2 u+ B
  174.             (format "第%d步" (1+ (session "step")))))))
    9 z* O. o0 J- C2 i6 w
  175. $ {# X  C4 T. g
  176. (defun board-position-parse (cmd): v# ?0 r0 s, [1 H% v3 Y1 y
  177.   (if (= (length cmd) 2)
    8 H$ G/ T2 C0 u! n! p
  178.       (list (string-to-int (substring cmd 0 1)); y7 U- N9 J, K, [7 y4 X8 Z8 Z4 A
  179.             (string-to-int (substring cmd 1 2)))
    ) @0 O: Y+ p7 v. [1 P( n7 c- I
  180.     '(0 0)))- [2 U6 H; b0 q. y6 h2 d; V  N* c
  181. 9 N  t9 }6 g' i! D6 W7 P( e
  182. ;;; 游戏房间
    7 m4 u9 o7 W* Q5 n( u3 f
  183. (defun game-room-init (cmd)( q( }" P# F/ `8 @$ l
  184.   (let* ((middle (string-to-int cmd))4 m8 t4 W5 _5 U1 r: X. L. i0 \- F1 k
  185.          (size (1- (* 2 middle))))+ y% C% a6 N4 N- b
  186.     (with-board7 |. _$ c" ]2 K6 }# [% i
  187.      (board-init size)
    . E( o3 w9 B/ a% c5 x; t4 q
  188.      (board-put middle middle)))
    7 `( N  ~/ H0 F, [( Q
  189.   'game-room)
    . k; w" |' L6 P, ~, G- b( M" @

  190. 7 n6 q( s; M4 B" H. q! l; d6 t
  191. (def-room game-room3 S7 E% L! v7 u0 p
  192.   #'board-show
    0 t3 \. ?3 K. r7 {- Q
  193.   (t (lambda (cmd)" E9 `* d( `: T  K
  194.          (with-board1 y  {2 i4 K7 ]1 E1 S8 Q
  195.           (if (game-over-p)$ S  _' i* t; q0 f% M( C5 b6 d
  196.               'living-room
    ! f: z6 B& V7 h. Z- X
  197.             (destructuring-bind (y x) (board-position-parse cmd)5 O9 ]/ x1 l  J5 X* J- D: O
  198.               (when (board-contains-p y x)1 e. v  A2 }' D  y
  199.                 (board-toggle y x)( ?8 v( n9 W6 s) p: ~( G" M* O( S8 h
  200.                 (session "step" (1+ (session "step"))))! ?2 d1 H5 h  ^& D
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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