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

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

  1. ! r, c1 b, T6 I
  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;">;; 定义新的游戏地图
    ; Q5 Y( f  M8 F9 i
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    : z, s7 O' ^! R4 ?5 y4 l" I2 y
  4.   'tutorial-room-0)                     ; 默认的入口
    8 P& A/ ?2 I3 ]- C. N

  5. ' h5 Q. B3 n- T$ m
  6. ;;; 游戏大厅
    6 c3 p- D+ h' F0 K8 n
  7. (def-room living-room
    - b; _. O- i+ I- u9 H6 {, M' X
  8.   ;; 进入该房间后的提示语- ^# j7 p( a, G  m, s8 Q
  9.   "1. 教程
    " N) x$ L+ q- X
  10. 2. 入门(3x3)# L4 L/ U4 E( X" M' C8 e' }
  11. 3. 初级(5x5)
    / G+ ^# M. e! p& j
  12. 4. 中级(7x7)5 _/ Q( v" a( _; l) q) ^
  13. 5. 高级(9x9)" {0 _9 ~* N2 {; q7 Q, h
  14. 0. 关于作者# z1 z0 \9 l. \9 R. t
  15. 请选择1-5开始新游戏:"  [- A5 c/ [9 ?6 J& O! j- }
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名! y6 a! {' @: }. D  f6 c8 z3 o
  17.   ("1" tutorial-room-0)9 R2 p" H, {* G* j! a
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    ; b. \. y' O  u4 S' z) p
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    3 Q5 [" N# q, w
  20.   (t living-room))                      ; 如果条件为t,为永真# I* y6 W5 X1 n5 R2 D2 [
  21. 8 W' `" a/ h3 ?
  22. ;;; 作者信息" ^; @1 }4 l- `4 l" P
  23. (def-room about-room
    ! p2 @( h) K4 d# i! }1 A
  24.   "作者:redraiment6 A/ j4 R1 }, {# u2 ]
  25. 微博:http://weibo.com/redraiment7 z; f% q" E1 P' A
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    . b0 |! T' m  w/ H* B, H
  27. 请输入任意数字返回游戏大厅。", x) d- C; U) g4 k6 c
  28.   (t living-room))9 M# V" O& V+ x" J' G( f2 @9 J
  29. 5 C9 t$ v$ L: e+ J, J5 d
  30. ;;; 教程2 d  Z0 y* V: |+ }. y. ?; X# d, R/ L
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    ) I7 ]8 l) N# X) h: m8 p3 _
  32.   "The number of tutorial rooms")
    * U6 p8 k" G* X- H, A

  33. , z; {! d% t( I' K' W
  34. ;;; 简化教程的定义' q- Q3 \$ j1 c% R" \' f- X
  35. (defun string-last-line (content)' C; ]+ q% ~/ r9 U
  36.   "多行内容组成的字符串中的最后一行"0 ~* G, O& D" @; u9 J  ~3 I! n
  37.   (with-temp-buffer1 K* \0 F4 X( S7 u1 \# G
  38.     (insert content)4 p7 C6 _; S+ O7 [  m$ R. ?3 _
  39.     (buffer-substring (line-beginning-position)
    / C4 p$ f! {" B5 g
  40.                       (point-max))))  g, m( \$ L5 P. b3 h5 a) i: A, u

  41. " Q. n0 b: b6 ^. i5 ?1 Z( f
  42. (defun def-tutorial-room (prompt)* B+ u* L& p1 v- x4 @$ s8 J1 P
  43.   "根据提示语自动生成教程房间。
    & N" g  u" ~. b5 `- ^- w' }
  44. ' X7 ]! R, e4 }
  45. 1. 提取最后一行作为问题;
    % K, U: G$ Y+ i/ y% S
  46. 2. 分析问题,获取期望用户输入的内容;; b6 K+ f8 R& |% u% Q
  47. 3. 定义教程房间和重复提问房间。"0 a5 A1 e/ x1 Z
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    ( k( w; o6 Y! b: ?
  49.          (repeat-room (concat room-name "-repeat"))
    1 V! t; x- o4 C) }7 G1 x9 Y+ W' R8 [
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    4 L0 v& Z6 l  c4 l0 T- ~
  51.          (question (string-last-line prompt))3 k2 x* N+ b: ?5 u9 |
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)  ?& W# e$ t2 |1 u3 k; u" T
  53.                      (match-string 1 question)))3 l5 I% t/ a# |/ O# O
  54.          (doors (if except
    5 E" V/ O& h' e$ z- b) d/ b
  55.                     `((,except ,(intern next-room))
    9 U5 h5 Y& a$ n+ G2 T
  56.                       ("q" living-room)' k% Q5 k1 X) J' b7 `4 E
  57.                       ("Q" living-room): {0 {/ H' Y# W) z& ~+ O
  58.                       (t ,(intern repeat-room)))/ O) [. ~' L8 ~# q4 E! ~/ ]
  59.                   '((t living-room)))))
    ( X" l* Z1 ^  {
  60.     (def-room-raw (intern room-name) prompt doors)
    ( X8 N8 E8 j$ `1 E; ~6 R9 L
  61.     (def-room-raw (intern repeat-room) question doors)))
    3 \% U8 M# F1 w: b5 v
  62. & B7 }( e4 ]1 P
  63. (defun def-tutorial (&rest prompts)
    / u  b7 j+ d# {
  64.   "批量生成教程房间。"+ ^1 U9 G8 j' O2 f, E; J
  65.   (dolist (prompt prompts)
    & I% K* u2 \" G- Y
  66.     (def-tutorial-room prompt)))
    ; J6 S9 L! a/ U
  67. ( u3 k$ ^1 q' Q
  68. (def-tutorial
    0 j9 U( `; C* m" Y# G9 H' i% o
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    . ^1 r6 O$ Z. Q- l/ h6 D3 m
  70. 1. 教程
    8 ?) Y& w; O/ b/ t  [; W
  71. 2. 入门(3x3)
    2 V, W! ]/ z$ g4 k* x: E3 p' k" O
  72. 3. 初级(5x5)
    + [( K. i9 \( ^3 h0 {
  73. 4. 中级(7x7)
    & e$ v* g; p' W5 P2 o$ b( x9 i
  74. 5. 高级(9x9)
    ' {% p8 N% ?2 ^6 N4 m* P2 q6 R
  75. 0. 关于作者; c6 {' f  D2 A) k
  76. 请选择1-5开始新游戏:5 a4 O8 Z& Z2 e$ }
  77. 您现在正在游戏大厅里。& ^! H, H) f0 P: p; J
  78. 请输入“2”进入入门级房间"
    . A# i. R: C  j, ~
  79.   "  ①②③+ y( L; O. {" A  D7 \7 d
  80. 1 ■ ; Q1 i8 S; C& i6 _
  81. 2■■■
    / x; \! G/ z) T" e
  82. 3 ■   Y3 c. A& A" x5 m
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    8 V3 \7 P) G5 k; b! x0 F
  84. 请输入“22”来关闭第2行第2列的窗户。". G! O7 \- d  U7 E& b( b
  85.   "  ①②③
      H/ c/ x# A% @4 B5 }( X
  86. 1   % ~) X* V4 u$ v
  87. 2   0 v% O% |' A1 ]5 F9 Z. K! ]
  88. 3   - l" W, q1 u1 h/ X& u
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。! M3 m' [# J9 }7 u+ \0 E4 T
  90. 请输入“11”,试着开启左上角的窗户。"
    ) `7 r& p( _) i9 ?7 N* _% ]
  91.   "  ①②③
    3 R- |/ z! Q- c: o. A, K. q$ i  y
  92. 1■■ 
    ! z5 J; K. c8 N6 h
  93. 2■  * A* ^. d, w2 _% |  G# @# ?
  94. 3   
    6 i. K& }  m! Z8 d4 R1 c1 {8 @, q
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    5 d5 P2 V+ i1 s7 [3 t/ V- @
  96. 请输入“13”开启右上角的窗户。"' ^; ?2 ?5 E2 z0 K! n- k; C
  97.   "  ①②③/ J' J) f% A) `! g) I3 V5 ]
  98. 1■ ■9 `# F/ F, Q; ~0 f" T
  99. 2■ ■
    " Z8 a9 i- C5 g, ?
  100. 3   
    % n; [- k$ \3 p
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    # ]& K2 }6 v; M8 {7 G7 ^% x0 j
  102. 请输入“31”开启左下角的窗户。"
    , N% t& m" Q/ D! W6 V: X$ o
  103.   "  ①②③
    : J% J3 ]) A6 _7 L& n: y
  104. 1■ ■
    ! A$ C8 t. g6 H7 P/ x
  105. 2  ■
    0 \) L' V# W% w( u
  106. 3■■ 
    ! S" e' d8 B; i. y
  107. 此时,总共有5扇窗户被开启了。2 a& L7 T7 [* T' o) l# ~  a
  108. 请输入“33”开启右下角的窗户。"" I; L3 |+ P1 q: @
  109.   "  ①②③: ~. P, S  }' \1 E" L" H: @- X
  110. 1■ ■
    9 f/ e& ~5 a* `$ y6 A1 ]+ ^
  111. 2   6 e2 j+ h& t1 h- K% n5 U. v
  112. 3■ ■
    2 H' V, }2 |  s; z
  113. 现在,只有四个角落的窗户被打开。% U, h% U( _' A- a. T. n. z! K
  114. 请输入“22”完成最后一击!"
    " U: O- k% n* f8 z
  115.   "  ①②③
    - k, T( q" n. p! d/ _) t
  116. 1■■■7 M9 E: Y& K) A3 V( s
  117. 2■■■
    2 v( z6 J- V* _! g0 b* g5 d
  118. 3■■■% X* W! X" E1 A; p& \3 X
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    3 A$ e: F0 P& D# F: M
  120. 6 ]/ S' u- l' @) @6 W, T
  121. ;;; 棋盘& `* q' M1 N/ P
  122. (defconst *wechat-5x5-white-chess* 12288
    # E% }6 t" y9 R
  123.   " ")8 m0 p; Z; l* x' I4 J

  124. + y, X$ b- i# W
  125. (defconst *wechat-5x5-black-chess* 9632
    & z% i; T/ D6 [8 E% }! U1 a! n
  126.   "■")3 M# z( C# z3 i, w1 {# N1 Y

  127. $ t+ V9 ?; _- G8 l# {  [! m
  128. (defmacro with-board (&rest body)
    # K# u; f5 r5 ~6 h$ `- d! p
  129.   `(with-temp-buffer
    , m+ q, L/ p9 ~5 G2 S' u! Y- a& L
  130.      (unwind-protect
    / Z) s, b/ [7 m) N. W9 l. E
  131.          (progn
    : s5 ~$ Q; ?, v" C; A
  132.            (if (session "board")& g9 K  L0 i- P. x1 W
  133.                (insert (session "board")))
    8 I8 T6 u) W# f1 [: n. ?. ~: k, w
  134.            ,@body)! w( p7 H- Y% N7 t. }
  135.        (session "board" (buffer-string)))))
    / n6 C' J; F+ C
  136. - i' y1 J6 j) y0 F9 X
  137. (defun board-init (size)
    3 K5 q- d* \" m" ]* K% m5 F4 b
  138.   (session "size" size)
    3 N5 U% Q) \, \! `1 }  r3 A; V3 I
  139.   (session "step" 0)
    0 w1 J" k5 S" ]5 E
  140.   (erase-buffer)' F- d3 B$ n$ w# G$ K5 E
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    : W  c, {% J1 p- z' W) Q% g
  142.   (dotimes (row size)8 ~. [: z; Q' b# V  e6 z3 f
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))1 F3 h6 X4 V6 g+ [" o# |
  144. : d" q  k1 R, u0 \
  145. (defun board-contains-p (y x)9 Z0 Q# ~+ J2 f
  146.   (let ((size (session "size")))
    0 Z4 O* ~8 z1 u
  147.     (and (<= 1 y) (<= y size)) X; w6 s& [' [7 `4 C7 \) ]: {
  148.          (<= 1 x) (<= x size))))& |3 R: |/ H' f
  149. 9 z( U& C; v% \6 y: `" k
  150. (defun board-toggle (y x)
    $ l3 T9 N% d9 ]+ A: @
  151.   (when (board-contains-p y x)" S. m# R+ ~- b. Y! D& h
  152.     (goto-line (1+ y)). i9 _3 j( a! p1 D7 Z" y) Z
  153.     (beginning-of-line)
    ! |9 A4 I. V4 {; [& J8 ~1 H& ~! Z
  154.     (forward-char x)+ W( x# H1 t: V/ w$ s4 ?
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))2 p; y' X+ w  P5 ]
  156.                 *wechat-5x5-black-chess** z, e- x/ W0 G  q* d
  157.               *wechat-5x5-white-chess*))- Y) K9 M/ d) C* y  G
  158.     (delete-char 1))); x( V/ M9 o7 q& `
  159. ) {; E! t3 Z  H9 O9 ^
  160. (defun board-put (y x)
    " K, t  a$ ]3 J/ a. C
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))3 Q8 w2 L: O; U) `! s
  162.     (board-toggle (+ y (first dir))4 P9 B' W. [: }9 A
  163.                   (+ x (second dir)))))2 j1 F% R3 T" A) W3 b

  164. 6 [; a. a, a4 j) [) |
  165. (defun game-over-p ()( u! B, O: ^) C6 M! t3 Q
  166.   (beginning-of-buffer)
    2 h' N/ a8 f5 u0 h5 E" M2 z
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    " r! u  e8 V. @( D+ a* O

  168. 9 W3 X* i5 c9 s: X
  169. (defun board-show ()
    ! ~" m$ C+ e% w* s2 E( ]
  170.   (with-board+ Q+ `' g7 [/ K
  171.    (concat (buffer-string)) j* Q1 K+ D  P% A
  172.            (if (game-over-p)
    8 @0 q. H" A$ U( R( _2 M
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))1 J6 Z/ U) u0 F, _9 O
  174.             (format "第%d步" (1+ (session "step")))))))
    ; G/ f+ W0 [: K3 a. R  @

  175. 4 s# ~3 s2 ~4 H* E3 m
  176. (defun board-position-parse (cmd)& L8 W) U- J6 f# l/ R
  177.   (if (= (length cmd) 2)  O& U4 e4 [5 s$ K9 i& W0 @
  178.       (list (string-to-int (substring cmd 0 1))
    " t7 n; |+ y9 _$ r% H% n9 U
  179.             (string-to-int (substring cmd 1 2)))5 R. v1 I, l$ \0 T. L
  180.     '(0 0)))
    0 I0 h; c9 j9 R' f1 U3 [5 G8 e

  181. ! w2 k4 [1 y7 F8 W
  182. ;;; 游戏房间: D4 t6 B# q. x2 ?. h
  183. (defun game-room-init (cmd)* q* v7 c7 |8 p" @
  184.   (let* ((middle (string-to-int cmd))
    * r% j2 t- W! ~
  185.          (size (1- (* 2 middle)))). A' R: n3 `0 ^5 N# U) L9 P+ P
  186.     (with-board5 o  x! Z* t9 F; b9 M  J9 |
  187.      (board-init size)0 q6 T: M7 v; M6 g
  188.      (board-put middle middle)))9 n" F2 @* l& \) N4 L$ Q
  189.   'game-room)
    1 o( o3 s! X/ B9 P8 i; K$ I+ V8 i% d% ~

  190. 0 |' y: A. A5 a  E0 A
  191. (def-room game-room* R! n1 ^- S2 n0 Q
  192.   #'board-show1 S6 k- E0 K7 {$ ^# ]% {
  193.   (t (lambda (cmd)
    / S& y8 C# x+ _: r: s
  194.          (with-board( D. {. T  ]" m+ p% B  s! `2 @
  195.           (if (game-over-p)
    # L. X. w9 Z2 q6 M1 U
  196.               'living-room8 {  q9 ~! }+ E  P; U2 c' ~, c
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    ! K; j! E9 C6 J0 K. h
  198.               (when (board-contains-p y x)
    % X! a2 v7 D5 |. c
  199.                 (board-toggle y x)
    ) z1 J3 U* K0 H) s) ~) K# Y
  200.                 (session "step" (1+ (session "step"))))
    4 B2 ?; u0 p# k4 `' z$ u
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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