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

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

  1. 3 u0 ^7 E! w( ?  \0 w/ V
  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;">;; 定义新的游戏地图  }! E4 G5 K. X' d
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL4 R8 H# L7 V+ E2 i# H& c
  4.   'tutorial-room-0)                     ; 默认的入口- r/ c7 V- d3 z" \

  5. 2 T' Z$ \0 N4 J  `* W. _
  6. ;;; 游戏大厅' \. R& S; `/ g2 N
  7. (def-room living-room
    , u! {5 Y6 d" z) |7 l0 i) f* ]6 p
  8.   ;; 进入该房间后的提示语% ?+ Q: f+ p3 x  B
  9.   "1. 教程
      l8 K  C1 Q, X/ w# K* W; o& A& o
  10. 2. 入门(3x3)) o4 O- t; S* X3 y4 p0 ]
  11. 3. 初级(5x5)6 R$ `( w4 c+ j8 ~2 _& g6 F
  12. 4. 中级(7x7)
      j: l. u3 t6 A4 X* c; U
  13. 5. 高级(9x9)9 j, i* L1 k. H6 A% p  f
  14. 0. 关于作者/ V( [+ |- W) m) \# Z' O# |7 D
  15. 请选择1-5开始新游戏:"0 G9 j0 L7 }( \) ^) t6 P
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    . }9 _8 E8 }( k' J: F
  17.   ("1" tutorial-room-0)4 X' _6 R5 d; @
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配7 v0 C6 A* z8 }) T* s& B5 P( \
  19.                                         ; 相应的返回也可以为函数,动态返回房间名, T9 l$ F9 B, M* C- y- Y  D
  20.   (t living-room))                      ; 如果条件为t,为永真0 E: t% ^% _8 E
  21. & t) _9 x3 \5 r1 r' C( ~
  22. ;;; 作者信息( w) N" L/ a& @- x, j
  23. (def-room about-room9 d, G8 G1 u. s' u/ d
  24.   "作者:redraiment! z3 g4 U0 s2 Y  g' H6 i
  25. 微博:http://weibo.com/redraiment
    ( W( x# ~* J8 L' g; m1 D( w5 y
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    & p. T, l2 A* f% _( R4 M- d* n
  27. 请输入任意数字返回游戏大厅。"& a% W' c2 i" {
  28.   (t living-room))
    ( [! t5 l. [9 I* j# @7 ^
  29. 9 g6 i4 H* }) r
  30. ;;; 教程
    3 [8 t2 f% |1 a+ w! R
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    $ I8 _0 W$ w/ o6 }  _0 a  V
  32.   "The number of tutorial rooms")
    9 J/ Y" O% n$ K: t/ P6 ~5 D

  33. 9 I% F" x7 ~- u- s0 E
  34. ;;; 简化教程的定义& K8 e. r- }- e' t/ c) G& z
  35. (defun string-last-line (content)' O8 S. L" }7 C, I- P4 N/ o/ o
  36.   "多行内容组成的字符串中的最后一行"
    9 S- z) |& M* S# A  X- i7 y1 u$ g
  37.   (with-temp-buffer( B) O* s# @4 x3 C) u5 A+ a3 l
  38.     (insert content)6 F* s5 S  p0 J2 h
  39.     (buffer-substring (line-beginning-position)
    7 [7 q7 E6 K$ o0 v
  40.                       (point-max))))% _5 n+ v! W( I

  41. $ c+ r2 s8 i( X/ l+ G
  42. (defun def-tutorial-room (prompt)
    & {0 S% F; s9 n# N' b. U
  43.   "根据提示语自动生成教程房间。
    / i# o6 A& z9 J3 R7 Y

  44. # @7 u; b9 O2 o3 X; G
  45. 1. 提取最后一行作为问题;8 I# `% f, m; @( p" G) Q
  46. 2. 分析问题,获取期望用户输入的内容;# t- [0 r# k# f7 D
  47. 3. 定义教程房间和重复提问房间。"
    - t& B8 ~7 J* M% V9 S( c1 G2 K
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    : D; q0 ~# `7 K) v- }+ H/ ^* h
  49.          (repeat-room (concat room-name "-repeat"))
    1 B. m  r' V# a5 `1 j9 F0 C, c
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))/ ^5 H8 A$ |& G, q% \
  51.          (question (string-last-line prompt))
      h1 Q: G2 O8 `9 e# p) H
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    ! k$ v: q* h& }
  53.                      (match-string 1 question)))* E* Z2 D2 t; t, N) l
  54.          (doors (if except3 o1 |! N: ^$ W, [. r% r
  55.                     `((,except ,(intern next-room))
    6 p" B0 S" s! s" ~
  56.                       ("q" living-room)
    % q: s# l. n1 D) P* D
  57.                       ("Q" living-room)
    4 C# b' y7 E* ?% h3 \6 g
  58.                       (t ,(intern repeat-room)))
    , `$ c+ `5 M* A
  59.                   '((t living-room)))))
    / L; V1 U- B. C, k
  60.     (def-room-raw (intern room-name) prompt doors)
    ' G; I9 I) x  d; U/ h: o2 ?; i1 \/ n
  61.     (def-room-raw (intern repeat-room) question doors)))
      L* O& U3 v! V8 |: T: j; `5 x' a
  62.   G( V$ E4 B+ w
  63. (defun def-tutorial (&rest prompts)7 y% ?# Z, b% o  a. z% W
  64.   "批量生成教程房间。"$ x2 W8 O# Q/ ]4 A8 N
  65.   (dolist (prompt prompts)! ]9 g1 {: A) K8 y) D& ~2 J: ?
  66.     (def-tutorial-room prompt)))2 H8 B. f& t2 S, B" L/ N

  67. * m" U* R% W  E7 C% D; m- N
  68. (def-tutorial4 @3 p2 a! ^# Z% [% `9 ?6 E0 Y
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    ! Y% T' z8 a6 b, E; t+ d
  70. 1. 教程
    6 }# @" }. C1 Z$ S- m  [% M# V
  71. 2. 入门(3x3)  U- ?3 |( j. o, o; m
  72. 3. 初级(5x5)( H% d0 q2 }- O, q; t7 _
  73. 4. 中级(7x7)
    5 C  s4 x$ q; U
  74. 5. 高级(9x9)
    7 X; T! C# J+ M' D: k/ \
  75. 0. 关于作者
    6 y! v5 X0 g* _
  76. 请选择1-5开始新游戏:8 N5 K9 J( u8 u, r( j, k
  77. 您现在正在游戏大厅里。
    - x. ]) e, k5 E% N
  78. 请输入“2”进入入门级房间"8 D$ K' S' N7 P( `" `$ [
  79.   "  ①②③
    " L$ j) K+ U2 V# j0 Y; z* N% r
  80. 1 ■ 9 a7 a. ?* q+ T$ h  Z
  81. 2■■■( V' F! o9 o0 L& o/ [# i/ C. d# |
  82. 3 ■ 
    9 t$ i0 a8 I, Y' [. K3 L' a* t
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    5 l/ `- c% m5 k/ ], ^. E7 L
  84. 请输入“22”来关闭第2行第2列的窗户。"9 j1 @2 o/ g( ^: a, ^
  85.   "  ①②③) _/ N2 A  g6 ^( O5 a. i
  86. 1   - @1 f7 j& F- ^
  87. 2   / V+ u2 f7 |8 k7 D& G3 w
  88. 3   
    / ]6 G! S5 B) ~# L7 X9 k- V; M
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    8 E5 A) y# x! n: w( y
  90. 请输入“11”,试着开启左上角的窗户。"4 J% t3 L! C* f  C$ X: {
  91.   "  ①②③
    % v. q; R$ p: @
  92. 1■■ & h" d, ?( n8 L& W0 f
  93. 2■  2 T9 I  P; ^) i/ f: _
  94. 3   ! O" ~" W* J8 T/ y! K- k
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。8 }) K- Q8 |) n& p( S
  96. 请输入“13”开启右上角的窗户。"7 ]6 ?5 o! x8 R
  97.   "  ①②③
    , A2 ~! A3 T# p1 K' {# M
  98. 1■ ■
    ! `  e% P' u/ Y% L
  99. 2■ ■
    * I* m( l0 P) {8 _) i
  100. 3   
    , l* @% v  {, `0 a
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    6 m$ ?7 t# v6 `- x
  102. 请输入“31”开启左下角的窗户。"3 Q. E, ~8 i2 F+ ?- B# ^' n% D
  103.   "  ①②③+ l! f2 Y# H& s; p# R, {
  104. 1■ ■
    ' F" i. l- I3 T, W2 R3 M5 t
  105. 2  ■3 n) J: y. j' b$ A
  106. 3■■ 
    0 ?. z; D1 B! y, Q! O. K
  107. 此时,总共有5扇窗户被开启了。/ Y3 R$ K' X3 M6 P
  108. 请输入“33”开启右下角的窗户。"( I7 o% H# q- U- _. V! h
  109.   "  ①②③
    % d1 g( G0 J3 O1 o* w9 S( R
  110. 1■ ■- U5 @0 u' Y  }: g" L4 c$ q
  111. 2   - G) \) t' |$ l+ |! Q7 x1 ]. ^" j
  112. 3■ ■
    ' n5 X9 \# b( `& x
  113. 现在,只有四个角落的窗户被打开。, c4 W; Q) t! K. L' g  f
  114. 请输入“22”完成最后一击!"  C  S# e3 V7 w8 @9 Z; o. F
  115.   "  ①②③% |, [3 W6 L" T4 B8 a2 _9 }
  116. 1■■■
    ; O; t) Z  h0 a0 U
  117. 2■■■
    # G3 g2 ^/ x8 b
  118. 3■■■+ Q, C% x5 j, `) E, |& u
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    3 m' Y1 w$ F( ~5 n

  120. 1 h) p' d2 `2 M/ m7 o" B
  121. ;;; 棋盘) }5 G2 j2 o! b
  122. (defconst *wechat-5x5-white-chess* 12288
    ! _' ^* u- Q" _, t
  123.   " ")
    4 ~& O) g! G. ]5 f
  124. $ F3 s! y' F( ^) g, g: v
  125. (defconst *wechat-5x5-black-chess* 9632
    % s2 J# {+ Y! Q9 F, e) q
  126.   "■")* ~1 @- ^- ]  V8 @( _" b: h

  127. & k4 z& |3 U& M7 [, \/ }" V; {
  128. (defmacro with-board (&rest body)
    8 l: @* D' i. Q% a7 h% H
  129.   `(with-temp-buffer3 [5 `/ r$ I6 H% x7 n( H
  130.      (unwind-protect7 d7 l( U# n/ \$ N  ?* v. h4 u
  131.          (progn8 A" G- K# a8 ^. F8 w( k- }5 B
  132.            (if (session "board")
      A+ }' E+ r! B& {5 P5 G+ K3 c- k
  133.                (insert (session "board")))
    , g- j  E# i5 B: _
  134.            ,@body)8 e/ z: q# B$ g1 d( [1 v5 O
  135.        (session "board" (buffer-string))))). Z. J# e4 e9 Y* i0 M
  136. ; t& X/ i7 p7 O/ Z1 J& Y& [, K$ J- Z2 @
  137. (defun board-init (size)3 `; s% G# l8 C$ ?
  138.   (session "size" size)/ e4 }$ v# ?* h, k
  139.   (session "step" 0)
    . W3 e" R5 ]1 d7 ]
  140.   (erase-buffer): M" ?* G" d2 r# H
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))  {- Y- s. D& o0 h
  142.   (dotimes (row size)8 c4 a1 y/ W* E
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    , \* n6 m/ p. S2 f4 M

  144. . [; L. ?3 T& K" G% i
  145. (defun board-contains-p (y x)1 V3 r# |/ q% c4 Y3 p
  146.   (let ((size (session "size")))5 D# o  F, P9 c) ]  r6 [
  147.     (and (<= 1 y) (<= y size)6 \' o* q6 N" j% L- `- }2 H7 h5 V
  148.          (<= 1 x) (<= x size))))2 a1 q* R% M$ b
  149. 6 A! y7 w6 F( N; `% J. @# Q
  150. (defun board-toggle (y x)5 q6 E/ ]7 f& \( b
  151.   (when (board-contains-p y x)
    ! H- X6 ~0 A$ b3 I( H/ s
  152.     (goto-line (1+ y))
    6 N" T1 J, I( a- X( n; M
  153.     (beginning-of-line)* Q2 i( P0 S" q* S& K2 @9 l
  154.     (forward-char x): k! I, j3 _" }' C$ T
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    ; h4 H2 `: j0 l
  156.                 *wechat-5x5-black-chess*: p1 W7 E" _) K' N/ ^# z9 _3 D* q7 {
  157.               *wechat-5x5-white-chess*))
    % W( K  h* T- C$ C1 H& @  s, v, M
  158.     (delete-char 1)))4 a: @( O; ?; x) t

  159. $ m2 r( S0 e+ w) g5 z7 V7 r! k
  160. (defun board-put (y x)
    6 k" n  q$ k/ s1 ?6 S4 ?
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))  S. @  S1 T( w& H' H
  162.     (board-toggle (+ y (first dir))
    " [) }( [- ]' H+ [
  163.                   (+ x (second dir)))))
    3 \& q1 V9 K) S

  164. ' Y: n8 _# l* q
  165. (defun game-over-p ()
    ; a; ~1 f. A2 Y% _( L
  166.   (beginning-of-buffer)* e% R( N, m8 S* z; H; {. \
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))6 o3 G5 L0 E* @+ j5 ^9 ]

  168. 9 f2 O' |1 D# H1 V
  169. (defun board-show ()
    ! c4 J. Y; c- E: m9 b; U+ [
  170.   (with-board( x5 m* N" y8 m5 x/ c" E
  171.    (concat (buffer-string)$ s" }3 N5 c4 A: ~; C
  172.            (if (game-over-p)/ C( j& ~! f6 n& S# {* j
  173.               (format "共%d步,输入任意内容返回大厅" (session "step")): G* d7 R* l% F* Z: G' r  e
  174.             (format "第%d步" (1+ (session "step")))))))
    ) ]1 s6 y6 v/ }

  175. ; }2 ~  b( h3 z- y4 D  I2 N& j
  176. (defun board-position-parse (cmd)
    ; c* V) v' B! C
  177.   (if (= (length cmd) 2)
    # p& k8 T& L3 F) a
  178.       (list (string-to-int (substring cmd 0 1))
    : F- \$ M: S% W; S( Z8 y; A3 m
  179.             (string-to-int (substring cmd 1 2)))
    - G$ e( J. P; C/ h* ^& l: g* _
  180.     '(0 0)))
    6 [6 r" m2 I4 M9 D
  181. 6 e6 h5 i  V3 d0 n
  182. ;;; 游戏房间
    ' G. A( L8 K5 z1 N
  183. (defun game-room-init (cmd)
    7 n2 ?, v# }8 v8 z: W
  184.   (let* ((middle (string-to-int cmd))0 w1 j! j& x' l( C- @! t; K' E
  185.          (size (1- (* 2 middle))))- F8 K( w; ~8 t- N% `
  186.     (with-board. c3 u  m. ~/ H  q
  187.      (board-init size)
    ; ]2 Q! t3 O* A( g  J
  188.      (board-put middle middle)))
    6 U$ E. [" s. e6 g; A4 s6 A
  189.   'game-room)
    / @6 x! F# p; D, c9 E& x
  190. " y- L+ |. P) P  e8 G  |
  191. (def-room game-room
    # o" Y- ~7 {( s: \$ E
  192.   #'board-show
    ) p  v/ w7 c* d* o0 k
  193.   (t (lambda (cmd)2 c& G6 |* O+ [9 @
  194.          (with-board
    . F( v2 A: |& @3 E- |
  195.           (if (game-over-p)
    : [; h% D2 R* O* {
  196.               'living-room
    ' C" F% y& Z% v2 J4 n
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    7 h+ x" }& [5 i; F; Q& j
  198.               (when (board-contains-p y x)" J2 ]3 }9 ^+ H: \; X. P
  199.                 (board-toggle y x)
    $ @# i, R6 u& _
  200.                 (session "step" (1+ (session "step")))): e3 |6 u- L4 _3 `1 w
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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