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

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

  1. $ t9 `+ i/ \2 @
  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;">;; 定义新的游戏地图5 I+ J7 P5 ~' O" u& u5 ]5 {
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL# x* e5 Z: H/ g9 g3 m3 s0 t
  4.   'tutorial-room-0)                     ; 默认的入口0 b, f0 }' A8 i  t4 f5 [
  5. 8 @9 s" d1 q3 Y3 j- s6 y
  6. ;;; 游戏大厅: B2 T2 j& O+ V0 o* @# ?
  7. (def-room living-room# }! z* a6 ~# _: F1 s& ]7 E
  8.   ;; 进入该房间后的提示语, P" r0 i6 A$ m6 v
  9.   "1. 教程0 Q2 P$ ]" Z4 |& B/ k9 e
  10. 2. 入门(3x3)% x! l: O4 k" W! t. F
  11. 3. 初级(5x5)
    4 o- w' f# w1 h$ c! Q
  12. 4. 中级(7x7)
    1 O+ I5 M1 R$ e, r" i6 m$ \, I
  13. 5. 高级(9x9)
    - Y7 G9 h( ^, M" _0 r; i/ d
  14. 0. 关于作者% _5 E9 J' a) y+ O/ w* g
  15. 请选择1-5开始新游戏:"
    : ]1 @6 _" e5 z
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名! Z+ h# S, [% s( K: j
  17.   ("1" tutorial-room-0)
    * M+ o$ k5 e' m: t2 `& l$ f9 O% \
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配+ C6 H( c' x, o  k, x( W% q
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    2 r, p! m+ b9 I( n
  20.   (t living-room))                      ; 如果条件为t,为永真
    , _+ o+ w: P- i9 s  n% r

  21. 5 D2 \5 M& n! S4 Q1 |
  22. ;;; 作者信息3 ^/ ?$ ?  D& D5 g; p
  23. (def-room about-room7 j, }* {+ u3 |7 q9 [/ U7 _
  24.   "作者:redraiment1 A9 C2 f( s6 F% E) R1 [( T, [
  25. 微博:http://weibo.com/redraiment: Q4 x, ~& I* `  r" q& Y
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    4 c8 S$ l* M$ f) e. p
  27. 请输入任意数字返回游戏大厅。") k- i9 F, P5 s: Q4 U
  28.   (t living-room)). [! T& _3 j: {
  29. . u6 ]& m5 W% g" o/ T, U3 R
  30. ;;; 教程
    3 ?* S$ |! N" O  Y
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    + \& v# c+ K+ k! K
  32.   "The number of tutorial rooms")& H: q' S  T4 l

  33. + r( a( `# P' d  g2 a
  34. ;;; 简化教程的定义
    ! z. c1 }8 j* V
  35. (defun string-last-line (content)
    6 |4 `( {- l9 m- P3 E
  36.   "多行内容组成的字符串中的最后一行". v. [- t; _4 `% R2 Z: w5 u+ D. o
  37.   (with-temp-buffer5 T" `/ Y  u, D" m/ L, A0 f+ Y8 b
  38.     (insert content)
    ' a' T% O" S! w4 J6 i
  39.     (buffer-substring (line-beginning-position)/ w  H7 P6 X$ o5 G
  40.                       (point-max))))" |/ [* e' i2 w7 {2 \# ^7 d
  41. / U: p0 [4 [! l0 u: [) R& L
  42. (defun def-tutorial-room (prompt)1 p* `! N* G0 E% f& L
  43.   "根据提示语自动生成教程房间。& C1 R# K7 A9 Y) P( o- T
  44. 5 m  y/ \& b; L: ]) ]5 q
  45. 1. 提取最后一行作为问题;7 L1 F: S* U6 e7 T4 S9 r
  46. 2. 分析问题,获取期望用户输入的内容;
    - ]" r* E3 }- F  R5 k
  47. 3. 定义教程房间和重复提问房间。"! }* q& A, U5 Y
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))2 m7 t( o% _& c3 Z. q! ?
  49.          (repeat-room (concat room-name "-repeat"))
    . t! a% p. C, _3 j
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))% p6 \4 t" J* o8 O( L
  51.          (question (string-last-line prompt))! y$ V  Q; R: N; z
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)  Z1 ~/ y2 p- B. W' s
  53.                      (match-string 1 question)))' i6 j* j! o1 ~
  54.          (doors (if except, H: `9 z# Z2 Y. v, D, [
  55.                     `((,except ,(intern next-room))0 x# T& W+ c  `' B( m; g6 L6 h
  56.                       ("q" living-room)
    1 W) ?# [3 [! r
  57.                       ("Q" living-room)) E* C5 u  m/ b( P" Z) z
  58.                       (t ,(intern repeat-room)))- D6 ?( Y/ d2 E, I
  59.                   '((t living-room)))))4 o" y! o; x" i0 b' `8 r4 F& G
  60.     (def-room-raw (intern room-name) prompt doors)6 Z" ?( p, t( O4 e7 k
  61.     (def-room-raw (intern repeat-room) question doors)))( w) U" C/ U4 r4 O% d$ x
  62. + r' }, p6 P! }& V7 Z7 @
  63. (defun def-tutorial (&rest prompts)
    : U: ~! |" L1 w7 n; L$ s! l
  64.   "批量生成教程房间。"( G& ?& u# W8 @+ Q& o5 y$ r
  65.   (dolist (prompt prompts)
    7 M& e' h( i# {4 t/ k
  66.     (def-tutorial-room prompt)))+ e( v2 O* m( `( y0 x7 a

  67. 6 @! C# S( ~, a- F
  68. (def-tutorial; x: [$ Q) ]0 y( C& Q
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。5 w0 @2 _1 f. ?$ l# ]
  70. 1. 教程0 ]/ v7 L. x! f  _3 j
  71. 2. 入门(3x3)) J; Q* O9 j+ a. T
  72. 3. 初级(5x5)8 l& {7 G, \) a$ c( O  I' F
  73. 4. 中级(7x7)1 k. w4 N/ }& u5 d
  74. 5. 高级(9x9)
    + p! g9 Y( {! j7 Y5 d9 w! |$ d
  75. 0. 关于作者
    8 e6 Z: q) I1 o) L0 }
  76. 请选择1-5开始新游戏:: b) b3 ]+ w% j) |, K4 r+ {) K
  77. 您现在正在游戏大厅里。
    - b/ T, M* `3 u
  78. 请输入“2”进入入门级房间"! ~# ^3 z% p4 {# q/ U5 F7 ]% l
  79.   "  ①②③1 C% b( g( C  i' Z- K( C2 w" [
  80. 1 ■ : o' b5 P2 h! }1 U: C
  81. 2■■■
    4 a4 H/ m: d; A% C+ k
  82. 3 ■ # I& Y) ^9 l4 l6 `
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!) L$ V7 t, J8 d3 [* H- a) `
  84. 请输入“22”来关闭第2行第2列的窗户。"
    ( [1 z$ u/ f( N/ B
  85.   "  ①②③+ h, S  t3 T1 O7 m* t5 j
  86. 1   ! i& \, [  H( e2 R
  87. 2   2 q0 r+ y1 h$ ~: W2 G
  88. 3   
    : ~% a' k: y3 {7 }. u
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。8 U2 U( U3 Y6 t' {7 g
  90. 请输入“11”,试着开启左上角的窗户。"
    * G3 c' `) b" J2 _! E
  91.   "  ①②③
    + z1 L; k( z( B
  92. 1■■ 
    - W) G' K. _: z3 u! Z/ ~* V' p) ^
  93. 2■  ) }# H, W" p% k- K. k1 P
  94. 3   
    ' T  w% z1 ?' \1 p9 ^
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    $ Q& n) c# j( X
  96. 请输入“13”开启右上角的窗户。"5 a4 u0 X" h$ E
  97.   "  ①②③
    / D- H3 [. Q' @% b& y
  98. 1■ ■
    5 I: D9 O$ w6 B$ X
  99. 2■ ■
    1 A& j7 \! d  S) P4 _
  100. 3   
    / ?+ e( k/ C. ]5 ?# ?
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。- O% ^/ d( J+ N, N
  102. 请输入“31”开启左下角的窗户。"" F" _3 a/ E  g: \9 s' N
  103.   "  ①②③4 h" ]# L6 S: v" {# q1 R, p
  104. 1■ ■8 y" g" Q$ B. Q8 a
  105. 2  ■; {, _7 K/ c: h
  106. 3■■ 6 p  B, J8 U( s# b
  107. 此时,总共有5扇窗户被开启了。+ Y+ \" R- N" }5 v% N) m0 _3 ?
  108. 请输入“33”开启右下角的窗户。"2 g. {+ X5 F/ F! f
  109.   "  ①②③' N; K0 o1 I5 K/ j* a
  110. 1■ ■1 S6 o; c2 C- S0 l- b# T- }) E
  111. 2   
    & B7 Z! k% \+ M6 l' w- ~( `
  112. 3■ ■+ L8 z2 ^1 u* p( P- I7 X* |* R* O
  113. 现在,只有四个角落的窗户被打开。
    $ k- @" B, d* B! V' H  c1 g# l1 ~
  114. 请输入“22”完成最后一击!"
    $ ^: N0 C" o0 A! l! F0 L" N
  115.   "  ①②③
    2 [3 g) r2 j$ c
  116. 1■■■
    " h2 d# L  Z  z- a. H" a
  117. 2■■■
    % l$ @% ^( h3 R8 U
  118. 3■■■. h  z; j% I  G: u2 R7 K; x
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    5 K+ _9 q: A% H: [) m7 i" p

  120. 8 h6 w& l' k# D$ R; j1 x4 H+ M
  121. ;;; 棋盘% v$ Q* G! x" L: o. J$ o
  122. (defconst *wechat-5x5-white-chess* 122886 G% f! v! l' n+ E0 k+ n
  123.   " ")
      w0 b" k6 l( O0 z# w3 k& ?

  124. 4 C, W0 l1 Q7 m  O
  125. (defconst *wechat-5x5-black-chess* 9632! \( N! s4 Y9 X! T  q
  126.   "■")7 j* [  E/ I" J' a" U$ q$ J

  127. $ z* s6 S% s: J" e/ ~  l0 k
  128. (defmacro with-board (&rest body)
      R0 n; B9 D! X  m' q
  129.   `(with-temp-buffer7 C3 J# I) ^, A/ T3 r0 k
  130.      (unwind-protect& E. a" j$ ~" N3 E9 k5 U
  131.          (progn
    ' h/ c, [6 C7 U  K# o
  132.            (if (session "board")
    & Z! r% C. d: b+ u: B
  133.                (insert (session "board")))
    # Q  w1 e: }( ^" |7 `
  134.            ,@body)& J8 U; B/ x4 T# L: j* W& R
  135.        (session "board" (buffer-string)))))' x6 t* G: C7 N" V6 W8 [
  136. ) |" c, [9 `# }- S- l4 |
  137. (defun board-init (size), z3 Y5 i2 r: m; h! ~7 b* w
  138.   (session "size" size)
    # d- F" t  p: Q" B  z+ [
  139.   (session "step" 0), s5 Z: F) E& F# B! M. q( }
  140.   (erase-buffer)- X; O. `9 t3 S4 w
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    : V9 n( K, x1 u3 A
  142.   (dotimes (row size)
    . [8 p' @: |$ \' F9 t
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))# L4 B% i( U- b$ {- c0 r" n

  144. , }* q  }4 A/ H& @
  145. (defun board-contains-p (y x)' j1 i  v: E/ r( w* M- h0 B9 h
  146.   (let ((size (session "size")))# a+ J# R* K% a# ?$ F
  147.     (and (<= 1 y) (<= y size)
    $ \( a( Y7 l0 W
  148.          (<= 1 x) (<= x size))))) V" {1 p0 u/ Y, P! Q
  149. ( {/ g! Q8 i: H2 Z; I) J
  150. (defun board-toggle (y x)
    , f, t0 V* J) W  T8 ^. c! L
  151.   (when (board-contains-p y x)- W, u8 X/ d6 t2 g
  152.     (goto-line (1+ y))
    ! v3 V1 S3 V  b) v' b, {
  153.     (beginning-of-line)
    : L: Y' L  u  w4 H6 G
  154.     (forward-char x)
    " \" E/ S. n* L
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
      R) t9 G  ?: m
  156.                 *wechat-5x5-black-chess*
      I8 b! |: Q2 p' f9 o- T
  157.               *wechat-5x5-white-chess*))9 l, C5 u  G( j" A
  158.     (delete-char 1)))" Y) J6 a5 g- G8 Z, y
  159. ' A  J# h, u( T% `' J  \4 L
  160. (defun board-put (y x)4 G3 J$ l4 ~" K+ \. P, q" u$ p
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))5 j& h0 g6 O/ W2 Z$ H" a1 C$ ?$ H
  162.     (board-toggle (+ y (first dir))5 e# D1 Y3 d, [% J$ t
  163.                   (+ x (second dir)))))
    ! D/ Y1 q* u5 D0 J
  164. 8 o, u* j4 a  h
  165. (defun game-over-p ()/ o& U. m7 S6 L+ x. z; }; Z
  166.   (beginning-of-buffer)0 t$ G; e3 t7 ^9 L  d
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))& P0 j& f3 k! B2 D: m& `

  168. 5 S, c" P0 s% a
  169. (defun board-show ()
    # w7 j5 z* P/ x  e% i
  170.   (with-board$ H! b, u: }* p: h! p% b7 V9 p
  171.    (concat (buffer-string)9 ]' f" j- p& n' ^4 S+ D4 f; |# [% v8 O! @
  172.            (if (game-over-p)! E( C1 ^- h$ w: n7 B: A. W
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))0 a  z8 E. k; Y
  174.             (format "第%d步" (1+ (session "step")))))))
    & s0 N- C8 w( K7 [/ Y) P+ \
  175. - U3 y4 B% a: i6 a& J
  176. (defun board-position-parse (cmd)/ n9 u2 [) ]# T* `1 n; a  W5 B
  177.   (if (= (length cmd) 2)/ r" V9 ?2 j- `
  178.       (list (string-to-int (substring cmd 0 1))
    2 C' U1 b* ?; [4 o/ }0 V) r+ P
  179.             (string-to-int (substring cmd 1 2)))
    $ g+ O/ L) O; m8 Q' a
  180.     '(0 0)))
    % O! m/ E3 l  W" `1 t7 O" C. p' o
  181. 4 y0 g! p+ l5 \' x" e/ g2 `8 n
  182. ;;; 游戏房间
      j! s1 i0 G) I5 d6 ~
  183. (defun game-room-init (cmd)" r* q9 e/ }7 W0 S! D, B
  184.   (let* ((middle (string-to-int cmd))
    7 |) i/ \: _9 w  x- v6 l
  185.          (size (1- (* 2 middle))))0 B! ~2 b  Q4 j' H
  186.     (with-board4 n/ P, q3 \+ y6 ?
  187.      (board-init size)) X% u4 _& ~3 Q1 w
  188.      (board-put middle middle)))2 [) I4 e' Q! l
  189.   'game-room)
    " C& [7 ]+ H5 x- h

  190. ) E8 |. Z7 \. d, K/ J# b2 L
  191. (def-room game-room# {/ q$ J; a- i/ _
  192.   #'board-show
    & l# t' O9 y1 g# F# j$ L
  193.   (t (lambda (cmd)- `1 W+ N; x9 u, r7 g
  194.          (with-board
    # M7 @/ m( ^- r. @# B4 h
  195.           (if (game-over-p)$ o8 M. J) {. O6 j- k3 H) _; \. h
  196.               'living-room0 `9 s$ `# b1 d: D2 d
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    * A8 \4 W- O7 |( [  ?
  198.               (when (board-contains-p y x)- h( i/ C) ?8 o, q$ z% B
  199.                 (board-toggle y x)7 B' Z. j+ U3 C% a, H, N" Q
  200.                 (session "step" (1+ (session "step"))))9 }/ }: w+ a; Z1 Q7 G
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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