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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
# U9 T9 t8 b& a+ D' B+ E借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. , E1 ^, W, x- ^5 ~  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;">;; 定义新的游戏地图
    - \9 W6 a; J' a
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL3 o( v  {% J2 q& |' [7 u
  4.   'tutorial-room-0)                     ; 默认的入口
      H  b, d7 k  `; y! A9 w* E( T

  5. - A2 @* }- t, A, b
  6. ;;; 游戏大厅+ Q: x3 t% o" S+ b, [  h. g* u2 X
  7. (def-room living-room
    8 c! s0 T" j( Z% ^! d2 o# c
  8.   ;; 进入该房间后的提示语
    ! t8 R; h0 {  K0 l  t
  9.   "1. 教程! a' k1 @4 j5 o* N) t+ j
  10. 2. 入门(3x3)* Y9 g! F8 M4 `' U" b
  11. 3. 初级(5x5)! l* E# @" l3 d# w' j
  12. 4. 中级(7x7)
    2 F. g2 L9 x5 v9 S# h
  13. 5. 高级(9x9)
    % v) R' O  f4 @6 A
  14. 0. 关于作者" f( h6 t$ d& N, `1 k
  15. 请选择1-5开始新游戏:"
    ; e* D/ G! l6 x: q# x- g* \
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    4 p2 z9 B. M4 o
  17.   ("1" tutorial-room-0)3 K( C3 D' |# K
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配/ k' Y: I# M2 w5 ]" I
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    % ^" J5 S. Y4 C. f- T
  20.   (t living-room))                      ; 如果条件为t,为永真7 Y3 `0 F1 X7 E  P) F; Z
  21. 7 K/ v+ ?9 i# B
  22. ;;; 作者信息% ~2 W0 K0 `5 H  g5 W
  23. (def-room about-room$ w; y) v: A: O6 p6 t1 p! G7 w. Q! I
  24.   "作者:redraiment
    ( X% {, M) r* t
  25. 微博:http://weibo.com/redraiment, G" ~9 V- H# f9 F# q* Y
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    ; ]5 E; o* r! X4 h
  27. 请输入任意数字返回游戏大厅。"7 u* ]4 P7 r" ^9 H7 X4 Z* H) A8 G
  28.   (t living-room))
    6 n0 r: s3 u5 i4 i+ G+ M
  29. 1 y% q3 J5 i! Q1 c, ^* _* Q2 s
  30. ;;; 教程
    ! V! t* |, E4 ~" t
  31. (defvar *wechat-5x5-tutorial-rooms* 0; u- V$ [( Y7 x' D3 H
  32.   "The number of tutorial rooms")4 I! y6 I. M6 K6 r
  33. 5 ?) L& b/ d% g. V) h
  34. ;;; 简化教程的定义
    " n8 y+ n. {- Q# r# g3 x- y% s
  35. (defun string-last-line (content)
    6 e; s! {+ p$ \0 [
  36.   "多行内容组成的字符串中的最后一行"1 `/ ~9 O4 J6 N5 J. Q3 V% t/ f4 ^7 |
  37.   (with-temp-buffer
    ! v3 c6 C' v4 K$ C: `+ ]2 n
  38.     (insert content)9 @' U/ Z0 H5 o# y  `. G7 ]
  39.     (buffer-substring (line-beginning-position)/ p5 m1 }$ t: ]) e7 m$ M
  40.                       (point-max))))& |6 G7 o) a& R$ J$ k
  41. ; ?  F# M/ k. q) y. O' u7 a
  42. (defun def-tutorial-room (prompt)
    ; y9 n9 H, [' i' {
  43.   "根据提示语自动生成教程房间。
    4 L" e+ \7 @! \( u$ l" |; i! v' r

  44. , x% N! _  J$ j
  45. 1. 提取最后一行作为问题;0 b* Q# f1 j, N5 L" D
  46. 2. 分析问题,获取期望用户输入的内容;
    2 z+ H9 ^- V9 p  m# y# M. ?
  47. 3. 定义教程房间和重复提问房间。"4 b* A3 h5 B/ L/ N
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))8 b& Y  x3 p/ s/ L5 k8 O2 R8 ^: h: Y
  49.          (repeat-room (concat room-name "-repeat"))
    " N6 y; e5 J0 ~6 _3 j1 v! c& _
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))+ l  `4 ~* R% o$ o8 a
  51.          (question (string-last-line prompt))
    " k% k7 M; o* a2 {
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    ' f& w( x3 g4 K
  53.                      (match-string 1 question)))
    2 x# F! a: X* I. W: Q" u
  54.          (doors (if except1 g* [: [) Q9 a) Q7 R2 S% y
  55.                     `((,except ,(intern next-room))# S4 z+ L- ~/ ?3 U' `& \
  56.                       ("q" living-room)0 I8 M" a6 \4 j+ }+ Q1 s
  57.                       ("Q" living-room)) K3 N, k2 m" U9 T5 L6 w' w
  58.                       (t ,(intern repeat-room)))' U6 t; l+ a8 U  @
  59.                   '((t living-room)))))" A8 ^* b  ]& ^& V; Q: H
  60.     (def-room-raw (intern room-name) prompt doors)6 I0 C1 L& ?& U+ `
  61.     (def-room-raw (intern repeat-room) question doors)))3 g# ?1 _, `, p" r

  62. ) l! h: x' i9 m/ N/ B9 t. e' t* z
  63. (defun def-tutorial (&rest prompts)
    2 @7 c4 A; y# ~5 p1 Q
  64.   "批量生成教程房间。"
    ) U  [2 q0 r9 ?0 Z8 A
  65.   (dolist (prompt prompts)$ N1 O' v+ }. Y2 g7 t* [( D
  66.     (def-tutorial-room prompt)))
    % ~) D" U% h0 i! c' q

  67. : J; j6 M& @* P5 ^& ?6 B1 N$ H
  68. (def-tutorial2 d) _. a1 W3 w. }
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。+ A. w7 e- s( }
  70. 1. 教程# Y+ q4 S9 O2 e' J. W' t1 s" L0 z: J3 m
  71. 2. 入门(3x3)
    " r8 p. I$ D6 _5 H
  72. 3. 初级(5x5)8 D# k4 }6 @( |
  73. 4. 中级(7x7)
    ' g  X6 T8 e- g
  74. 5. 高级(9x9)" g6 K& ]0 _1 G
  75. 0. 关于作者
    * {* |( [+ g3 y. v+ G( q
  76. 请选择1-5开始新游戏:$ x" Z8 A- R, ^' a
  77. 您现在正在游戏大厅里。# ]3 P  F- N, o9 O
  78. 请输入“2”进入入门级房间"& m2 ?! j0 g5 P9 y
  79.   "  ①②③( P5 R0 l& x, l& q& ~& m+ x
  80. 1 ■ 9 ~- Q2 u) B: Y: o/ j
  81. 2■■■: }9 J$ {. M+ R, N- j! Q. {' Y
  82. 3 ■ 
    + |: g, d& k& R; `* {3 h- I0 p% D7 l
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    ) b4 G4 j; w0 k. N+ I$ l
  84. 请输入“22”来关闭第2行第2列的窗户。"; g8 {" [. `; Z
  85.   "  ①②③
    / q3 M* `7 E1 h1 V( v
  86. 1   0 n; T/ b  }, q" a2 N3 q+ P# H0 w2 ^
  87. 2   
    : [! W3 k4 n% X( ?6 c7 B
  88. 3   
    + S6 G4 K$ A3 C  p$ t
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    * e  i6 `9 S; G- ~' `
  90. 请输入“11”,试着开启左上角的窗户。"
    : u" F* x# e' q& b  C; ~
  91.   "  ①②③9 d; J; o* n  V$ k! ^! D: w
  92. 1■■ ! U0 K: b/ l, N- W
  93. 2■  
    0 I, X0 J3 [2 W  q- [  z+ c( M  w6 p5 }
  94. 3   
    % e( v4 w8 n! M& N% r$ B
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。: L6 I. h  f/ ^3 V
  96. 请输入“13”开启右上角的窗户。"
    / N! w3 l/ [" n9 f
  97.   "  ①②③, K) T9 t- j; M: X
  98. 1■ ■: H  ~8 ]1 Q; @9 F0 e$ w' s- ]
  99. 2■ ■; d* C, ?- p. J3 A4 L
  100. 3   
    " I5 f& L/ h+ o/ D- P* P
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    8 s2 E: W+ |3 D; r% Q$ f( q
  102. 请输入“31”开启左下角的窗户。"
    6 O; H4 W2 e- w) n# C. ^3 Q
  103.   "  ①②③
    5 w/ d% s$ ?( z$ M7 C6 e5 I# Y5 K( l
  104. 1■ ■
    ( E1 x) Z) r. \7 _* {' T; Z
  105. 2  ■; v6 S5 b* U+ K+ {1 r3 |
  106. 3■■ 8 g- b0 @1 x1 l9 }# K
  107. 此时,总共有5扇窗户被开启了。
    ' c5 G' G1 ~  y  m% B
  108. 请输入“33”开启右下角的窗户。"
    5 ]1 n! a. m, h( b' _! e3 ^
  109.   "  ①②③
    9 y" U' ~0 t0 q$ U4 x
  110. 1■ ■  f5 ]" k; U- L0 i  m
  111. 2   
    / r, l* Y# X! s5 C
  112. 3■ ■
    8 ]" T! r) l/ {+ i3 i7 k$ z
  113. 现在,只有四个角落的窗户被打开。
    6 _2 J* Q$ N$ l9 n
  114. 请输入“22”完成最后一击!"; q* k# D1 o. O4 o  U
  115.   "  ①②③8 ^! Q1 [' V4 f9 Q
  116. 1■■■
    # K4 h$ Q4 K* D- I7 p  l! g
  117. 2■■■
    % T6 B% U" B( h% x+ c! b1 I# b
  118. 3■■■; L  U0 V' H+ d; v
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!"), ]9 D  v& d- i" `( `% n

  120. 6 Z& U- Y8 J: U" z
  121. ;;; 棋盘
    7 U1 ^" m* e, v  o  v/ d
  122. (defconst *wechat-5x5-white-chess* 12288
    / W# r) L0 K( O$ [8 n# v! I8 O
  123.   " ")8 i( X: d$ C4 D0 E# w; Z
  124. 2 Q8 G4 L" a8 {/ q3 M: V7 d8 J
  125. (defconst *wechat-5x5-black-chess* 96324 F* S4 T+ z( q3 P  O
  126.   "■")' v9 G0 S, F: y1 x3 `: v

  127. 2 ~9 C) j: R) z% Q+ ~4 r( q
  128. (defmacro with-board (&rest body), Y7 `3 n8 p2 Q1 c9 `
  129.   `(with-temp-buffer
    % Y2 l' H1 R0 _7 Q' K
  130.      (unwind-protect
    2 p2 O" |3 d4 V8 T6 Y# B) r
  131.          (progn8 [# ?" X( V6 H1 e% v, g# d( w  n1 Y
  132.            (if (session "board")9 h& h  `2 Q1 x3 ~* ]
  133.                (insert (session "board")))
    - C, |! I# U; `. @9 E! J- D
  134.            ,@body)% K0 s. `' {1 I1 |* U( F
  135.        (session "board" (buffer-string)))))+ k. d( q5 ~9 a
  136. 0 H" C" C' j, m% K# _
  137. (defun board-init (size)
    : v% Z. c( p/ e, d9 r
  138.   (session "size" size)7 T/ G# q& \9 J* P5 T4 z
  139.   (session "step" 0)
    " m5 I$ _8 U2 f& p
  140.   (erase-buffer)' K; \" @$ y) E% F$ `% O* b. U
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))" g( u8 T9 F2 G3 g# T. Q
  142.   (dotimes (row size)% ~! y: a+ J0 C7 P
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))3 F4 f: z7 v( h& F9 |- G

  144. 2 n; r, d# q9 w. X+ i
  145. (defun board-contains-p (y x), N7 T- Q" r' E5 ^- {7 T
  146.   (let ((size (session "size")))
    1 a8 j) Q7 F. j, R$ ^
  147.     (and (<= 1 y) (<= y size)% v0 l) q2 W4 v$ r
  148.          (<= 1 x) (<= x size))))# D, q3 k9 l# C  z6 Z0 z, O

  149. ' n  |1 X$ u% y( V0 D& y# |
  150. (defun board-toggle (y x)
    : G2 b; h# l' F: S3 W) C8 k" C! k
  151.   (when (board-contains-p y x)
      z/ X3 r# h/ T  t( Y, G
  152.     (goto-line (1+ y))$ P2 t& _% C: m) e% ~+ v5 V6 l3 W5 E
  153.     (beginning-of-line), _8 p9 _" b% M
  154.     (forward-char x)
    % W# \- F  I) K2 ?
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))1 f" e. {% j( t$ s  U9 O
  156.                 *wechat-5x5-black-chess*
    1 Q2 I1 v) Q$ P4 ]1 {
  157.               *wechat-5x5-white-chess*))
    5 E' z# @5 D, V
  158.     (delete-char 1)))% {2 Y1 w* K; a! R$ L
  159. ! K/ t) [& {* ]9 o& |; b/ C
  160. (defun board-put (y x)) B# Q7 J+ _: K0 j$ F9 P
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))9 b( d  ?+ m; e
  162.     (board-toggle (+ y (first dir)). ~$ A( q: F3 ~
  163.                   (+ x (second dir)))))3 ~* d$ C5 r1 L0 F
  164. 4 p2 {0 ?. v% g* U2 E1 w$ c
  165. (defun game-over-p ()5 z- L& U+ W8 e8 z
  166.   (beginning-of-buffer)
    # v0 b) q- j4 b8 t6 H
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))6 S. ?. P' |) G- d
  168. 6 X% U& N0 C% o7 g9 L
  169. (defun board-show ()
    6 \2 V4 A$ b% ^( z3 ^( s, ^% v
  170.   (with-board
    . ]+ |* v2 d/ K8 {, Y1 {5 b* ?9 D
  171.    (concat (buffer-string)
    . s9 |5 A5 F% V+ Q* u: t
  172.            (if (game-over-p)
    $ {% J. t7 h7 g' j2 _% q
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    . c+ r3 g" i3 w, m; w
  174.             (format "第%d步" (1+ (session "step")))))))
    5 T! Y: H; s# Y2 Q% |
  175. 5 D$ F0 L: W- d$ e8 u2 P* Z
  176. (defun board-position-parse (cmd)/ @$ n- t8 g4 q) m. g
  177.   (if (= (length cmd) 2)8 g; W: V% L' N* k7 Q6 Y, y3 @
  178.       (list (string-to-int (substring cmd 0 1))
    ! s& e, [# M- i9 y& b0 Z
  179.             (string-to-int (substring cmd 1 2)))
    9 v5 j2 x) G2 a. l; r3 k
  180.     '(0 0)))7 T: o7 t$ |3 P& x. c& W  \$ @

  181. % c) _6 e- \5 C& D3 q" d
  182. ;;; 游戏房间
    ) z% \4 o. U; c8 B0 `
  183. (defun game-room-init (cmd)! c% M6 c3 U0 X) I: f3 C: h
  184.   (let* ((middle (string-to-int cmd))5 R- _/ i* c& s6 r6 u
  185.          (size (1- (* 2 middle))))
    " h* `  U6 s2 q5 c  n0 e* V
  186.     (with-board
      Z8 g- s* ~3 r) V* Z9 O8 D9 I
  187.      (board-init size)( c6 D( Q" x9 A- R
  188.      (board-put middle middle)))
    / P. m: e1 K% b: @. _
  189.   'game-room)
    3 D, j3 R  h; @7 v! Q. M" P4 p

  190. + }" J: m# ^8 N( @7 w. @/ g5 Z6 k4 h7 K
  191. (def-room game-room6 b  e6 V' G, E$ N+ u) y- m9 s
  192.   #'board-show& r# _* f1 C0 S7 c8 q
  193.   (t (lambda (cmd)
    3 a8 V) ~) Q% ?/ J9 @
  194.          (with-board
    + Q* s9 `, @! {/ \7 p
  195.           (if (game-over-p)
    6 H3 U- P' R  s, w+ E4 n, h
  196.               'living-room# s$ v5 ^( Y" q- D) c, `- ]$ Q
  197.             (destructuring-bind (y x) (board-position-parse cmd)& J* S+ A1 p' d) E" x8 ]
  198.               (when (board-contains-p y x)% @( H5 ^0 n+ F# `, U
  199.                 (board-toggle y x)  q' U# p2 M2 [. K0 @
  200.                 (session "step" (1+ (session "step")))). D" P6 {+ L5 Y3 @0 Q0 d3 k1 F1 f
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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