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

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

  1. 0 K& y3 h. n* ^& ~! j4 C
  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- ?. u/ _% N% j6 j
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL- U, M( o/ a( u' K
  4.   'tutorial-room-0)                     ; 默认的入口
    $ |7 y5 J. v0 [

  5. ; w( K( ^5 @0 D; [- U
  6. ;;; 游戏大厅5 ?  T& P0 Y& M0 C
  7. (def-room living-room2 {  v$ p. x9 v
  8.   ;; 进入该房间后的提示语
    # W) A( w4 B1 L6 D6 I0 F0 f
  9.   "1. 教程- ?8 q- F# ~1 m) D
  10. 2. 入门(3x3)
    $ I, B4 Z6 H& d" d' [2 k1 O
  11. 3. 初级(5x5)
    . T/ w' q9 I" R& k8 ]# A* R0 H4 i
  12. 4. 中级(7x7)
    , }- N! x3 L/ y% E2 U  m) u
  13. 5. 高级(9x9)
    ' o* J# N  m( T2 Q7 M" u
  14. 0. 关于作者
    2 s# d8 Y8 V9 k4 j
  15. 请选择1-5开始新游戏:"
    3 x7 i* D3 Y8 `: A% V
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名, b% S: P5 Q- F- G& I& k3 C4 a  S
  17.   ("1" tutorial-room-0)) I( t  |% e; K
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    ' y! ]) Q% y5 I5 j
  19.                                         ; 相应的返回也可以为函数,动态返回房间名1 D. n& V) [2 ?
  20.   (t living-room))                      ; 如果条件为t,为永真. p6 W3 |+ _8 d  w  g

  21. 3 J! H& s; ?. V
  22. ;;; 作者信息
    - I# J9 P; F: b& R- ?& b
  23. (def-room about-room, M+ B: |5 Q9 a7 H! L2 a
  24.   "作者:redraiment8 h0 m( Q; @/ O2 [6 |+ j" n
  25. 微博:http://weibo.com/redraiment" O: U2 J5 Q- k& T$ H9 M- ^
  26. 有任何建议,欢迎在微博或微信上联系redraiment。3 z  t4 F9 ~( h. W7 |* l
  27. 请输入任意数字返回游戏大厅。"
    ( \4 i$ C' H; K6 ^
  28.   (t living-room))# q( }& h" Q1 y

  29. / n9 v: f2 b& N9 F* K2 y3 T
  30. ;;; 教程" j  s* _$ r; T$ m0 C
  31. (defvar *wechat-5x5-tutorial-rooms* 0: M0 A* v8 y  G- q% q
  32.   "The number of tutorial rooms")
    , P- P" {2 E. d( {; B
  33. * x. k2 l6 V; [
  34. ;;; 简化教程的定义: o! K: P' S: n
  35. (defun string-last-line (content)
    , a: }, v& G- Y& E2 c: v
  36.   "多行内容组成的字符串中的最后一行"$ d% _1 D+ I' R2 m: h5 _3 i4 K; p
  37.   (with-temp-buffer
    ; u' Q6 C, v. h2 q
  38.     (insert content)0 S# b; N! Z$ v; Q2 }9 U
  39.     (buffer-substring (line-beginning-position)+ U, i# N6 |( z+ }$ @$ y5 k
  40.                       (point-max))))
    ( K' M% H6 D' N! b  o  w9 g  o- d
  41. : N' t0 V4 g0 t& C0 K) ?2 x: c- I
  42. (defun def-tutorial-room (prompt)
    7 S# E: Q3 X: L: Y4 y( |
  43.   "根据提示语自动生成教程房间。
    3 J+ a# [1 q: u# R! s; \; ~
  44. 9 _" I) o9 g) `' w0 r5 @  n
  45. 1. 提取最后一行作为问题;) S6 U! x" Q) {! N( ?
  46. 2. 分析问题,获取期望用户输入的内容;
    , s  U: |2 m( m( O0 {% A
  47. 3. 定义教程房间和重复提问房间。"
    $ q3 X* ^4 |; N; t! |
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    & O; h9 ?5 j: B8 B# J. n
  49.          (repeat-room (concat room-name "-repeat"))4 Q  k7 I& M+ J: |; y  u
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))" M* T8 ?3 _6 g! p+ ^2 a1 B7 }
  51.          (question (string-last-line prompt))
    ' N/ @3 v8 C' Q
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)7 i! E. E7 O) L2 `2 o9 K+ h, x! d
  53.                      (match-string 1 question)))# s  t$ F8 Q3 v9 R% _
  54.          (doors (if except
    0 w% i5 ?& {7 [  M3 q8 _+ W
  55.                     `((,except ,(intern next-room))
    - [/ L2 S  F2 b+ t& t1 @
  56.                       ("q" living-room)
    ) s/ i( R8 K8 I3 \" |
  57.                       ("Q" living-room)& R) o* ^5 c' q% t
  58.                       (t ,(intern repeat-room)))
    4 m& g3 w8 F) {
  59.                   '((t living-room)))))7 f" W* b2 Z; v3 V- k
  60.     (def-room-raw (intern room-name) prompt doors)
    & ^6 ^6 \1 i( D( v4 C, q6 {8 H
  61.     (def-room-raw (intern repeat-room) question doors))), O+ I6 }1 @( n; h, \! j% X

  62. 3 \5 j' ?, _3 a4 p1 b, v5 j
  63. (defun def-tutorial (&rest prompts)
    8 k5 g' I. B' s# }
  64.   "批量生成教程房间。"
    6 m  B5 y& ^" `5 _. q( l4 a
  65.   (dolist (prompt prompts)7 y: a! Z* `; w6 M: \9 P% ]7 ]
  66.     (def-tutorial-room prompt)))* J+ ~2 N* y9 C# f- W! O. N0 Y+ r; a' \

  67. $ Y6 Y& ~! h- H9 S8 j: r0 i
  68. (def-tutorial, k5 M3 L/ d1 Z" W2 ^; k% \$ R+ U7 r
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。, q, l1 `7 M8 \% p
  70. 1. 教程+ z5 V  _8 e/ ^* W
  71. 2. 入门(3x3)+ q1 Q0 i; B, S: Y# Y. H$ f
  72. 3. 初级(5x5)
    : v) J/ i, D8 }4 S8 D3 P1 Z
  73. 4. 中级(7x7)! V/ v' r! [) T6 A' {4 C
  74. 5. 高级(9x9)5 i8 ^: S! g' |# B) h; z9 r
  75. 0. 关于作者' i# E' c/ U) q. A" ]
  76. 请选择1-5开始新游戏:( @+ e; s6 S% c( E6 g) y0 V
  77. 您现在正在游戏大厅里。/ g" n1 O$ W3 W  ]/ T* u! H$ Y
  78. 请输入“2”进入入门级房间"
    ! e9 w% R" ^. S3 J
  79.   "  ①②③7 {7 W1 E6 u9 I. t2 }$ W
  80. 1 ■ 
    % A5 I& Q; v& Y' {) V
  81. 2■■■( W, |0 ^. [- `6 i. B$ e6 i
  82. 3 ■ 
      ^- w% d- C* {* w
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!  @. v  A" h2 b* c9 W/ V9 i
  84. 请输入“22”来关闭第2行第2列的窗户。"
    : ^; d7 e, ?9 d( t* c5 I
  85.   "  ①②③9 [0 Q7 R, i0 x' Y7 \7 s. |9 J
  86. 1   7 j; a% J( m  [7 v# u& O/ s7 Y- B- I
  87. 2   * g2 t/ B  J2 I$ `& ]' r" M9 T' G
  88. 3   
    ! M4 G3 Y1 R$ b
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。, w! l; v4 n& u+ q- r
  90. 请输入“11”,试着开启左上角的窗户。"% F6 r# i8 t. l* D  S
  91.   "  ①②③
    : z2 u, n  q! [
  92. 1■■ 
    3 k  j5 P* Y* Z$ |+ f
  93. 2■  
    . }7 F3 Q  c& n' S7 ?; p+ c
  94. 3   
    % j4 ]* o6 F! [: K% R
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。: |" {: H2 T# k" r6 e
  96. 请输入“13”开启右上角的窗户。"1 w! s$ u- Q  o1 Z- m( M8 o
  97.   "  ①②③
    " d! M& \- }4 a8 Z
  98. 1■ ■; R2 F/ P2 S4 u1 ?0 }' O
  99. 2■ ■
    ! J: D! _2 ]1 w2 n
  100. 3   - \0 c4 E5 w  u* y& ]3 g: P) ~
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。/ U; [+ j6 s& ~* G8 ]" ]
  102. 请输入“31”开启左下角的窗户。"
    & o8 y* D( o  c  T, W* w/ c
  103.   "  ①②③7 s" ^3 q- @4 h4 \. d+ f
  104. 1■ ■' b) u6 ^$ y- W, B# N, A
  105. 2  ■% }0 P# V' H2 }6 s& b
  106. 3■■ * L7 F/ k. n' i# P$ _+ r
  107. 此时,总共有5扇窗户被开启了。- L5 k/ z' o. a* @" W$ z4 k
  108. 请输入“33”开启右下角的窗户。"
    # |# |  z+ M- F# f) }3 N' z( r' _" M
  109.   "  ①②③
    : M0 j. k8 Z9 p( E' n; p& Z
  110. 1■ ■5 ^0 x9 q' t3 G
  111. 2   ( k/ ?& h5 ^' o' ~( D+ \' H
  112. 3■ ■; V5 G  S8 F/ ^1 t& v/ `
  113. 现在,只有四个角落的窗户被打开。
    ! z& `' S! j0 [: {  V$ L! D
  114. 请输入“22”完成最后一击!"0 ~0 Y, @" I2 j
  115.   "  ①②③; P  k. T  y1 j* x
  116. 1■■■8 C2 d' G+ T7 p3 a6 |
  117. 2■■■! e  B3 l/ q, G. m
  118. 3■■■
    / n% _% d0 |9 e* |
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    1 Q  W0 e& D) b
  120. % N! O' G; U7 ^$ L
  121. ;;; 棋盘
    . ]. z) K- r7 n! D+ c+ z/ L2 b
  122. (defconst *wechat-5x5-white-chess* 12288* K$ C* V- _) D" v# a8 u$ R: `
  123.   " ")
    3 z' g% |* u9 Z2 r! D7 X2 ]

  124. 8 {3 P+ C: i! H/ y
  125. (defconst *wechat-5x5-black-chess* 9632* `9 E) _" l# o6 N& X
  126.   "■")
    + y7 {- _4 A& I1 H, L, E' Z: q. {

  127. ; f# z* F. G$ i
  128. (defmacro with-board (&rest body)
    5 x! b$ M3 P# I& ^) F
  129.   `(with-temp-buffer
    , W' y8 ^3 n- m5 |1 Y" N: r- [
  130.      (unwind-protect
    + f/ k& a& G# P2 m1 e  q2 W
  131.          (progn, M( Z) j( |7 o' v2 S) Y
  132.            (if (session "board")
    3 O" f8 m% k3 c& e7 F
  133.                (insert (session "board")))2 |' x- S! ^! b
  134.            ,@body)
    ( L; q/ m( n) g/ w/ M
  135.        (session "board" (buffer-string)))))% [- a; p5 }' l+ R5 I1 c

  136. $ H7 ?8 |5 K3 F, g: E9 I
  137. (defun board-init (size)* q/ q* n+ d& s4 i7 B% H  r
  138.   (session "size" size)
    8 o# m& U7 R4 m
  139.   (session "step" 0)4 O) Z  R9 s, u# _, F
  140.   (erase-buffer)
    ; K# h/ O& _& q) F% h; |8 `
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    ) q$ L/ |6 g% c3 U$ r
  142.   (dotimes (row size)
    % o6 R6 l( {# E3 W6 Q: \
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    3 z. X6 b2 [* Z* I2 g, O* v5 X

  144. + @' J1 M: ?2 A, `
  145. (defun board-contains-p (y x)
    ( g3 s, C) y" ]
  146.   (let ((size (session "size"))): n; G. K9 B9 Y  r, v
  147.     (and (<= 1 y) (<= y size)
    1 K( A% D) `* d, b
  148.          (<= 1 x) (<= x size))))5 p4 I8 x4 O9 w2 {3 h( o. t8 f

  149. . n/ R: E. o% L1 r
  150. (defun board-toggle (y x)% o2 V0 h5 u) `' N
  151.   (when (board-contains-p y x)9 l! f) l  H! {1 b) a9 ^" v' B- f
  152.     (goto-line (1+ y))* Q9 A# S( |" O3 ~
  153.     (beginning-of-line)$ N3 ]6 L! w) M( ~( [, k9 D* j1 X
  154.     (forward-char x)0 m1 r9 Y+ f* \1 L, }6 _8 R4 u6 R
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    ; W/ j- n8 v/ D* X" w
  156.                 *wechat-5x5-black-chess*
    + w5 ~- p$ O6 `: t" L% M
  157.               *wechat-5x5-white-chess*))
    ! }$ |, Y3 A  O  }$ G1 ^6 q( m7 N% I: c
  158.     (delete-char 1)))- D7 t8 L$ \+ W

  159. 4 M6 l: S8 H- s
  160. (defun board-put (y x)- T" [0 P( t4 ~) z' t' s
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    " ^$ L7 s5 N+ [7 y2 \: r  `
  162.     (board-toggle (+ y (first dir))& j7 p6 J7 U2 Q7 A9 V' U3 v, K
  163.                   (+ x (second dir)))))
    , m# D  p7 v4 b- p4 U4 J7 R3 w; e# \
  164. ! a' s, x- Y( j: U
  165. (defun game-over-p ()0 @% x" W& f/ ?: U* d) v
  166.   (beginning-of-buffer): n/ T9 G. q; E
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))" L) d' g$ b' c; p) c# R

  168. - d$ y  t* U- V1 Q+ S
  169. (defun board-show (). l" g3 Q5 H% @* P$ I0 |
  170.   (with-board
    ; o0 w% F' ~$ p% I% s4 d
  171.    (concat (buffer-string)
    9 ~- C/ [" k1 X8 P7 `- l3 `
  172.            (if (game-over-p)! y1 U/ x1 R" k4 C3 q
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    : @7 B1 ?" |) K
  174.             (format "第%d步" (1+ (session "step")))))))
    , p9 K0 [2 o6 U1 K: R. \
  175. % |3 V2 x: e! c% R5 j
  176. (defun board-position-parse (cmd)
    ' I' b' h6 e# J; `8 Q* i9 W, l
  177.   (if (= (length cmd) 2)
    4 H, Z; R1 _# K' ]0 t
  178.       (list (string-to-int (substring cmd 0 1))( P" Z/ ~9 C- Y7 x
  179.             (string-to-int (substring cmd 1 2))), Y* a4 r3 {$ T* r
  180.     '(0 0)))
    , a7 A" l' u( F. I4 F' w3 w9 H
  181. 6 |; n! h# Y% ~  q: h" ^
  182. ;;; 游戏房间" _( ~  u5 C8 V: W. A
  183. (defun game-room-init (cmd)
    ) r* R* l: P) H3 K+ b& {) x4 x! j
  184.   (let* ((middle (string-to-int cmd))
    . W& V! U, [7 `/ E) \
  185.          (size (1- (* 2 middle))))
    . K* o9 H9 S, w, o8 b
  186.     (with-board
    ) _* G! `! c. Q0 x& p( L
  187.      (board-init size)0 x* |* Y0 G) S8 s0 O- [
  188.      (board-put middle middle)))- e0 m7 E6 S; [# Z0 T6 r
  189.   'game-room)
    # Z1 @7 H& H9 c) d+ L# S9 p
  190. ' B5 D; t2 |1 x# x: n5 w
  191. (def-room game-room1 @. u! @' c3 f: R& g1 C% A
  192.   #'board-show
    3 Z) J9 V4 d( U! r5 v5 U. O# H& d
  193.   (t (lambda (cmd)
    " l, ?8 y* Z. A, y- O# P7 ?
  194.          (with-board
    % y5 a+ ]1 l. i: B& i
  195.           (if (game-over-p), n3 {" c. Y! N, n& D7 x
  196.               'living-room
    3 v. X8 U7 S, H' `, g
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    - W3 ], m+ B, j  Y9 {$ R) u) I
  198.               (when (board-contains-p y x), R! V9 ^: i% W& N* s! M
  199.                 (board-toggle y x); a, E8 @6 _$ Y, q* h3 w+ }2 s
  200.                 (session "step" (1+ (session "step"))))1 U6 s" K! g- F; S' ~/ f3 @) H. q
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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