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

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

  1. ( }- P6 `( M9 S: {$ ?  S
  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;">;; 定义新的游戏地图7 p$ R1 }1 c+ ]8 {. v5 i
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    ! O5 u- q3 z) ~
  4.   'tutorial-room-0)                     ; 默认的入口+ A3 m+ L/ J& Z7 m1 B

  5. 6 I0 R7 Y: b! s
  6. ;;; 游戏大厅
    9 P3 S" X" f0 Q5 T, g
  7. (def-room living-room
      W& L) {2 c( z* ~7 T. t1 z" T% J/ z
  8.   ;; 进入该房间后的提示语
    / I; I* H9 }' [: ~; c7 h7 R/ U
  9.   "1. 教程/ G2 g1 s5 t9 M4 [8 L
  10. 2. 入门(3x3)0 C" E) f. o% b9 i9 L( Q
  11. 3. 初级(5x5)3 R8 X. \( D7 @! M
  12. 4. 中级(7x7)2 W; B7 C" \0 o; `
  13. 5. 高级(9x9)
    , b+ W4 \$ S0 z7 ?9 X
  14. 0. 关于作者
    3 m0 Q0 P0 g" q# F, G+ w; {
  15. 请选择1-5开始新游戏:"0 ]1 [1 n8 h) X! u& ?) Q
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    * Y, O/ n5 W7 v9 k8 q0 M; ?
  17.   ("1" tutorial-room-0)/ d5 j+ x+ x2 ]' U) [! d
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    9 u! B! f" c1 w
  19.                                         ; 相应的返回也可以为函数,动态返回房间名& _1 Q& ?. s8 z: p
  20.   (t living-room))                      ; 如果条件为t,为永真- U' M: X+ p+ b1 O" Y3 p. N  \- y

  21. ( i/ E' {4 A# F$ M
  22. ;;; 作者信息
    3 y7 i4 D- a+ v& F; ?4 ~
  23. (def-room about-room
      W/ \/ T* j# H0 a$ N5 h6 y" ^
  24.   "作者:redraiment. P2 T# U) T" N' w4 A6 z
  25. 微博:http://weibo.com/redraiment
    + O  ^) ]$ x" c. I/ G/ N
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    7 }/ h& B5 {1 h& M
  27. 请输入任意数字返回游戏大厅。"" S4 u6 m; p3 j! `% r- k  ?2 l
  28.   (t living-room))
      ~4 l% F8 s0 A2 s6 S% G

  29. 9 e" c1 ~8 ?4 M8 z' ]& n" K4 ^
  30. ;;; 教程' ~$ s  |) z3 V) x3 O+ W
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    3 Q/ O5 [$ J' h9 v) o" M! m1 o. \" R& S
  32.   "The number of tutorial rooms")1 a) j/ O5 \% R
  33. - E0 D3 p) R9 \
  34. ;;; 简化教程的定义
    + [2 q% t/ O. Z- }# }
  35. (defun string-last-line (content)
    * k# c- ]+ {$ V% v
  36.   "多行内容组成的字符串中的最后一行"
    6 @3 d! p# [8 O9 C- w6 l
  37.   (with-temp-buffer
    7 c% B! c# `: P
  38.     (insert content)
    ( v0 f7 U/ o7 K# e
  39.     (buffer-substring (line-beginning-position)
    7 N9 B8 r$ l; x$ [( N8 `
  40.                       (point-max))))
    ' h6 K. k) [, h% O
  41. , h: B6 y) O; n* j" [
  42. (defun def-tutorial-room (prompt)
    # i4 Q3 X: R; ?7 p" U  H3 a
  43.   "根据提示语自动生成教程房间。' i* z+ I$ s( z( ?8 R! l  f" o

  44. 8 t0 u8 `0 \2 Q$ T# M$ j8 n
  45. 1. 提取最后一行作为问题;
    3 u# {9 {. p0 @. H
  46. 2. 分析问题,获取期望用户输入的内容;
    - F& ^2 {# h2 I# ^* C
  47. 3. 定义教程房间和重复提问房间。"/ j& w, }9 c. ~$ [
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))9 L$ Z+ G' |0 x( S. s" Q
  49.          (repeat-room (concat room-name "-repeat"))6 k+ z$ N. E* E+ T6 N' t* Q
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))+ y6 y2 v7 [. A6 E
  51.          (question (string-last-line prompt))' J/ P* K, I: r0 c
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    ' n! }; E, ]" |: C. \
  53.                      (match-string 1 question)))
    ! \& [0 E4 E; ?9 p- E
  54.          (doors (if except
    2 V" J& [% ]0 ~2 @9 J6 p9 C
  55.                     `((,except ,(intern next-room))9 D7 z* @6 F( U5 F1 H, ^' |* a
  56.                       ("q" living-room)0 S7 T/ U* |3 `) B) d- @# Q# Z
  57.                       ("Q" living-room)* i/ A+ x% y# l
  58.                       (t ,(intern repeat-room)))6 K; K: P( `! U/ ^4 j
  59.                   '((t living-room)))))
    ; ^1 J4 S$ P5 k+ R6 ^
  60.     (def-room-raw (intern room-name) prompt doors)- K% X: @5 {) ]( a
  61.     (def-room-raw (intern repeat-room) question doors)))
    # ?4 g2 q4 ?6 R0 K2 h
  62. : k! b1 m" g6 K" Y) M
  63. (defun def-tutorial (&rest prompts)
    ) F1 \% Z+ x3 c/ F
  64.   "批量生成教程房间。"5 ~! U( w) E4 U' s$ S0 m3 M/ |0 ~
  65.   (dolist (prompt prompts)
    7 ~) |. z  x3 E8 g; E
  66.     (def-tutorial-room prompt))); n2 H4 Z6 e! F
  67. # U' I6 u7 ^) z
  68. (def-tutorial3 l3 a9 c0 [. w0 ?  w! R- h# R
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    / R7 j% l1 H5 B$ r4 U  |. |
  70. 1. 教程
    1 @( G/ ^5 z6 Q: Q
  71. 2. 入门(3x3)4 q& e: p, Q. O) ~( N
  72. 3. 初级(5x5)
    , ~" `* Y9 V! _" \3 v
  73. 4. 中级(7x7)8 A$ v, Y; i4 v' N
  74. 5. 高级(9x9)
    - Q) [6 B5 P& M& V% B; @+ G
  75. 0. 关于作者- h5 s- E/ y" X/ o# y6 M% r6 i1 h
  76. 请选择1-5开始新游戏:' Y8 A5 N* D! g' T
  77. 您现在正在游戏大厅里。$ Q, G) A0 y) u4 e
  78. 请输入“2”进入入门级房间"
    . M7 v* @0 v5 W$ R
  79.   "  ①②③
    0 M7 S1 {' u- v5 X0 j  [' h9 _- S
  80. 1 ■ 8 V9 f6 E/ i+ w* z; I1 u
  81. 2■■■7 K* W* W1 H* l' Q& m, G& R+ f
  82. 3 ■ ( Q% E* B% h' Z0 Z
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!( u$ t5 @0 A* K% m$ g7 p8 A
  84. 请输入“22”来关闭第2行第2列的窗户。"
    / x, ^8 e! G- [* d
  85.   "  ①②③% K  x, ]' C, M: \% b7 U0 P8 e
  86. 1   
    $ o' N% Y1 v+ ~
  87. 2   
    ; [' V0 j* J7 _& [1 `  W& g& m$ d
  88. 3   8 j2 @' N( z$ z+ z* u* y- S
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    5 [5 d$ I- H4 P  ?, c& a2 e7 [
  90. 请输入“11”,试着开启左上角的窗户。"
    * b& Y/ c2 U" I, E& p" G2 ?  p
  91.   "  ①②③; S. d$ M! r, }' b% d3 ]
  92. 1■■ 
    4 b. Q  K2 c+ P0 f4 y6 j$ H
  93. 2■  
    0 n! x" D/ G8 p  [- T5 F. q
  94. 3   
    3 h; U" j; O7 V4 i7 a* L9 L
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    % |& a) m& d' K" _
  96. 请输入“13”开启右上角的窗户。"
    $ i1 L/ V# k* d' e3 P; Z
  97.   "  ①②③  y. N0 o" H3 T- b2 P' @
  98. 1■ ■
    ' |, n5 X% G) I) B4 O- V
  99. 2■ ■) f% P1 J1 f7 D0 ~
  100. 3   " \3 E7 W8 W, l
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。; Z2 [; y. \$ E; E, N1 y2 U8 X
  102. 请输入“31”开启左下角的窗户。"
    : f  F3 t  g" V5 P1 z  z1 c: M
  103.   "  ①②③( Z7 G+ `% ^2 i$ C' @
  104. 1■ ■
    ( P- C; Z" j" _- ^* w$ C( ^# G
  105. 2  ■
    * Q3 J2 E" ^( H" B' r8 p2 A
  106. 3■■ 
    / I0 H0 y& i0 E0 L
  107. 此时,总共有5扇窗户被开启了。
    4 N$ @4 ?& i  W* x9 N
  108. 请输入“33”开启右下角的窗户。"/ }8 r. [! [: Y& A+ D( l% {9 d
  109.   "  ①②③
    , n- l: g1 L0 M+ u
  110. 1■ ■
    + i7 r2 u; b/ @1 Z6 q2 h6 v
  111. 2   
    1 t# E2 R/ l0 ]3 ^2 u9 a, |2 p
  112. 3■ ■
    + m. a7 b* ], m2 R
  113. 现在,只有四个角落的窗户被打开。+ ~8 d5 s, {( t& y
  114. 请输入“22”完成最后一击!"
    + L( ^# Q  o' J& P
  115.   "  ①②③
      N% Y# U4 @' T& u& ^
  116. 1■■■
    , k' Z$ P2 n- H. o& o: ?
  117. 2■■■. Q1 s! S9 h6 q- w! K! w3 j
  118. 3■■■! a; s9 j9 E( m7 _% J+ ^" ^
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
      W/ i/ b. A( K* I# C. L( X0 ]4 R
  120. $ w. |! `# N# R" Z7 {
  121. ;;; 棋盘, E  ?# w* I1 V3 x! N
  122. (defconst *wechat-5x5-white-chess* 12288
    * k" J& Q# v1 W
  123.   " ")
    5 Q5 f2 G3 K/ |! n

  124. # q9 o8 x- U  X9 T% Y5 D
  125. (defconst *wechat-5x5-black-chess* 9632
    * A" L5 o5 i* S6 R/ {
  126.   "■")
    " d  c' g- z8 s$ d! X) m' s4 G- Z

  127. / N; G8 A4 I4 ]. J
  128. (defmacro with-board (&rest body)- m" h( I  Q  T0 s& p9 o) U3 Q2 p& s$ J
  129.   `(with-temp-buffer% t8 Z4 ^: X7 G( l3 @
  130.      (unwind-protect% ^* s7 ~0 O& {& S  m/ E
  131.          (progn
    / D# _( X8 V, ~, v) w
  132.            (if (session "board")
    - u0 p( W4 V( o2 F( ~
  133.                (insert (session "board")))
    . }. ?1 y6 p' p# e; u
  134.            ,@body)  X8 G1 M6 J- O! H- i
  135.        (session "board" (buffer-string)))))7 e9 V9 q. n& f

  136. , I& o; N+ o8 k
  137. (defun board-init (size)
    8 q" t2 G5 U  Y. c" H. w, \9 [6 R
  138.   (session "size" size)! }( {5 x9 G( t! I
  139.   (session "step" 0)
    4 c# a  @/ g8 h- _. J
  140.   (erase-buffer)
    3 y/ i# h; O: b* s; _9 o
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))' _& W4 v: h5 d+ h0 R2 c2 J
  142.   (dotimes (row size)3 C1 a( d  V: V
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))* j! A  y6 T( U5 z7 M, N. g. V. X( u

  144. # F" {3 g% G2 q8 D7 A: Q/ H
  145. (defun board-contains-p (y x)  Q. g, l( Y0 J( k+ E7 ]9 j
  146.   (let ((size (session "size")))
    " q+ L3 h; \, c& @4 [1 f1 B7 d& |
  147.     (and (<= 1 y) (<= y size)" s$ b/ Q& t4 p4 T  F. h( v
  148.          (<= 1 x) (<= x size))))
    . n  j; i: e2 |) _# n" u
  149. 5 f) u) b/ ~$ n( Y! t
  150. (defun board-toggle (y x)& _, H  c. }0 w  E1 q4 a4 Z! q2 @
  151.   (when (board-contains-p y x)6 z- Z6 ^) J% P/ C
  152.     (goto-line (1+ y))
    & ?8 Q% j8 R# M4 E5 A2 X+ ?6 _
  153.     (beginning-of-line)  w" d& B! ?( ]( E; }
  154.     (forward-char x)
      g3 t, A# R, W) A: u! f) N. {$ C3 ^
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    7 P6 e2 }7 }4 H9 [  F- ]
  156.                 *wechat-5x5-black-chess*) W# d" W- Q& |& ?: n/ R  x, t1 j
  157.               *wechat-5x5-white-chess*))
    6 j# E3 b( E! ~9 b( T( M
  158.     (delete-char 1)))+ ^6 e) H& F! ?: J7 m
  159. , ^) ^9 ?; k  O. Z$ [; T9 l
  160. (defun board-put (y x)
    , S" e; U1 e  v! ^# \
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    6 {; |% k8 y# j) P$ \
  162.     (board-toggle (+ y (first dir))
    6 c; F5 A. C1 L8 `5 |* i& l7 k+ c6 _
  163.                   (+ x (second dir)))))
    & a' x* x' y7 K
  164. # L0 P8 j$ M2 z: p2 K3 `6 e5 b6 O1 Z
  165. (defun game-over-p ()* p0 l; s( @4 \. `& N4 s
  166.   (beginning-of-buffer)0 g) m/ I' @# N/ v$ H+ }
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    1 B  z) c2 ]- Y0 t; ^

  168. : v# U4 u4 j7 w) |( c
  169. (defun board-show ()
    " Q( E5 t: D9 }' i1 D# D4 g9 V! p
  170.   (with-board' h6 ?, t: ?8 V9 s/ {8 j! \
  171.    (concat (buffer-string)! m: W2 s& R9 ]. }7 C) I
  172.            (if (game-over-p)' b+ ?" C. f, c7 y) u. b5 J
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    6 i% i5 Y: \& N
  174.             (format "第%d步" (1+ (session "step")))))))
    9 \) O5 s! M. c! |1 H

  175. * m6 n5 h# U4 h) L! r1 g
  176. (defun board-position-parse (cmd)
    , p8 X3 k0 G- v6 y  k  h
  177.   (if (= (length cmd) 2)" T' u- i" J) Q0 T+ L0 N
  178.       (list (string-to-int (substring cmd 0 1))2 N' K; b0 w" _; J5 o" X
  179.             (string-to-int (substring cmd 1 2)))
    8 g- g; h, }" T) i' g
  180.     '(0 0)))- O* l5 e. p: q7 [

  181. & }1 H7 z# ~, q% r' q
  182. ;;; 游戏房间
    / a- |5 A- B! l4 {
  183. (defun game-room-init (cmd)9 |/ a/ Z& Q) `" M& ]2 {
  184.   (let* ((middle (string-to-int cmd))
    ) J1 ], z- X, `0 ^8 `
  185.          (size (1- (* 2 middle))))
    $ _, ~5 d4 [" N$ D1 m
  186.     (with-board3 K% C( H1 b8 I. u1 [4 d
  187.      (board-init size)' {/ e0 N/ p0 E) B# K7 |- C  w
  188.      (board-put middle middle)))
    5 s+ t( Q; G- p0 \, a! f
  189.   'game-room)
    # [% w$ W( R' W

  190. 3 U( \' n1 `. h6 C
  191. (def-room game-room8 q& p5 _% q! }0 f6 r
  192.   #'board-show
    & Z5 F/ S# z2 N3 s# a
  193.   (t (lambda (cmd)
    6 G1 q/ ^9 a! H- A, v* z
  194.          (with-board
    ( o+ C* D0 t7 s9 ]
  195.           (if (game-over-p)
    - a1 ]* U4 @8 t: ~: Z6 B# }- _
  196.               'living-room- u# C1 B6 B- o3 O% j2 F
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    0 m- M% D7 L; ]- L: D1 k' l
  198.               (when (board-contains-p y x)
    + W% d: @* U  [5 O2 C
  199.                 (board-toggle y x)8 Z" H8 t' w& l+ G3 ~( x- l# a
  200.                 (session "step" (1+ (session "step"))))( t+ l$ o+ P7 w. i& x
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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