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

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

  1. & Q; Z( t9 ~! ^$ u6 B& H
  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;">;; 定义新的游戏地图1 |. h; @; X: @6 [( g8 _
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL" g# K3 D# i# b9 v3 E* N
  4.   'tutorial-room-0)                     ; 默认的入口" @. M/ R% S; J- b! r9 e: V

  5. - O7 R( Q) H; Z8 L  I7 w
  6. ;;; 游戏大厅
    ( n5 ?. L& x. H- o  W; p6 n& J0 V
  7. (def-room living-room
    : Y) Q! R$ Z0 a3 ~' Z; I! q
  8.   ;; 进入该房间后的提示语, x) ~4 g7 H/ ?0 U# {
  9.   "1. 教程& l/ Z( L; ]& c; ?6 g8 q
  10. 2. 入门(3x3)1 `. r. E, V, ^
  11. 3. 初级(5x5)
    . r3 F$ R  H- R
  12. 4. 中级(7x7)' n" U. E3 Y  m; Y, z5 u
  13. 5. 高级(9x9)7 _0 T$ ^) H& }" n/ l9 c$ i& Y$ X1 T
  14. 0. 关于作者
    $ Y/ Y3 K) u5 Q  k3 j& j% v
  15. 请选择1-5开始新游戏:"
    " I& |' Q# s* l7 }/ P5 Q& Q& u
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名. W& U3 k* ]; Q5 G( p
  17.   ("1" tutorial-room-0)
    . ]* G; D! b8 h3 q2 K
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配7 U  ?6 B$ j' S7 P& f$ h
  19.                                         ; 相应的返回也可以为函数,动态返回房间名: U4 i3 f$ x& Q/ D9 z5 w
  20.   (t living-room))                      ; 如果条件为t,为永真5 O7 E7 i% B( i) e/ y
  21. 6 D4 C# F; l# S: f
  22. ;;; 作者信息3 W7 `7 z4 X2 E4 n$ L  j
  23. (def-room about-room1 m$ m/ O3 O/ `8 o7 _: l
  24.   "作者:redraiment8 R2 a! T! \* S/ [) ?0 x' u+ _
  25. 微博:http://weibo.com/redraiment
    , X" Y. D& _7 e+ M6 [2 ]
  26. 有任何建议,欢迎在微博或微信上联系redraiment。( M/ k& p. z5 d7 m% B
  27. 请输入任意数字返回游戏大厅。"
      y8 e8 |7 D% {
  28.   (t living-room))
    * H) j, i; W4 X
  29. : P/ c" y6 g' B* F
  30. ;;; 教程
    9 A- Q: q& Y2 H
  31. (defvar *wechat-5x5-tutorial-rooms* 0( w) C: W, m) l4 u9 @/ V# W2 A
  32.   "The number of tutorial rooms")
    ' f) f) M- k  ~) u% `

  33. # ]; P0 m# r. p/ _9 N( u) D
  34. ;;; 简化教程的定义
    4 @  m; a; ]+ d! n+ l' S1 ]
  35. (defun string-last-line (content)
    , @9 c$ @, Q' s" k% h" ]" R3 t
  36.   "多行内容组成的字符串中的最后一行"
    : _& e9 V4 p/ J5 N; X; k
  37.   (with-temp-buffer
    7 k1 S& u+ j4 W" P! U7 B5 |
  38.     (insert content)8 M3 y) y% S3 Z* e7 c2 K4 I" W+ P
  39.     (buffer-substring (line-beginning-position)" D& h1 X1 b2 Y9 n2 c
  40.                       (point-max))))
    8 j4 j! U" x3 \) g6 D; B! U  K

  41. / S0 A) f& _( s" c* n0 k
  42. (defun def-tutorial-room (prompt)
    6 z5 A5 N0 Y3 V7 u6 _- H5 y
  43.   "根据提示语自动生成教程房间。: p4 V) p7 x5 c3 s+ {( C

  44. ( Y: Q2 D' T4 d0 K1 c7 x
  45. 1. 提取最后一行作为问题;/ s) o+ K; S( x7 c5 P3 m" I' a
  46. 2. 分析问题,获取期望用户输入的内容;
    ; {; D* |/ q( [$ q
  47. 3. 定义教程房间和重复提问房间。"+ [9 ^: [+ U7 g. y$ Q( @+ r
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))" `2 C: w8 h! L8 l6 g2 E; j. i( {
  49.          (repeat-room (concat room-name "-repeat"))" K# E9 Q* j3 b+ U3 N: Q3 x
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))5 _6 {+ `) U# O
  51.          (question (string-last-line prompt))
    0 D) f2 S% f/ J& F3 k
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    ) G$ d) j2 G# Z: x- E3 V
  53.                      (match-string 1 question)))) u3 B) p! s+ e$ g
  54.          (doors (if except2 n  W5 A9 }( L( E+ s: i# f/ S& [
  55.                     `((,except ,(intern next-room))
    2 U( A, Z) q  J% g# `7 E! l0 @
  56.                       ("q" living-room); O( L1 F4 c% W, w2 u/ D" k# t
  57.                       ("Q" living-room)" u5 m6 [! n; K; |  |$ G
  58.                       (t ,(intern repeat-room)))$ d" D6 g% H' Y( V, }
  59.                   '((t living-room)))))+ T0 T, v7 R; @( g% ?% d+ r
  60.     (def-room-raw (intern room-name) prompt doors)0 |& e( _4 v# t# Q1 R& K
  61.     (def-room-raw (intern repeat-room) question doors)))8 c. p3 ]; y: o+ i

  62. # z9 U7 R" K# I$ ?
  63. (defun def-tutorial (&rest prompts)
    5 x2 P& c6 [! r3 K# m
  64.   "批量生成教程房间。"; ^: a/ ^. k1 j# u2 b# Y2 N
  65.   (dolist (prompt prompts)
    / y7 \$ r& q. _6 g! K5 f4 t& H
  66.     (def-tutorial-room prompt)))
    ) l" b/ q6 A' ^# h2 Z% @3 v
  67. 2 O% U3 h. V4 M. Y: O
  68. (def-tutorial/ q) {& f! L/ l! B4 p. Z
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。3 D' ?  L& Q! A! m' W( P" g
  70. 1. 教程
    , |* c) R( D9 l( Y8 h+ a$ ^0 q4 r
  71. 2. 入门(3x3)2 |& E* ?! k* t! K5 q5 r& h7 {
  72. 3. 初级(5x5)
    ) K7 N+ U1 V0 _* Y3 s+ `. R
  73. 4. 中级(7x7)
    : e% N+ a0 m  c  c/ N: z
  74. 5. 高级(9x9)% c+ [8 Z2 C8 F8 {. @2 c0 H. v. Q
  75. 0. 关于作者; N6 h8 I$ S! e
  76. 请选择1-5开始新游戏:- a, j, f% |& ?/ ~/ D, D  b
  77. 您现在正在游戏大厅里。, q0 `7 }' c* E
  78. 请输入“2”进入入门级房间"! o( }! s5 \0 u
  79.   "  ①②③% [& ?' k: S4 X. O0 A8 ^
  80. 1 ■ 
    ( e' f" E7 P5 C8 H7 f2 R) k
  81. 2■■■7 r. Z- v# I7 J% ]- T
  82. 3 ■ 
    - N6 `# M. I. D
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    7 M. q2 J6 M1 g
  84. 请输入“22”来关闭第2行第2列的窗户。"# s4 P# s3 o' D: _6 A0 p7 p) Z- \
  85.   "  ①②③; Z4 o, o# P1 B# `3 D, [# a
  86. 1   
    7 C7 g2 w* r' _
  87. 2   
    + ?, t% F4 ~' w! c
  88. 3   
      U' a9 f- {/ w
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    + r" Y" {6 I4 j, a
  90. 请输入“11”,试着开启左上角的窗户。"
    # k3 s! M0 Q/ N1 a8 c" ?
  91.   "  ①②③  F! z' [, `: p  j) J8 ^
  92. 1■■ 
    ) T: F) S1 |3 {5 f/ ?
  93. 2■  
    " l$ W1 M7 q( a- r: R
  94. 3   
    9 p. }3 u. q/ s  i
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。, }: T& w- z4 ~1 G# l
  96. 请输入“13”开启右上角的窗户。"
    . R, Z2 H) m. \/ p4 T
  97.   "  ①②③
    * Y* M; z) C) S6 k6 P- u( |
  98. 1■ ■
      X; \; x+ h. {( h
  99. 2■ ■
    " T  {8 q4 }- U0 ~' F
  100. 3   & W9 i: Y3 x1 j% D
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    6 t( a% i1 X1 o) L- M8 D  M
  102. 请输入“31”开启左下角的窗户。"
    ' k8 j) _$ b+ d$ b! ~, Y& `' U
  103.   "  ①②③
    7 y* H! x) r: \9 _2 h9 `" D6 A4 y+ P
  104. 1■ ■
    3 P% G3 K( D9 E5 b8 P5 ^
  105. 2  ■6 N7 u* c8 ~- ]9 V
  106. 3■■ 
    3 o% i2 S4 l" ?  U" k) }
  107. 此时,总共有5扇窗户被开启了。
    4 ~5 z/ O1 _& `" ^2 E/ ^  L
  108. 请输入“33”开启右下角的窗户。"( [+ n3 J/ t6 D9 A2 V
  109.   "  ①②③
    # y% M, [: V$ m5 s
  110. 1■ ■
    ! c/ y8 [) Z. f1 h9 k9 @+ q0 n: d
  111. 2   
    ! J5 C3 }% R, x* |# C
  112. 3■ ■
    1 S/ u! B6 H+ J" a, d: g9 g
  113. 现在,只有四个角落的窗户被打开。
    / v1 f. l3 K9 E* g
  114. 请输入“22”完成最后一击!"
    " y6 k. e6 T' n" t- a
  115.   "  ①②③
    " S+ O0 y: N  B$ e+ C8 G" F" M
  116. 1■■■8 r. V- u9 v3 _$ V4 g0 g
  117. 2■■■
    4 ~+ Q& k( }9 Z1 r! r$ D3 b
  118. 3■■■) ]& e- X4 S6 S. [
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")8 r/ q/ _5 k9 Y% a( r7 r8 a  s9 O

  120. ( H2 }/ _+ ~0 D9 n: O" |! B
  121. ;;; 棋盘
    1 p1 @4 L9 A5 b$ y, v" w0 a
  122. (defconst *wechat-5x5-white-chess* 12288- Y) E( O) u' {1 Z2 q2 k1 X- E
  123.   " ")  ^3 q3 O" e- S& c4 Y9 A7 r
  124.   U- H% I, g+ f1 {8 o; H' I8 f
  125. (defconst *wechat-5x5-black-chess* 9632
    0 _' _. H9 J: p- U
  126.   "■")
    . c* j# O8 ?# U6 J' m: K
  127. % y2 c* y1 P; O& U. ]
  128. (defmacro with-board (&rest body)) {, ^0 v7 L/ u& o* `4 X
  129.   `(with-temp-buffer: c+ p( j$ _5 B
  130.      (unwind-protect
    7 `' D2 Z0 z2 [  _5 u
  131.          (progn  s/ F* k  K/ G( W
  132.            (if (session "board")
    4 }1 x9 p: g7 c8 n' B
  133.                (insert (session "board")))! m0 _! ~0 g# e1 Q2 y) Z  w
  134.            ,@body)2 Y/ H; @* ~% j) I* @
  135.        (session "board" (buffer-string)))))- D2 K, q. k" g+ f1 o
  136. " @2 o: r! N, Y$ w- S6 f
  137. (defun board-init (size); E" F" z) g5 m; O; k
  138.   (session "size" size)) R  E) `' R1 y+ N3 t' x
  139.   (session "step" 0)8 s5 y, ^( C! ?
  140.   (erase-buffer)& D& @9 _1 `, C
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))# ~* ~) ]2 C7 J& s  L
  142.   (dotimes (row size)
    ' D6 ?* W) J: k( d- ]1 i+ M% w
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    ! G* y4 q0 u5 v

  144. 0 D, Q8 f  K6 Z' X. O  E! j
  145. (defun board-contains-p (y x)& ?" A5 L& v* q/ H, @
  146.   (let ((size (session "size")))
    * H& ]% y' A/ j# I
  147.     (and (<= 1 y) (<= y size)
    / `+ _* o: i  _7 Y- H! n- o: t
  148.          (<= 1 x) (<= x size))))$ j& M# p' q( v  A
  149. ) N8 S; n' w8 o: s- C
  150. (defun board-toggle (y x)
    : K& Y7 F, ^. w+ E( y1 |3 e
  151.   (when (board-contains-p y x)& ]4 f: D0 ~- a
  152.     (goto-line (1+ y))
    $ T9 F( i( O$ e  j; j  ?
  153.     (beginning-of-line)* B; ^: P- L1 R1 n8 W
  154.     (forward-char x)7 f, x! }, b' s1 P; ]/ D/ U" D$ @
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))3 u9 ?5 i$ K' U' E
  156.                 *wechat-5x5-black-chess*
    8 k) y1 q% I5 Q5 e' m: W
  157.               *wechat-5x5-white-chess*))
    ! |- W2 |# w! B) ?
  158.     (delete-char 1)))" O$ r4 f, O, x# n) L
  159. 6 k9 |6 q' b9 m/ a/ @; [
  160. (defun board-put (y x)
    / H. S- J" R1 x/ t( ^
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))' _+ j& c' A' r2 T; V
  162.     (board-toggle (+ y (first dir))
    ( G+ n/ o* k) D& f& @% s
  163.                   (+ x (second dir)))))! [( s! k6 D7 k$ `
  164. ' M7 M2 t2 S( f8 w. H# r/ r# g" v
  165. (defun game-over-p ()
    3 j) e1 P) C- O1 E- l
  166.   (beginning-of-buffer)
    6 m" s4 @2 M8 U  Y. q; A. |5 i
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    . Q: d5 f; ]; {! X7 D) o& E, t

  168. 7 D1 `4 n4 C0 c! f6 i
  169. (defun board-show ()0 u- {7 H0 L/ i
  170.   (with-board
    . F6 h. [2 ?1 N% P0 {
  171.    (concat (buffer-string)
    ; i) g- s* q( L4 D# t
  172.            (if (game-over-p)" b& z9 `% J  G6 p( w& m$ r
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    - b! D  s8 D6 I+ L
  174.             (format "第%d步" (1+ (session "step")))))))
    9 v1 v! `, A- s2 ~% ^

  175. - l/ a4 z8 v$ ]7 o0 T' y# g% r
  176. (defun board-position-parse (cmd)+ `  z0 @8 E+ h' X5 l1 Z
  177.   (if (= (length cmd) 2)- ~' B. [" u9 d+ o
  178.       (list (string-to-int (substring cmd 0 1))
    - |8 l) N4 \8 c" E$ w9 v
  179.             (string-to-int (substring cmd 1 2)))
    : M/ E- i  q$ h: f! M
  180.     '(0 0)))6 N; Z- k: I3 ?, ]+ U9 v
  181. / G. d8 ?) [. C
  182. ;;; 游戏房间$ z6 m: M  n" N1 J$ q0 w0 r
  183. (defun game-room-init (cmd)2 o- m' }* F7 u
  184.   (let* ((middle (string-to-int cmd))
    . l$ Z! T. T0 e& d# y' D
  185.          (size (1- (* 2 middle))))' r$ I1 n7 {9 l, h: F
  186.     (with-board! u' g$ u$ S6 K# N
  187.      (board-init size)
    ' X, o  |4 Z  J8 W9 D0 U0 J
  188.      (board-put middle middle)))$ q# i5 h( U; T8 y
  189.   'game-room)6 j9 v2 p- |1 M* }: U9 u3 Y" n
  190. ' Q1 P7 R8 T% R4 q1 W
  191. (def-room game-room# j6 |% b/ H) `, J4 k0 b- V! _5 }6 g
  192.   #'board-show+ t0 x+ `( W6 d) e! d  i% d
  193.   (t (lambda (cmd)
    0 _+ [4 n$ \9 p
  194.          (with-board1 f; s+ g6 @7 R# l8 y
  195.           (if (game-over-p)
    ) E4 L, _9 I0 d2 _8 R
  196.               'living-room8 r; o8 N0 ~6 m6 e4 p3 A+ x
  197.             (destructuring-bind (y x) (board-position-parse cmd)* M6 I' A6 c1 ~0 {$ Z5 @0 C8 }
  198.               (when (board-contains-p y x)9 Y& Q8 F5 C7 K& H; {% ?' c5 w
  199.                 (board-toggle y x)2 }! T, |8 t! S9 Z
  200.                 (session "step" (1+ (session "step"))))/ Q' b0 Y" b- }& P% [  \
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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