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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
  Q; T) b4 v7 E+ R借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. 9 l; v! E0 l2 j6 j
  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;">;; 定义新的游戏地图
    + S0 r% e2 \+ V8 Q* z! {
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL2 n6 N0 q; y9 K0 u+ Z. i  O1 t
  4.   'tutorial-room-0)                     ; 默认的入口) E$ P  S9 \& G& Q* h7 v

  5. ; K4 H3 {  l3 @. g1 [0 X0 [
  6. ;;; 游戏大厅( e/ ]5 L% e) b' R; A; {' e  k
  7. (def-room living-room$ X8 U' ~( o5 z- P' W6 }; Z
  8.   ;; 进入该房间后的提示语3 Z# B. W  L/ P( T4 R- ^
  9.   "1. 教程
    7 z  n) P- M$ B% _9 ?0 A* r
  10. 2. 入门(3x3)6 H6 ?+ C% Y4 q
  11. 3. 初级(5x5)
    8 y5 K8 \& g7 j' N) D: L) n
  12. 4. 中级(7x7)" t- ?7 D  P7 v. H& g) z
  13. 5. 高级(9x9)( }9 R' N/ H3 M/ R: h
  14. 0. 关于作者5 o% e: P! E; l
  15. 请选择1-5开始新游戏:"
    . T0 R3 p) \1 `
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    , y* c3 e1 x# l
  17.   ("1" tutorial-room-0)3 h: m; |2 \* u, n7 R
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配7 G5 R9 g/ t" [( \( r; Q6 S
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    # G: j! Q9 g! ?+ S: B* e! t
  20.   (t living-room))                      ; 如果条件为t,为永真
    6 E# V0 U  ^3 ^

  21. 7 Y$ ?( c1 [7 H8 I, i# h, ^
  22. ;;; 作者信息
    / l" w8 B8 U# Q
  23. (def-room about-room6 O5 J" Y3 k; B6 _0 R1 O
  24.   "作者:redraiment& u/ o! @& d! H- w: v
  25. 微博:http://weibo.com/redraiment
    1 n* V1 ~. `1 B
  26. 有任何建议,欢迎在微博或微信上联系redraiment。7 l: g4 c  |: Z) v7 Q7 ]
  27. 请输入任意数字返回游戏大厅。"
    " Q- W2 g- S( H- r
  28.   (t living-room))+ e- S2 H; h( E( c+ L6 T% Q* P

  29. : w' N6 W2 q9 M! \$ o1 ?
  30. ;;; 教程
    : y3 `* o6 }. i9 R4 c
  31. (defvar *wechat-5x5-tutorial-rooms* 0" a# _. r$ O2 Z8 Z! d) [: H
  32.   "The number of tutorial rooms")7 r) T7 ~/ P  N0 G5 y* c& }. o' F

  33.   X2 ]3 B& T  n, H
  34. ;;; 简化教程的定义( i% O9 `# k1 y9 |# b- |& [% a
  35. (defun string-last-line (content)3 y3 Y+ x6 @1 W+ x* b
  36.   "多行内容组成的字符串中的最后一行"
    . y+ [' o9 B; e7 |. W: B1 A
  37.   (with-temp-buffer
    7 e+ n3 `$ w8 k" |& g) O
  38.     (insert content)
    4 m. @6 ^6 F. Q7 K
  39.     (buffer-substring (line-beginning-position)
    0 U( {6 K/ b# \: j- S
  40.                       (point-max))))/ o, l  E: t1 |
  41. ! J% n2 D0 B, y$ r0 _
  42. (defun def-tutorial-room (prompt)
    4 r9 J" M! I/ L$ m
  43.   "根据提示语自动生成教程房间。
    : g! g$ b. \- p* g; M* V
  44. 3 u' _+ q  ]: F* ^6 z% `% p
  45. 1. 提取最后一行作为问题;! c6 ~, O5 B5 L
  46. 2. 分析问题,获取期望用户输入的内容;5 m  o$ a! o$ ]4 F4 J/ m
  47. 3. 定义教程房间和重复提问房间。"0 F9 A) ?* e! q9 w6 O
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*))), Y  P0 T  d' {: B& C2 m0 X3 ?, E4 V
  49.          (repeat-room (concat room-name "-repeat"))$ K% ?! F4 D( d
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    - I4 i& f' R- E9 p( ?( C' L
  51.          (question (string-last-line prompt))( l6 O+ p3 t( S7 g) k( j, A
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    - r$ L8 v( c: u$ `' Y( H0 B
  53.                      (match-string 1 question)))
      `6 f7 {+ A  T# s
  54.          (doors (if except# b* n: H9 g) [" t
  55.                     `((,except ,(intern next-room))
    & M7 g+ z6 O# [" H; x
  56.                       ("q" living-room)6 L3 o+ y. [( ^& g9 o6 a  }3 A4 }
  57.                       ("Q" living-room)7 D( D; h7 X2 A1 P( W, [1 ~! J( r
  58.                       (t ,(intern repeat-room)))7 Q) G+ c5 v- _6 t* M
  59.                   '((t living-room)))))
    1 _* d9 J5 \! O
  60.     (def-room-raw (intern room-name) prompt doors)1 r0 x, H$ o& w4 H4 p
  61.     (def-room-raw (intern repeat-room) question doors))); N3 ]3 N( I! [) l7 k# F6 w
  62. $ x1 R# a6 l5 c6 V& J- D- y
  63. (defun def-tutorial (&rest prompts)
    ! g, h$ d% S1 q
  64.   "批量生成教程房间。"; x# c* t; Y5 X9 y# o0 `
  65.   (dolist (prompt prompts)
    8 B/ p" `$ v# T( Y# L$ h+ F) V
  66.     (def-tutorial-room prompt)))
    # k2 U/ ]$ T% k- y% z
  67. 5 A6 k, w; I- x. {8 g1 ^
  68. (def-tutorial! f9 |3 a& T5 s5 z9 ~. T" L2 K9 r
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    ' I3 w1 O+ D' |  y0 t9 l/ e6 i" [
  70. 1. 教程8 Z! j: G, z; {1 J
  71. 2. 入门(3x3)
      n* D. D4 F$ I1 B/ `/ P& W
  72. 3. 初级(5x5)
    4 p! x( ~% j' T6 {' x' {" T
  73. 4. 中级(7x7)- b: U6 `+ [2 N# R9 ?- A
  74. 5. 高级(9x9)! h, i& d/ |8 f
  75. 0. 关于作者
    4 X. |) [* V, O3 ?
  76. 请选择1-5开始新游戏:, w6 f( d5 X7 p( j& k  m
  77. 您现在正在游戏大厅里。/ e/ d3 g* H- r( M
  78. 请输入“2”进入入门级房间": S4 _" @  h* }! k+ n6 T" \1 t
  79.   "  ①②③4 A! z1 |0 ?7 `; W) }
  80. 1 ■ 
    - F" _$ ]9 {, V$ H6 Q7 f% B0 \
  81. 2■■■
    # q5 _. Y0 l% k# r7 d. |/ f
  82. 3 ■ ' ?* l8 n# G" Z- f5 L9 }
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    ) Y! m4 _/ ~( o- D3 u0 T% @
  84. 请输入“22”来关闭第2行第2列的窗户。"
    5 K6 L/ h0 }9 I3 X  O( v
  85.   "  ①②③
    ' n* e' O* G; _& E8 ^: U
  86. 1   
    6 l/ B! Y! ]5 b- g
  87. 2   " I& ]0 c; V# B, Z
  88. 3   / J* S* I- U* o% k: n, C
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。7 h, ]' W; _( ~% i
  90. 请输入“11”,试着开启左上角的窗户。"
    + P2 k' B( x: V  x9 ]0 ^
  91.   "  ①②③# I& D( S5 j1 S' I$ B1 U3 ]  @
  92. 1■■ 
    * k+ o, g* `& n- H1 a& a$ Y
  93. 2■  
    : Y% a, p( g/ j" s  U
  94. 3   7 \; {; ]8 g; r, J) @# M# ?' @
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    - n- t( y# ^7 l7 f* Y
  96. 请输入“13”开启右上角的窗户。"
    9 l9 t4 h) o* ]$ q  j
  97.   "  ①②③
      d* g5 [6 \" q9 k& X9 R
  98. 1■ ■
    , ]$ A0 v: h, R. X; O
  99. 2■ ■
    5 w, T; Y# L. J$ j
  100. 3   0 A' O7 }  g& j( r3 {! _9 I4 V
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。, Q$ ?7 ^. }) ?2 A
  102. 请输入“31”开启左下角的窗户。"
    ' J; s" w9 m4 z0 i9 n
  103.   "  ①②③5 j4 `  o4 C) x
  104. 1■ ■
    % C' s4 E1 u: r- Q+ C" t
  105. 2  ■
    6 o3 \8 |% M: J* u6 Q# |
  106. 3■■ 
    ; ]3 V' Q; b' p: S6 e
  107. 此时,总共有5扇窗户被开启了。) y0 f6 n* T: v% T. o3 E' O% q7 j
  108. 请输入“33”开启右下角的窗户。"
    * `' K, I& D: t
  109.   "  ①②③$ u! x/ t% q7 t8 L
  110. 1■ ■; ?: }, Y  b  m
  111. 2   
    8 q& I2 l( D! W/ Q1 T
  112. 3■ ■
      e; Q; _! M+ b: I- [/ Q- D
  113. 现在,只有四个角落的窗户被打开。( I1 i) q: t2 F8 x
  114. 请输入“22”完成最后一击!"7 [4 R* D5 T# M# t
  115.   "  ①②③
    5 }/ `! A! f% V5 K% v3 {' \1 c& D
  116. 1■■■
    # S8 ^" K. C. l. F. ]" ]3 i* ]) U
  117. 2■■■) O0 R8 B6 P7 {; u/ M- `
  118. 3■■■# X" d; G! e6 ~) H; v
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
      s% w1 K1 M7 Z  E9 h( ^) [

  120. ! E! O/ m; p; M. h& f/ t8 n
  121. ;;; 棋盘4 |6 }7 B% L' W
  122. (defconst *wechat-5x5-white-chess* 122883 X6 C# T3 ?) ?. w9 ^
  123.   " ")4 L+ ]( R4 m1 O! P' J2 B
  124. 9 \) f! M; {: K6 ^" q
  125. (defconst *wechat-5x5-black-chess* 9632/ z: \+ ]7 ~) W, M* f, I$ U
  126.   "■")
    : f0 u3 P' b% |' {, T# N  ?6 o
  127. 8 p% x1 b9 b8 W2 d0 G: |
  128. (defmacro with-board (&rest body)8 d6 ]9 W  F. t* Z; Z
  129.   `(with-temp-buffer. ]. t; E3 B6 W& q
  130.      (unwind-protect
    3 x2 ?7 k8 L' V. i# `
  131.          (progn/ Z  n& n5 S3 Y8 I: |3 x8 m) }/ y
  132.            (if (session "board")
    8 Q% N) l( U3 ?% k
  133.                (insert (session "board")))
    * J+ g* e( ^4 `' U* s) f
  134.            ,@body)+ G* o1 ~1 V9 b% W
  135.        (session "board" (buffer-string)))))
    / C( K) t' q0 Z9 ]. m9 @

  136. # x0 a2 e4 j! o3 Z' y# F
  137. (defun board-init (size)  S! r2 q8 q8 N" _' p
  138.   (session "size" size)
    1 K' {4 j6 v' T7 h6 E( s
  139.   (session "step" 0)
    7 v# _" u  e. M/ t& O
  140.   (erase-buffer): H3 ]9 t8 s, r7 w& c/ i
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    # a, ?4 Q' ]9 X4 v" E( T, F
  142.   (dotimes (row size)
    ( H0 Z3 [8 \) R
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    5 M& k  z$ T6 g, w! F$ v! X! ]1 l
  144. 8 a- v  i& t7 t& k
  145. (defun board-contains-p (y x)  \- ?/ }+ P/ I. U) E! j! `2 ?. J
  146.   (let ((size (session "size")))$ R5 Z! ]  P/ L- u
  147.     (and (<= 1 y) (<= y size)1 }5 e4 b' l% G) c: b; D
  148.          (<= 1 x) (<= x size))))
    ! f, v- G& U# T  v- s
  149. 1 D! n) |" Z8 }) [$ U) |! I6 M; f; i
  150. (defun board-toggle (y x)
    2 [* l7 \2 h  `) w
  151.   (when (board-contains-p y x)
      Q, ?- j% S$ T. F$ |
  152.     (goto-line (1+ y))
    ) i1 w3 y- z& I+ Y) e' s) s& ~& v
  153.     (beginning-of-line)
    . z! [. s) s$ k& o; D& a) U' o
  154.     (forward-char x)
    ' }& K8 V$ c+ d& v5 r& J
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))- K; x# b/ j" v9 d5 T$ i: z
  156.                 *wechat-5x5-black-chess*, y2 l( V& w1 h" F: W) {
  157.               *wechat-5x5-white-chess*))& P( T, o& W6 ]* u+ {8 i) }
  158.     (delete-char 1)))0 S4 \1 W" ?- [6 V; I, Y

  159. & X9 D' [9 r1 z% p+ k
  160. (defun board-put (y x)+ F, T3 T1 R$ f: F
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    ) |$ z0 p3 F# c# ?8 N- k  f' Y
  162.     (board-toggle (+ y (first dir))* t" Y3 ^6 [, _. Y# h9 S
  163.                   (+ x (second dir)))))
    9 i9 y# W. s0 r) `" |: D  ^

  164. " k+ k0 h7 u/ {0 a* M; k; o3 w
  165. (defun game-over-p ()
    9 m2 r9 n4 `0 u% i% I7 u, V* W
  166.   (beginning-of-buffer)
    $ V8 M% \+ q( ]6 I; R
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    * y/ k! t+ F& ^! Y# l/ J+ b9 s

  168. . B  v  J: R8 |! q4 U9 ?# P1 P
  169. (defun board-show ()
    / X0 x  v. Y/ F
  170.   (with-board, J6 A5 ]5 S6 r4 H6 Z5 j+ q
  171.    (concat (buffer-string). H! t8 g% J' U6 u
  172.            (if (game-over-p)9 h) N. ~* `. a% L2 a* V
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    6 y5 ]- f+ Y  o+ j8 I$ r: }/ v8 s/ C9 W
  174.             (format "第%d步" (1+ (session "step")))))))4 G6 K' b1 A" F, t3 i# ?' m9 v# U- Z
  175. 5 x( i+ o/ w- T1 M& X- D
  176. (defun board-position-parse (cmd)
    8 B8 r4 @$ U  i
  177.   (if (= (length cmd) 2)4 |) F# a2 ~1 w! x' {
  178.       (list (string-to-int (substring cmd 0 1))
    ; K, M0 \2 W# H; W' I! w, L
  179.             (string-to-int (substring cmd 1 2)))) m) e( ^' Y2 t/ D
  180.     '(0 0)))7 |! t4 c% K( R3 V, F- u

  181. 5 X0 R. K; `" {; b' P. W1 f% c, [
  182. ;;; 游戏房间
    2 l; o8 V  H9 f( l1 Z# e
  183. (defun game-room-init (cmd)' I$ R) e1 Y0 ]4 d$ K
  184.   (let* ((middle (string-to-int cmd))# `9 @# t7 e" Q, g/ g
  185.          (size (1- (* 2 middle))))
    5 i1 \( ?3 o" l% ~' Z9 n
  186.     (with-board
    , i. \+ K: K3 @5 O  L2 S
  187.      (board-init size)
    4 R. C, j. r7 U# ]3 c' o% _, O) s
  188.      (board-put middle middle)))' H& Z1 C- f& Y* [5 b0 N& B
  189.   'game-room)! r: c8 Z, I. @) l( [0 p6 Q* L

  190. 6 O* K/ m5 ^9 @) J; I
  191. (def-room game-room
    % |" v. w8 Z: I$ R/ ~# s5 G
  192.   #'board-show
    9 ]  O/ p  {8 K6 E- {# w9 @
  193.   (t (lambda (cmd)
    2 W2 \" J9 B/ d% V) S5 [
  194.          (with-board
    $ T& u( R  E* O1 f/ }
  195.           (if (game-over-p)5 r+ \3 D* j6 r5 m% B8 G
  196.               'living-room" X' ^' c( v# M' U# A
  197.             (destructuring-bind (y x) (board-position-parse cmd)( K2 E! U, k' n  p8 N
  198.               (when (board-contains-p y x)
    ) [3 `' }: c, X
  199.                 (board-toggle y x)
    * _7 s- ]) B2 F
  200.                 (session "step" (1+ (session "step"))))
    1 U/ h/ I" a% W7 i2 m
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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