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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
: Q' ]8 s7 u" r+ S- q% f  X+ y' G) @0 ^借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. : G+ C. |& C5 k# w! t: ]* C8 P+ 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;">;; 定义新的游戏地图* n+ ?' ~+ G0 y. S) X( d
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL+ g1 U7 G3 N/ f: h- o. }4 D
  4.   'tutorial-room-0)                     ; 默认的入口. `+ P9 a0 |9 V# j; e" X3 b

  5. + i- t; s3 I! A9 l, ]' {, }
  6. ;;; 游戏大厅
    0 ?" T$ k' O3 N5 D2 L) y
  7. (def-room living-room
    / p% W, s' n2 ^
  8.   ;; 进入该房间后的提示语* b% K% E4 |. H6 {1 W
  9.   "1. 教程4 X% T2 o. L' h9 [  F4 @# ~7 c
  10. 2. 入门(3x3)
    8 q) g8 o, u  E, u; y6 L7 d
  11. 3. 初级(5x5)
    . ]5 i: H4 h- L8 w
  12. 4. 中级(7x7)
    6 X9 m7 {" F" O5 w
  13. 5. 高级(9x9)
    5 z7 B4 n9 D6 k* Z
  14. 0. 关于作者" U/ v/ H' l3 p  {
  15. 请选择1-5开始新游戏:"! b6 P4 F5 D$ i7 Q4 Y6 c2 U9 a+ |$ q
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名9 Q5 q$ k3 M  v7 `8 W8 M
  17.   ("1" tutorial-room-0)
    / ]0 z( |+ `" u; u- [/ C
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    ( L; N2 @5 b/ r5 [# ^# m& E1 I
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    5 r4 p- ^* ]: K1 Y' w, _
  20.   (t living-room))                      ; 如果条件为t,为永真3 V3 p6 {8 ^' F/ u( x

  21. " [& d8 H  N* \, Q0 Z: w* F
  22. ;;; 作者信息
    & L0 k+ B2 J6 X9 F1 C) n3 I
  23. (def-room about-room# x5 D3 b; g* V
  24.   "作者:redraiment
    ' N. m; |/ r: B) ^5 r
  25. 微博:http://weibo.com/redraiment. y( ]& R- [9 l7 Z( z
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    8 i# W* ?* y7 ^# s9 t# ^
  27. 请输入任意数字返回游戏大厅。"& x6 ~3 F4 z8 p
  28.   (t living-room))0 F& N* J1 C" t+ E# u3 p

  29. 5 |! c; B( V3 J
  30. ;;; 教程( X: d+ A# N; q$ Z3 _! N& g
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    : O# q1 Q. ~5 V( [6 e) k# k% c" M
  32.   "The number of tutorial rooms")
    / A% M& p% M' b( D9 f0 v1 {+ O

  33. 8 m/ }1 Q6 u- E
  34. ;;; 简化教程的定义
    4 H1 z7 X, S% t( z2 L; t# f3 K8 g# Y( h
  35. (defun string-last-line (content)
    0 ~) U9 W* s0 K4 L2 D
  36.   "多行内容组成的字符串中的最后一行"" }/ v; [( s" T, _1 k, v
  37.   (with-temp-buffer
    ) h; j7 L! S/ R* _
  38.     (insert content)
    " J+ f9 F8 e# X) F. u
  39.     (buffer-substring (line-beginning-position)% U' Q- j8 u* k6 Z/ L7 m
  40.                       (point-max))))
    : ]; F) H* m- U; @: g8 ?4 m6 S
  41. 7 p" u6 f4 n. }6 k1 t
  42. (defun def-tutorial-room (prompt)
    7 t& I  V# B9 q9 c" ~, t, s
  43.   "根据提示语自动生成教程房间。2 Q* h1 H" d8 _# u1 K9 Z( C' n" ~" [
  44.   x& }0 y7 |9 o. T
  45. 1. 提取最后一行作为问题;
    / q! @- c8 C3 W" y& @- d+ e: c
  46. 2. 分析问题,获取期望用户输入的内容;" G5 {" c( z/ A, H0 S! P
  47. 3. 定义教程房间和重复提问房间。"
    * A* Z3 @" }% f2 Y& S
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))% k7 k% a" P! C) [6 J' A0 H1 J& r
  49.          (repeat-room (concat room-name "-repeat"))( k* S4 p$ G* C2 J# B: v4 U8 y
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    " M9 @# X; r- U
  51.          (question (string-last-line prompt))
    4 J# ^. G5 o# d& x, P5 {
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)% a/ a' O. r% U, Q
  53.                      (match-string 1 question)))
    7 T) p+ z' R' v7 l8 r3 N
  54.          (doors (if except
    0 [5 f/ h/ M7 b9 d
  55.                     `((,except ,(intern next-room))
    7 w/ }0 f- Z& K1 x! G
  56.                       ("q" living-room)5 d" o7 w9 p5 j8 T* F7 E9 W! z& U4 _3 Q
  57.                       ("Q" living-room)4 }0 ~1 V+ S/ j* I: `
  58.                       (t ,(intern repeat-room)))3 x/ W6 J2 [7 d$ z% _" Z
  59.                   '((t living-room)))))
    : A5 k: V/ b! }' j4 n  ^9 ]2 ^
  60.     (def-room-raw (intern room-name) prompt doors)# E0 i) e0 N9 s1 ]
  61.     (def-room-raw (intern repeat-room) question doors))); f- y8 V% u  n* e  a
  62. ! K0 _5 E* B/ P; i1 j  J) ?
  63. (defun def-tutorial (&rest prompts)3 `1 `; c) X) N4 I$ a! m4 m- @5 ?  r; t
  64.   "批量生成教程房间。"$ C. u" N7 K: o6 f
  65.   (dolist (prompt prompts)
    2 G: d( V7 U0 Z
  66.     (def-tutorial-room prompt)))
    - u; t  S' a3 {$ c8 \5 Z

  67. 5 y8 L: J3 m/ f9 k
  68. (def-tutorial
    $ l3 I8 u3 q: D& s: Z: f
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。. p( s, `: `2 l
  70. 1. 教程
    ; q( F- K& H" G3 K+ `1 _) X
  71. 2. 入门(3x3)7 r$ o" H5 u3 K. @
  72. 3. 初级(5x5)( D; \7 E- u' Q# l8 D
  73. 4. 中级(7x7)0 L. Z* T6 S  W2 t
  74. 5. 高级(9x9)
    5 D' B' C! a/ i/ J0 }
  75. 0. 关于作者: [  U" O1 @' ^- A1 s, M
  76. 请选择1-5开始新游戏:
    : `( c+ X. r5 ]4 Q3 U! `1 m
  77. 您现在正在游戏大厅里。  _9 T$ c5 n; _" D  U
  78. 请输入“2”进入入门级房间", B  ^1 f) }) j; M: i( w
  79.   "  ①②③# H  i8 A2 m6 q& r+ v$ y
  80. 1 ■ " x8 q* {* u% K% I5 z+ V/ r
  81. 2■■■
    $ t/ i6 E# g: Q4 I/ r2 N
  82. 3 ■ 
    - v& `8 q2 Y% |" }, |2 U
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    ( b2 P1 e# X4 ^4 j9 {% W
  84. 请输入“22”来关闭第2行第2列的窗户。"
    1 E/ j( ]3 ~- c* T
  85.   "  ①②③
    $ r! D+ p; f& \: ]1 l* u
  86. 1   
    # d# t7 w5 l8 I/ D
  87. 2   " U6 w6 T5 G7 ]
  88. 3   
    1 m* D6 Q; }9 b4 d8 V9 P
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    ' J8 Q7 l8 ]5 }1 Q4 V+ A0 y. L
  90. 请输入“11”,试着开启左上角的窗户。"; G, i6 v2 k( z3 t; s* ^
  91.   "  ①②③
    8 s. w  f2 N4 Z; m! c4 t8 q! T
  92. 1■■ 
    * L4 M8 g. K& |- ~" `
  93. 2■  8 e8 Q) |1 Z) m
  94. 3     @7 n0 Y. o' O- n  C3 |) r
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    + i# u5 q6 \- v( x
  96. 请输入“13”开启右上角的窗户。"8 C3 x2 f, y* [  b" M
  97.   "  ①②③2 [* x& w  h- ~, }
  98. 1■ ■7 Z8 H( r3 O  y* ]/ ?/ v4 G: G
  99. 2■ ■7 o, p+ E2 o' L+ H& c" [. c1 a1 y
  100. 3   
    : Z* Q5 Q9 X! r
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。8 s# b: _5 N' E
  102. 请输入“31”开启左下角的窗户。"
    2 e( j5 a# O3 E4 a- @
  103.   "  ①②③& w/ r& V# {3 R( H) O4 F/ M
  104. 1■ ■( X8 Q6 F7 a+ x  |" d; n) z# P
  105. 2  ■5 K  @- k4 a2 {, N$ X
  106. 3■■ 6 C5 ]7 i) j, G" k+ q
  107. 此时,总共有5扇窗户被开启了。. O0 [+ `& F) M4 e/ Z
  108. 请输入“33”开启右下角的窗户。"2 e3 t" c2 Q; q2 J
  109.   "  ①②③
    $ l6 }( r1 z  `" n! L
  110. 1■ ■
    5 {+ g! T0 v* Q9 s" J" t! c5 C6 B
  111. 2   8 A0 N/ J$ K& O3 V
  112. 3■ ■0 e1 n- `  T% Z+ X
  113. 现在,只有四个角落的窗户被打开。6 ?. Q- i5 I' j; s- R0 b) Q6 q: _
  114. 请输入“22”完成最后一击!"7 B4 J- |0 M; z$ `  M: r
  115.   "  ①②③9 b+ Y1 B% V& Y) z5 F& T; k8 |
  116. 1■■■
    $ ~: v7 Y2 ^& d5 O( ~$ Y
  117. 2■■■$ M) e  T6 Y  M9 q! d/ S  I% i
  118. 3■■■" ]' y4 w# o( Z' q( |4 s0 v$ W
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")7 j7 G: f7 D( E5 Q8 N2 ]& ]- ?& p
  120. 1 Y  q% P! T4 E( a- \+ v3 D# w7 Z1 l
  121. ;;; 棋盘
    / j/ d: E9 [% p5 r8 r; M. G
  122. (defconst *wechat-5x5-white-chess* 12288
    % ^$ u+ m6 _- m5 B; a
  123.   " ")
    . F1 n' e. \8 V1 E; |7 B

  124. / I8 L- ~: c7 A) d! W: e7 j0 C
  125. (defconst *wechat-5x5-black-chess* 96325 v0 V- F! ?- S( d2 {7 L/ p" Y1 I3 b1 u
  126.   "■")- o$ g/ c4 i9 n$ N" V& t( x) [3 X2 W
  127. " z% k1 l* ?. f( s; }9 k. Y
  128. (defmacro with-board (&rest body)7 _8 Z* b1 v; Z. Z* E$ x
  129.   `(with-temp-buffer
    & h  o8 V+ }8 A. z- R( d
  130.      (unwind-protect7 X0 k5 t* X2 ~7 }7 }
  131.          (progn9 T8 u. Y2 X' [
  132.            (if (session "board")' J" w$ t" l0 y5 J( x1 ?
  133.                (insert (session "board")))
    $ p2 Y1 ^0 Q, q6 b8 z
  134.            ,@body)
    3 u/ u% _; h8 C  k
  135.        (session "board" (buffer-string)))))
    9 [+ [) ~* n& ?. Q0 y, J

  136. + `3 z" O1 g, C0 T8 J9 F
  137. (defun board-init (size)" \* A) m4 e9 D, O3 E: Y
  138.   (session "size" size)
    ' b% P2 R! s% M) c
  139.   (session "step" 0)' |- O7 m5 A, n$ `; w6 p
  140.   (erase-buffer)4 X) C! E" c& \5 [
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))2 w. S2 q2 ]7 e. \
  142.   (dotimes (row size)% j+ v, o) D3 }4 v- |" k
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    , r8 @% T* [3 s+ a! {$ h6 m
  144. & V( [: q7 O" [6 I+ F
  145. (defun board-contains-p (y x)
    * |" C; I- h6 P$ ]6 B- B
  146.   (let ((size (session "size")))! x# D4 ~/ }% K6 @% `9 l" a7 y8 X
  147.     (and (<= 1 y) (<= y size)
    + z9 }- [) F2 Z' J% ]7 i
  148.          (<= 1 x) (<= x size))))6 M2 ]# i6 J% [( i

  149.   l  S" D; D1 ^6 N# A. \2 M% z; j8 I
  150. (defun board-toggle (y x)8 ~5 y5 v0 i$ z3 P( I
  151.   (when (board-contains-p y x)! c% b" C& a- l! f; E' _0 }1 \8 m
  152.     (goto-line (1+ y))7 w/ ?$ e. j3 c; T0 T; Y
  153.     (beginning-of-line)# a& K" g1 k6 l$ F: [3 H8 z: C
  154.     (forward-char x)
    / m8 A5 Z9 k& ]
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))/ B4 S6 ]) b' b/ \
  156.                 *wechat-5x5-black-chess*" ]. |( A  p' v* j8 O( i0 Y9 m
  157.               *wechat-5x5-white-chess*))
    / Q' L! ], G. A8 J2 y2 W1 c' a
  158.     (delete-char 1)))9 {& H) c- W. I% K/ k2 R6 f

  159. 3 ~/ p: Y' \, y/ ~
  160. (defun board-put (y x)6 L/ q) t0 a2 w# V  M7 e3 K
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    : g& U1 W, J+ x  m  p$ u
  162.     (board-toggle (+ y (first dir))* J% f2 M4 c2 T, k4 Y0 V
  163.                   (+ x (second dir)))))
    / X0 x2 U: S' R

  164. ( F9 l$ h- n- X" `- B2 r4 k
  165. (defun game-over-p ()0 V$ j" I6 l* r% c) p' k
  166.   (beginning-of-buffer)
    8 Q' x2 M" E7 A/ \! E
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    , t8 v; L5 U+ P5 P1 ^. ?/ Y1 c8 t

  168. : l8 ]- h8 j2 G# k* ?
  169. (defun board-show ()
    ' F  ^8 @; M0 l
  170.   (with-board  \. Q9 V" m, i5 K# n  l9 B. y
  171.    (concat (buffer-string)
    5 S* q' z( ?) q# U
  172.            (if (game-over-p)
    8 @( O" Z, ]8 T: U
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))! X- @+ y5 T% \, o" A, F
  174.             (format "第%d步" (1+ (session "step"))))))). i+ E& j% U! s7 s" O7 O

  175. " S. w1 ~5 R0 L: G$ C
  176. (defun board-position-parse (cmd)6 f: u9 W* P; A8 |. G
  177.   (if (= (length cmd) 2)
    $ X" E4 O( t  L: d; p7 S& K5 Z# k
  178.       (list (string-to-int (substring cmd 0 1))- J6 G# q" L1 j. n. [! W% v
  179.             (string-to-int (substring cmd 1 2)))' Q/ t, U, n+ r! p1 P
  180.     '(0 0)))
    ! [7 i* I6 w4 w5 S

  181. 6 S# ]1 L+ c  c$ U
  182. ;;; 游戏房间
    5 Q: K$ y( g0 u! r' \
  183. (defun game-room-init (cmd)$ ~* g) E! j, ?1 ]
  184.   (let* ((middle (string-to-int cmd))! ~5 z1 S: b* F) ]' k
  185.          (size (1- (* 2 middle))))7 u) v. P8 B- K9 F" Q! M, L: Z
  186.     (with-board
    # y7 p1 r3 Q3 C; N# m- b, F
  187.      (board-init size)4 G. ^8 \. O5 x  w, _. E
  188.      (board-put middle middle)))
    , g3 S; k0 I- y1 u2 M* W
  189.   'game-room)
    9 t& E) r3 W- l' H- W
  190. 9 J6 L4 C4 l4 S9 I. o& ~4 O
  191. (def-room game-room8 J, n% e. ?9 V. J* m' T' w
  192.   #'board-show& J4 `9 \4 m% H' i% D
  193.   (t (lambda (cmd)
    + ?3 D( ~. L+ ?7 u2 Q4 Y
  194.          (with-board
    - T/ b) i# q% C: Z6 `
  195.           (if (game-over-p)
    5 m3 C- u6 H9 U9 B
  196.               'living-room
    , E3 M; d  E5 }; K
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    - i8 F: |# Q5 I  l
  198.               (when (board-contains-p y x)4 I3 U/ p# t. e
  199.                 (board-toggle y x); o. }- x2 X, Y" |1 U/ s. G
  200.                 (session "step" (1+ (session "step"))))
      J4 I9 R* Y* {% K. `3 ~1 N
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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