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

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

  1. 0 j. m. O( s! @4 w% n: {
  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;">;; 定义新的游戏地图
    % ^' O. `, b4 _5 H* `8 `& J, l
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    5 U9 F7 t6 m* ~9 e' x1 s
  4.   'tutorial-room-0)                     ; 默认的入口  E* J+ U7 r( K1 R5 e

  5. $ _3 G" o& ?8 q: X7 A1 D
  6. ;;; 游戏大厅2 `3 U4 }& b6 ^+ H( r/ ~
  7. (def-room living-room7 L, h. x  i0 N3 q
  8.   ;; 进入该房间后的提示语- F$ X- U( }' R( l+ |9 v* Q
  9.   "1. 教程* ~5 {, \7 B% |) a+ }
  10. 2. 入门(3x3)
    : g) {1 y/ a* T, @2 g6 v  k
  11. 3. 初级(5x5)* b2 o& C# x5 T3 p6 I* m6 C
  12. 4. 中级(7x7)
    , w9 i2 w% q# `
  13. 5. 高级(9x9)
    9 T+ b; @6 K' O" K
  14. 0. 关于作者9 _/ B( l! x( _9 }4 B; d/ M' U
  15. 请选择1-5开始新游戏:": m0 \5 z+ h+ `
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名6 w6 R: c1 s! W8 e# w8 [
  17.   ("1" tutorial-room-0)
    : |, F1 o* T7 q7 R
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    . K9 o* Z  B9 \; u
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    8 ^3 q. R  }* J8 u
  20.   (t living-room))                      ; 如果条件为t,为永真8 K* W- a. f0 \/ l' h& C1 I
  21. 0 b; h8 A/ M: V* L7 q: \
  22. ;;; 作者信息
    8 j* t+ X: Y5 W) |
  23. (def-room about-room
      l3 y: b. |6 T9 A6 L; X
  24.   "作者:redraiment
    + g; L1 _  Q3 N6 k
  25. 微博:http://weibo.com/redraiment+ ]3 c7 c4 P" M  O# X
  26. 有任何建议,欢迎在微博或微信上联系redraiment。" a$ V" L/ S" y" l, G
  27. 请输入任意数字返回游戏大厅。"
    : ^$ w1 s$ G8 |( O1 F7 d
  28.   (t living-room))
    & p7 `1 ~* L9 Z" d4 }2 b8 P+ u
  29. 8 m( C6 s- ~( `2 G# H
  30. ;;; 教程
    # I  H2 _" K, c8 a* \: Q! s2 U
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    2 s$ U# x( m$ x( ~% {
  32.   "The number of tutorial rooms")
      ^' i$ s7 _' g7 }6 i# p
  33. & `. {# G0 A$ I9 i. W9 ]* R8 m- o
  34. ;;; 简化教程的定义; q! d9 H# X2 w$ d5 V. K+ h
  35. (defun string-last-line (content)
    + T% X9 Z' B9 A% O: m0 F* \, `: [
  36.   "多行内容组成的字符串中的最后一行". c, Z/ J* x  R
  37.   (with-temp-buffer
    3 [5 i, q4 G+ z$ B/ X; v6 l
  38.     (insert content)2 _/ ~: J2 n0 |, d$ @: _
  39.     (buffer-substring (line-beginning-position)
    4 h* ?( M6 z9 _& m; ]9 q
  40.                       (point-max))))
    ( ?. S# I+ i5 }5 y
  41. ; ]; T" Z' r- W
  42. (defun def-tutorial-room (prompt), l& T& i4 @1 O! K* M4 O% I/ |
  43.   "根据提示语自动生成教程房间。) o" {5 U# h1 j: u& g
  44. 9 K7 M! b* ]5 m- Q# P
  45. 1. 提取最后一行作为问题;0 \1 _3 ^. Z8 _4 B$ p3 s! z
  46. 2. 分析问题,获取期望用户输入的内容;
    8 @* E0 L( Y' `# l
  47. 3. 定义教程房间和重复提问房间。"# x/ J* q! L3 h* H6 e9 T- N3 d( l
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*))); D, w* k+ _4 S2 Q
  49.          (repeat-room (concat room-name "-repeat"))
    9 a8 q, G8 d3 _& w/ H4 n0 U
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))4 x. k# F* @8 X$ h% g
  51.          (question (string-last-line prompt))
    $ C. c, M6 h$ u2 T' S+ \. |
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    - f/ K: ^! p8 T7 m
  53.                      (match-string 1 question)))
    ( l9 `% \. L  O6 ~, y
  54.          (doors (if except
    0 L# b9 ~: t+ s8 J  p
  55.                     `((,except ,(intern next-room))4 A3 N7 T4 I2 D5 s  r: C. l1 s" i
  56.                       ("q" living-room)9 ?7 y+ [7 {5 z% W
  57.                       ("Q" living-room)5 ~" q# c. Y! [. i0 r
  58.                       (t ,(intern repeat-room)))/ _9 n- A. Y. h- f/ k) z
  59.                   '((t living-room)))))
    ! g/ A8 `4 F$ I$ [5 m, R3 E: E
  60.     (def-room-raw (intern room-name) prompt doors)
    / f- b: l' W# t9 [! I
  61.     (def-room-raw (intern repeat-room) question doors)))7 y5 n* f: H* l5 I; |' h& ]& N# e- @

  62. ' L( }' f9 r! Y; \1 E0 _
  63. (defun def-tutorial (&rest prompts)% L% g3 n! j/ o$ [/ [) t9 I4 Z
  64.   "批量生成教程房间。"
    0 D. X/ `; c' K
  65.   (dolist (prompt prompts)5 c' T7 c  A, k6 c. y  }% Z' L
  66.     (def-tutorial-room prompt)))! k8 g. H, L5 P5 e. F/ |+ ~7 t% u

  67.   y7 e4 u. [. o. A4 A9 E2 y, `
  68. (def-tutorial6 F# |' |( Y, M( C* h7 P" D
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。$ {4 _7 P4 ?3 d( R' u
  70. 1. 教程
    % R, A7 D! t  w, v- [1 W
  71. 2. 入门(3x3)/ G* e# E8 I4 N8 ]9 U! N  q; j
  72. 3. 初级(5x5)
    0 b1 k& c4 z5 r. R+ T2 o
  73. 4. 中级(7x7)6 V* r1 H( e" H9 N
  74. 5. 高级(9x9)4 Q$ E& W$ K: r( z9 R/ w; ?
  75. 0. 关于作者/ z: M. M5 X/ {% S+ U- s
  76. 请选择1-5开始新游戏:' o: G; _( V8 v8 @
  77. 您现在正在游戏大厅里。
    # ~- k2 f$ q' R, J9 E( \
  78. 请输入“2”进入入门级房间") T* W  H- R2 i
  79.   "  ①②③3 e) A1 L2 s' P, ~- ?  `0 ?1 I
  80. 1 ■ 
    3 B8 ~9 ^4 U7 C& c; _5 }
  81. 2■■■  M2 y7 f2 J" M  b+ z
  82. 3 ■ : X4 P6 C/ I0 `
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    " A: K$ A$ Z& i9 `
  84. 请输入“22”来关闭第2行第2列的窗户。") a9 b% @  p. r# ^# V
  85.   "  ①②③. P; g, P6 m# F7 h  g. m/ V
  86. 1   
    & {' Y0 y. ?+ ^/ q$ e
  87. 2   3 U% r. M! `4 k% W" {: y( H
  88. 3   - X( [4 {  d* o4 D9 B3 W. K7 }2 Z
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。  z  W$ x! k8 X) A6 l
  90. 请输入“11”,试着开启左上角的窗户。"( b  d! ]: N, B9 |( [3 q
  91.   "  ①②③) `: @. ~% z5 c& r* Q
  92. 1■■ 
    ! C% @$ k. \, G" K
  93. 2■  - [7 b* j5 H# g/ e
  94. 3   
    8 k- @/ w% K, [9 |3 z
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。4 V$ z4 g  O% d5 z+ D
  96. 请输入“13”开启右上角的窗户。"
    ; X5 L; \, L6 G% a9 l" w' d* C
  97.   "  ①②③( a8 H/ j$ l0 k0 K3 i
  98. 1■ ■5 c4 \' g- |: `$ f
  99. 2■ ■
    / g% _1 {* a  B+ ]: c" M/ e
  100. 3   * G) d/ \" m7 a
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    7 Q; E& D: G' u+ ?9 I/ a5 f8 x, G
  102. 请输入“31”开启左下角的窗户。"
    ) p: p% W7 F5 d5 Z
  103.   "  ①②③1 k, J4 R- R3 w$ N& ?4 w
  104. 1■ ■
    ; T. i0 g9 u8 k6 S& m, X
  105. 2  ■
    ! S) @" E" t" q  U8 k7 j
  106. 3■■ 
    % E1 D6 U0 |* {8 |8 h$ z  b* U
  107. 此时,总共有5扇窗户被开启了。: |" r$ X5 n! e( ]
  108. 请输入“33”开启右下角的窗户。"
    ) F' O0 |2 {# c. P
  109.   "  ①②③
    ' P1 H; x9 N; X0 D5 |7 }
  110. 1■ ■3 Q( p+ V5 m) y
  111. 2   ' Y4 M: w: [. g/ @
  112. 3■ ■
    , r. a3 c9 R7 A* D6 {0 l
  113. 现在,只有四个角落的窗户被打开。' g5 _. j; `( G5 ]  }+ P8 Q, }
  114. 请输入“22”完成最后一击!"
    4 T# [4 n/ m9 w0 z4 r
  115.   "  ①②③
    / U: i4 j( O* ^6 b5 g, A
  116. 1■■■2 N4 v- X& q9 g: l
  117. 2■■■
    ( e+ d; f0 I( \0 ^) Z" ~7 ~
  118. 3■■■
    ( m# f: \% V* y5 r+ s  J
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    + W/ ^5 B9 Z" c( K8 S' K$ R3 Q

  120. , W( I- `* z. W7 g5 S# L
  121. ;;; 棋盘
    ( l# W/ S* B6 y: ]; M- c/ b
  122. (defconst *wechat-5x5-white-chess* 122884 g) W7 q4 a5 }. P+ G
  123.   " ")
    - V0 C. O, x! F9 D% @' @+ j
  124. 5 N2 ^" [) g0 H1 i3 z
  125. (defconst *wechat-5x5-black-chess* 9632& B. p7 v+ l3 x- ^* U4 q/ G
  126.   "■")8 P3 d  C3 j% _- B* R+ n' G. e8 {: D
  127. ( C" S& ~: X3 P% w
  128. (defmacro with-board (&rest body)( @: u0 C0 |4 U9 [! `' C. Y
  129.   `(with-temp-buffer
    5 C( D' U" H" @" B3 M% n
  130.      (unwind-protect
    ' {2 b2 D7 K/ U! C+ ]
  131.          (progn$ F- M# B6 l2 [
  132.            (if (session "board")4 z- O7 E/ N' T+ ]6 a) I8 F$ S
  133.                (insert (session "board")))( S: Q3 [0 \9 U
  134.            ,@body)' W$ ?& o5 Q2 K5 }( q
  135.        (session "board" (buffer-string)))))* z. O# T7 G& _5 s3 [
  136. 9 Q& Q  V- G8 L" G) t% ~: c; e
  137. (defun board-init (size)7 Y9 z7 L' H! t  D0 B+ s+ q
  138.   (session "size" size)
    % W, G% Z! ^& D! m* N' a
  139.   (session "step" 0)( t- C# L0 @3 `
  140.   (erase-buffer)
    / _4 I# G8 l  g/ A3 Y
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))  W5 b) e4 p/ o) g; [; t
  142.   (dotimes (row size)
    / f$ u1 _) ]1 o$ q" o3 k5 z* y
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    ( m0 J' e, d( u! x

  144. / h4 M7 N1 ]+ _3 {* O  r" l
  145. (defun board-contains-p (y x)) G: l& \8 x/ X, v7 Y# d
  146.   (let ((size (session "size"))), j, D. c5 n' @9 Y7 l" G; w
  147.     (and (<= 1 y) (<= y size)
      K* V5 \8 }: P5 }
  148.          (<= 1 x) (<= x size))))
    % c5 k* J" g0 V" d0 ]% `4 a; z: Y
  149. / B) A8 b( ?# l3 z/ F
  150. (defun board-toggle (y x)
    ; ~- _, E' C: E
  151.   (when (board-contains-p y x). }; R% e, u! o, `! m% M; @. g
  152.     (goto-line (1+ y))% \, Y! u. J* L5 \0 ^
  153.     (beginning-of-line)0 @" ~* @6 ?; {4 o9 j
  154.     (forward-char x)2 x, x! ~0 Z6 z" d
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    ! |, G& Q1 ^( z4 B) j* N
  156.                 *wechat-5x5-black-chess*4 ?8 K9 j9 J& R$ `! q+ j# f
  157.               *wechat-5x5-white-chess*))
    # f3 l" N% H6 q: @( F
  158.     (delete-char 1)))
    " Q, b6 m9 Q$ w; b1 A# s' v; a+ {
  159. 8 ^4 h: q* H- \7 u6 f
  160. (defun board-put (y x)7 X+ E+ m& X2 l! E
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))* m8 S! ^* W; Z4 x
  162.     (board-toggle (+ y (first dir))# ^. {% p# {. N5 T: o9 _
  163.                   (+ x (second dir)))))
    ! [' o# j5 J, W" E  W
  164. $ J  a" t/ S3 ?) F/ i
  165. (defun game-over-p ()  d7 F, t& I$ b- ]; `& x* b
  166.   (beginning-of-buffer)
    * V) ]0 Y; w" d# p  j7 d) R# `, ^' u
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))$ I9 l/ V# j/ L+ Y# g, h% x

  168.   i; }, y+ D0 Q, Z: M
  169. (defun board-show (); E% d1 {* y0 h* u
  170.   (with-board- N4 G3 X! Q1 v1 K1 e5 r, Y, V- R8 J
  171.    (concat (buffer-string)
    7 l% ~1 h: m, t  h9 h  q
  172.            (if (game-over-p)
    2 {% |+ h" }  `+ v- ~
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))9 G% e% D; K/ c+ k; g8 s9 R- R
  174.             (format "第%d步" (1+ (session "step")))))))
    * J" \* n3 o) K9 C4 X; |; l

  175. 6 P; N1 w( {6 {5 n) n
  176. (defun board-position-parse (cmd)5 N- i7 |: w# k1 @0 t( C4 Y
  177.   (if (= (length cmd) 2)$ [9 h* H3 c) V% F9 V
  178.       (list (string-to-int (substring cmd 0 1))
    + f( B+ B/ W, D
  179.             (string-to-int (substring cmd 1 2)))
    % V. T. L8 k1 v! T6 N' ?
  180.     '(0 0)))7 I0 ~4 K  @# a$ J/ ^

  181. % A* k0 R  w+ f! ?: _
  182. ;;; 游戏房间$ n1 @0 t) S- w0 M7 Y
  183. (defun game-room-init (cmd)
    $ |9 ]) S/ s8 n
  184.   (let* ((middle (string-to-int cmd))- i/ m6 x  {: K, F8 ~
  185.          (size (1- (* 2 middle))))& o- B" ?! d6 q* R
  186.     (with-board
    " |& u# _8 ^, M. @+ |; a$ P
  187.      (board-init size)
    / g9 V% m! c1 o+ ^& |* J
  188.      (board-put middle middle)))5 E$ G; v3 V8 M) w# v
  189.   'game-room)
    $ \" d) S5 {* Q
  190. 5 c1 H0 x4 p: o9 w; y3 u$ A( z
  191. (def-room game-room9 m) T; e$ Y* U: Q1 N
  192.   #'board-show
    : K0 r1 l: _8 |# q3 z# _
  193.   (t (lambda (cmd)
    3 R+ ^$ \- L* @( C
  194.          (with-board
    ; V, f2 e# V9 j
  195.           (if (game-over-p)9 R$ x% |( E* k4 m6 S& l# q
  196.               'living-room- u8 ~  @" S$ j8 A
  197.             (destructuring-bind (y x) (board-position-parse cmd)# T- B9 o4 g1 E8 d
  198.               (when (board-contains-p y x)
    * G) _! \: m* _6 G: H' ~
  199.                 (board-toggle y x)
    4 m9 Y+ q/ s: i9 S
  200.                 (session "step" (1+ (session "step"))))0 H0 g4 A* |3 D
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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