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

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

  1. 6 U5 U# F5 Y3 i, 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;">;; 定义新的游戏地图2 K  T6 g6 X. G" T- r6 K
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL9 U' t: z+ a# ?. x9 V9 V9 D
  4.   'tutorial-room-0)                     ; 默认的入口2 k' Q6 l) M6 A) X: e

  5. 7 v. A) B0 D8 a, t
  6. ;;; 游戏大厅0 r# B* `+ n8 S$ a& T0 q: [$ O
  7. (def-room living-room( C- C% R; ~9 x3 C& d& \7 o7 A( y
  8.   ;; 进入该房间后的提示语
    - y/ ]" O6 O. U3 M1 O( H
  9.   "1. 教程% @) L8 I, P6 p6 a- H9 M$ o  A& B- d$ i6 u
  10. 2. 入门(3x3)+ J2 U; ]$ Y+ c: U/ a4 g
  11. 3. 初级(5x5)
    $ ?# @# |* {: \$ a% }# l
  12. 4. 中级(7x7)
    - z# D) Y" L* Q% a4 n& \7 I# C3 X
  13. 5. 高级(9x9)
    , ~1 N/ s: @+ q+ v: \  {* r
  14. 0. 关于作者" T( V' D0 r$ q0 y, r$ r
  15. 请选择1-5开始新游戏:", P8 g& \/ q2 E( ^) J8 @
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名; v. F  [0 J& M7 n6 q
  17.   ("1" tutorial-room-0)4 b' f5 n6 z. b( @, y  W8 u. q
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    : d8 R8 E& a3 @
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    ( N1 c! c7 c; l( g+ v4 ?' t% o
  20.   (t living-room))                      ; 如果条件为t,为永真* ^$ R8 w9 z1 e7 I4 ?8 l

  21. * L# J8 v) [" ~. H
  22. ;;; 作者信息
    2 D9 |- D6 T6 v/ _
  23. (def-room about-room
    ) G; W% ~; F* J9 n8 \8 E6 X" T
  24.   "作者:redraiment
    ) b* V! h- _. y$ k4 S
  25. 微博:http://weibo.com/redraiment$ T4 U5 W: r: x" c
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    # a1 O1 r0 Y, u% N- L' \
  27. 请输入任意数字返回游戏大厅。") w: U4 K4 A9 b0 d- j
  28.   (t living-room))
    ! S1 h1 p" e+ H- N$ S! n  H' X
  29. " W2 H' G7 B4 {- L
  30. ;;; 教程
    7 V8 r, K" ?' {
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    5 w. B! q4 a/ K) J7 ~8 Z
  32.   "The number of tutorial rooms")
    1 [, T5 H" V  _5 u8 x& n! `$ a; M
  33. 9 {0 U9 `- |  L- S3 ?1 Y2 v  k- v
  34. ;;; 简化教程的定义
    * X5 r' z% X* I. N
  35. (defun string-last-line (content)
    . j! H' l$ `/ {& x
  36.   "多行内容组成的字符串中的最后一行"
      f0 u5 n: P6 ^
  37.   (with-temp-buffer: l+ D% f: ?* ]
  38.     (insert content)' x0 @; J  ?: o$ f+ t7 u7 j
  39.     (buffer-substring (line-beginning-position)
    $ ^* Y3 K4 H) S. o, g) e
  40.                       (point-max))))
    5 E* a- M& n+ p2 t: H( V1 e$ p
  41. 2 l' ]. ~" p- {( ?
  42. (defun def-tutorial-room (prompt)( A8 m/ p1 a+ D: X- K
  43.   "根据提示语自动生成教程房间。
    % p' T9 T) q2 d/ [
  44. 9 m0 G" I# s2 o& t( P, n* x+ ^/ ?
  45. 1. 提取最后一行作为问题;
    - P: d% P& Z% \% d4 {2 P
  46. 2. 分析问题,获取期望用户输入的内容;5 b  B, t/ Y2 k# B% ^
  47. 3. 定义教程房间和重复提问房间。"+ a  L& {; U' @4 r+ ~7 Y' f
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))4 h7 j; }* D7 i$ z
  49.          (repeat-room (concat room-name "-repeat"))
    ) @0 o( y+ |. E! }* F3 V9 H
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))9 H" L% L8 w9 j$ [0 A0 ~3 `- i& h" c
  51.          (question (string-last-line prompt))
    3 Q2 |8 T; Y( ^5 A8 t
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)  P. P+ p' F2 M8 Q1 F; ]
  53.                      (match-string 1 question)))8 R+ Y: N) j$ A5 b
  54.          (doors (if except
    ! L4 X9 a: {+ K1 L# h1 X
  55.                     `((,except ,(intern next-room))/ M9 ^1 I- A% p; t3 {
  56.                       ("q" living-room)
    % B1 W9 }) f. y! c& i% {% ]& V2 p
  57.                       ("Q" living-room)! n" G6 }7 J# `! r: ^7 [
  58.                       (t ,(intern repeat-room)))3 h. o" h# u. s! C" ^( s: r# _
  59.                   '((t living-room)))))
    " G9 e' Z8 w& q$ r( S0 J+ c
  60.     (def-room-raw (intern room-name) prompt doors)
    5 O+ m2 U8 V0 V* _6 {2 `2 P$ y$ I
  61.     (def-room-raw (intern repeat-room) question doors)))
    / m) B  V1 J' \% Z. K
  62. 7 {5 B) Y6 I$ W  g! o  N
  63. (defun def-tutorial (&rest prompts)
    ! `; Z1 ~8 v# ~4 E/ I
  64.   "批量生成教程房间。"! K  f+ E! y$ h) J) _( L& n
  65.   (dolist (prompt prompts)
    1 u) E$ D$ [, U- j6 @
  66.     (def-tutorial-room prompt)))
    8 D( W# H9 A* |7 v" k  D

  67. 4 M1 t" u7 s: N+ x, ?1 m9 r/ p
  68. (def-tutorial
    # Q' V# R& O2 X; `
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。6 z8 v  D9 P9 ^: l6 \* r
  70. 1. 教程) x& k* G' t! M8 J* e1 e3 i
  71. 2. 入门(3x3)4 s# j  M! A; r  v  ^9 H- {3 \
  72. 3. 初级(5x5)
    , Z! X0 Z) W+ f& B$ @+ q* `) `
  73. 4. 中级(7x7)
    " h+ _+ r) N! w. I6 L  n# Z. E$ p
  74. 5. 高级(9x9)- T4 O4 _2 A, I2 d* a
  75. 0. 关于作者& N+ k  K. q' {+ s3 S
  76. 请选择1-5开始新游戏:
    3 y3 m( D8 }& C: v2 ~- D) t
  77. 您现在正在游戏大厅里。" v7 r' ?- i& R) T" n! }
  78. 请输入“2”进入入门级房间"2 B% K5 d3 |0 g& B
  79.   "  ①②③
    ' g  ?0 r0 {/ D  d
  80. 1 ■ 
    : O. o+ }3 ]& {% i) {) f
  81. 2■■■
    0 c' q: u1 I. ?# v
  82. 3 ■ 
    & i6 w0 Q! T- s: J5 q- ?
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!, K$ e9 |5 Z+ A- V0 r# q+ v
  84. 请输入“22”来关闭第2行第2列的窗户。"0 a1 v) I) g; w+ G  W0 F# f4 U$ F" p
  85.   "  ①②③0 b2 p+ N9 M# o: k& D9 j
  86. 1   
    2 v) B- w! G) Z
  87. 2   7 _+ ?6 K8 ?  n
  88. 3   ' u8 R0 j7 h1 t2 T7 S' {/ {6 m
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。. i$ `$ h8 R- e" v8 K! U* G5 l
  90. 请输入“11”,试着开启左上角的窗户。"
    * S: w% ]. F4 n; T) ?4 F( L
  91.   "  ①②③
    ; y  R# M$ {& w5 \. a3 N  y
  92. 1■■ ' T; J# @  v- M) B0 j0 e" l
  93. 2■  
    7 ?. Z5 Y* X, x2 G/ o' ~
  94. 3   * }% }  O* S5 w1 v7 Q* s' N2 y
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    + F' J5 _9 n& B# u3 M4 e* K
  96. 请输入“13”开启右上角的窗户。") N% r+ q- V- H+ j* F0 D8 }% T
  97.   "  ①②③2 z+ S* C8 j: O' }( ~2 f. g6 g! h
  98. 1■ ■
    ( x) x  r( [) w! E8 X! k" u
  99. 2■ ■2 t5 e8 M1 Q5 w& Z* H8 o
  100. 3   
    ) O2 V: w3 S+ P& U4 c$ L9 s! Q4 R5 |
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。. |( S& v7 d: L. k3 h: H/ N) l0 _
  102. 请输入“31”开启左下角的窗户。"
    / b. |# o$ w! r# U+ S
  103.   "  ①②③5 F) k9 `) {: r" |
  104. 1■ ■
    9 `7 G% Y" j  p0 s5 N
  105. 2  ■
    $ T* m! ]+ ]( O& W- Y  j
  106. 3■■ + U8 o% a- ^: j% c! F
  107. 此时,总共有5扇窗户被开启了。
    6 z% q3 ]' o7 o. g7 c
  108. 请输入“33”开启右下角的窗户。"! k' j, ~5 g9 p' R( \
  109.   "  ①②③6 C. ^( e: \2 q5 G( Q; ?! H1 a" X$ C
  110. 1■ ■* A8 {+ e% f3 D: w/ v* B' P
  111. 2   + A; c  m+ j. F+ E6 D
  112. 3■ ■
    2 V6 T1 T" `3 [# ~% A# |9 a
  113. 现在,只有四个角落的窗户被打开。1 N! ^% o( i& X- _
  114. 请输入“22”完成最后一击!"+ \, f/ S$ T6 B' n
  115.   "  ①②③
    # z# n. o2 a' o$ R
  116. 1■■■3 ?# H# V# B) u% F# w' ?! ~6 e# Z
  117. 2■■■
    ; @1 p( a- e$ R' W4 h0 }
  118. 3■■■
    3 W# T9 j; U" x( ?9 p+ x: H" O5 y
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    % O' M3 \# g5 g. [, i
  120. 6 c) T8 P, ?; g5 U7 l( @
  121. ;;; 棋盘
    5 Q" t) d4 m! g7 o  g) E
  122. (defconst *wechat-5x5-white-chess* 12288# G: g# t. w' E
  123.   " ")
    % j! ]5 N4 n/ {7 K: }5 ]
  124. ) i8 p/ C- r/ J
  125. (defconst *wechat-5x5-black-chess* 96329 A6 I, v8 q7 u  l( L4 o
  126.   "■")& n* {+ J* A, u7 _; b  I1 x

  127. ) Z' R) k2 a' s* Q8 @
  128. (defmacro with-board (&rest body)6 _' |: x1 t. Y$ S2 G" ?! c+ O
  129.   `(with-temp-buffer
    / V  M( V5 A6 }7 O
  130.      (unwind-protect; D* n+ x  }  F. m' V% L2 B
  131.          (progn
    & ]3 W9 C' ]8 p& _5 [3 r
  132.            (if (session "board")
    ! z$ k+ Y$ n7 M$ d# `  _' w/ a
  133.                (insert (session "board")))
    9 y; G9 {! \! M0 u5 _' |3 q
  134.            ,@body)) G  ~; ^$ @8 d2 ~: O( P
  135.        (session "board" (buffer-string)))))
    9 k4 J0 I2 y" _  J+ k$ l) [: u

  136. / ~9 R9 J( |9 x8 s4 ~
  137. (defun board-init (size)+ r& p3 \8 {/ G- w# b0 [' n
  138.   (session "size" size). o$ A- B4 a1 t
  139.   (session "step" 0)' c+ t/ x! o9 b% k, W- v# N7 C
  140.   (erase-buffer)' r& `3 p. K/ D, @% R+ {
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    # f1 F& g# |8 y
  142.   (dotimes (row size)
    1 B$ ?' G; B7 V. }
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    4 e4 H6 E- H# C0 x2 E
  144. 8 L, I- q9 V# t' o
  145. (defun board-contains-p (y x)4 @- G$ j! }$ I; n0 N3 {& o5 D7 ^7 ]
  146.   (let ((size (session "size")))
    ( o9 e8 U' d8 |4 x. u: v
  147.     (and (<= 1 y) (<= y size)5 f4 ~8 ^4 V: x6 F( V1 }1 C3 Y4 z
  148.          (<= 1 x) (<= x size))))
    & R9 S- D% }6 H

  149. # X/ P& w' J% Z% G/ V& z3 j! [' W' o
  150. (defun board-toggle (y x)
    9 e6 D, P; b" O0 t) [
  151.   (when (board-contains-p y x)& K  h1 g3 s' s9 F2 }  p4 u! X
  152.     (goto-line (1+ y))' o/ t% l; R$ i( \4 G9 y& C2 R
  153.     (beginning-of-line)4 |/ o0 e) `% O% l
  154.     (forward-char x)
    6 k1 o; J6 M4 z, `" E9 w9 G
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    # E3 n- ?: D" p4 @) N# k6 _" O1 t' V
  156.                 *wechat-5x5-black-chess*
    " f. s* ?3 P" ]! F0 m2 _, i
  157.               *wechat-5x5-white-chess*))
    . ]0 r* ]- f8 ~( s+ B  T
  158.     (delete-char 1)))
    ; x+ x: C7 n/ P% X1 e4 X

  159. 6 T% x0 ~: l' R! x  P/ @
  160. (defun board-put (y x)
    ( w! l3 a- |  |$ C1 H5 A2 d) i$ K
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))( d# T# V' c. ?& T
  162.     (board-toggle (+ y (first dir)); H4 ]3 q' B. X% j% Q- Q
  163.                   (+ x (second dir)))))
    ' y: w% c( ]9 t- c1 Q, o  _
  164. # K% R/ l$ l! p6 L0 Q# O6 h
  165. (defun game-over-p ()
    / s, o3 n  B& b+ @6 ~# t6 W
  166.   (beginning-of-buffer)
      c  A, Q+ H0 G  }+ D' h
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t))); E5 _& v" y, _1 H2 E" g

  168. . ^) z; L2 |. `" g0 R
  169. (defun board-show ()( p' H5 H2 i5 L3 h2 p; r
  170.   (with-board1 V0 y0 F; X% o# W0 r6 {* M
  171.    (concat (buffer-string)3 j0 [% K, _; A. f
  172.            (if (game-over-p)
      n1 T& {' |3 ^5 P" h$ P7 p1 g
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    , y% a0 D4 h' p0 H  G6 c, O& b5 K
  174.             (format "第%d步" (1+ (session "step")))))))
    , ^& e( S  h+ {- y3 V: P3 M% X0 f9 _+ T

  175. 3 G& h% c# t+ [& K" `% T
  176. (defun board-position-parse (cmd)1 i" c- O" U# R/ r
  177.   (if (= (length cmd) 2)
    5 K- f! l0 _$ `  b
  178.       (list (string-to-int (substring cmd 0 1))
    + s* ~# ^4 A8 @0 [1 `& J
  179.             (string-to-int (substring cmd 1 2)))
    ' N6 f; {: g. o1 m
  180.     '(0 0)))
    " {. [) u# _' k) p+ e1 M

  181. 2 _* \& d# m* z
  182. ;;; 游戏房间
    9 \3 {5 I5 @$ Z. X
  183. (defun game-room-init (cmd)
    7 o2 @' m0 b8 t8 {
  184.   (let* ((middle (string-to-int cmd))1 f1 w$ ~* A4 _. v0 M2 C
  185.          (size (1- (* 2 middle)))): Q. z9 r4 ~# C' G$ Q
  186.     (with-board
    - w. e1 C4 a3 P- h: m/ H7 C. |
  187.      (board-init size)
    * _# |/ n$ z- t8 @) f0 A/ U+ G
  188.      (board-put middle middle)))2 {/ l* S  B) m
  189.   'game-room)
    + T  q) q$ [9 d+ Q3 ?
  190. $ {% ]9 a+ C6 k  P  u- _+ J
  191. (def-room game-room1 l' s2 F1 b' t
  192.   #'board-show' i( z0 z: j/ ~
  193.   (t (lambda (cmd)
      t2 P) F. t5 _, f1 \9 e
  194.          (with-board
    ' e$ t, \! j- Y9 ?. g5 M
  195.           (if (game-over-p)
    : Y1 t2 u4 W0 M1 y. D7 z! Z/ ^1 K
  196.               'living-room# {0 D& r: u+ K
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    4 }3 S5 {! `! b6 }3 P  m& Y+ s
  198.               (when (board-contains-p y x)
    ) O& {# t% t6 W& M; E
  199.                 (board-toggle y x)! K, d6 W. R% t. g- k+ D
  200.                 (session "step" (1+ (session "step"))))
    9 v: r2 s! S! e$ b6 D6 g
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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