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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
$ b  j! c+ J3 q4 u* v" s* x借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1.   _# q" a" z+ o9 Z  e' R
  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;">;; 定义新的游戏地图
    6 W' i1 C+ p! @$ G  G! p
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL: p' D' m0 C( a: k5 W
  4.   'tutorial-room-0)                     ; 默认的入口
    3 x: W% P2 }: q3 u, C
  5. / `( x3 L  J5 w) r1 {
  6. ;;; 游戏大厅
    % p! q2 X$ y3 e4 S. _+ H
  7. (def-room living-room
    ( U) K/ c. K' ~9 I) e
  8.   ;; 进入该房间后的提示语
    & U8 L9 G0 N3 [3 u1 b( c0 L
  9.   "1. 教程  s! I7 a& @, T2 e0 P) R# `
  10. 2. 入门(3x3)
    ' k1 {  m1 Q- q3 u/ ]
  11. 3. 初级(5x5)3 x! K' h, X! N
  12. 4. 中级(7x7)8 F6 x+ Z# O' p: d& k) s
  13. 5. 高级(9x9)
    4 T5 d0 d* m2 v
  14. 0. 关于作者5 z: u; l2 H8 M4 i: ~. g# |
  15. 请选择1-5开始新游戏:"
    % P( [# a+ P) z* I5 b) W, Z1 `
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名, G0 W' j' Z# @2 n- z' Z
  17.   ("1" tutorial-room-0)- }. s  D, i6 l7 w8 i' T
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    9 j$ s5 K$ R# b% O# ~4 M
  19.                                         ; 相应的返回也可以为函数,动态返回房间名% q7 Z* M& w) ]! B, D/ W
  20.   (t living-room))                      ; 如果条件为t,为永真  Q. x7 {$ U' H! t; y: L

  21. ; G; b0 g+ a/ C% y2 g- I
  22. ;;; 作者信息
    # c+ f4 H& U0 l8 S  `, ]
  23. (def-room about-room; v6 x3 j+ A' ]8 k: \
  24.   "作者:redraiment/ Q. I, w6 U: Y0 [# t( J1 ?
  25. 微博:http://weibo.com/redraiment4 g6 J& o+ z9 n; [$ a2 k
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    ' `* o% X+ v) E
  27. 请输入任意数字返回游戏大厅。"* h6 D4 [3 V5 t+ T1 P
  28.   (t living-room))
    ' N5 ^( ?" _+ W" n( j: `9 B9 a7 ?
  29. ( C- t8 D8 Q+ Q4 ]) s* V
  30. ;;; 教程9 u4 D0 _& k3 c; y2 g* {& ^) u6 U1 F
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    % g% v$ O) E! r+ |" l3 A
  32.   "The number of tutorial rooms")
    9 t2 w; V" _" |, U6 s9 O: {+ Z% F; S
  33. & `2 m/ _4 D' J4 W
  34. ;;; 简化教程的定义
    7 \& k) U" q# a& \" Y
  35. (defun string-last-line (content)
    # u+ M* B2 a: b: s, d
  36.   "多行内容组成的字符串中的最后一行"
    * u" C! R  C" O' R' _
  37.   (with-temp-buffer
    / u, B8 N. l7 l5 U
  38.     (insert content)
    8 D1 s' J+ O; C; o0 q
  39.     (buffer-substring (line-beginning-position)' y  i1 F0 A/ e  m; `5 p
  40.                       (point-max))))
    ; y) q. l  F3 K: i( n

  41. % g4 i) {* A  o! q' J; R  b
  42. (defun def-tutorial-room (prompt)
    1 a, ]2 V! s( C5 Q/ E
  43.   "根据提示语自动生成教程房间。
    + ]0 @" e) J5 H. {

  44. 6 S3 f3 {. `9 _6 n: E
  45. 1. 提取最后一行作为问题;0 j4 q0 v  S& N! P" ~& X
  46. 2. 分析问题,获取期望用户输入的内容;
    ) y8 y& l% V4 L: G6 t6 ^' q
  47. 3. 定义教程房间和重复提问房间。"; {+ K2 G" L1 g8 z5 Q' o' q  v$ m* _% V
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
      g1 f+ l( E/ u; F
  49.          (repeat-room (concat room-name "-repeat"))
    # B' H8 @3 N4 h: n! l$ B
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))( i3 x$ z! B# ]
  51.          (question (string-last-line prompt))
    * ~& h1 \: U* x* W8 w# t
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)) l; t2 w7 J) a1 e2 q
  53.                      (match-string 1 question)))
    * S: Y9 c  ~7 H2 P3 v7 b0 |* ~
  54.          (doors (if except/ Q2 ^& \7 c' D% J' e  d% r  R& \
  55.                     `((,except ,(intern next-room))
    / ~# `) C2 m1 h
  56.                       ("q" living-room)
    : k) H( f  ^1 l) J
  57.                       ("Q" living-room)9 K7 \4 A/ r! v" T& w! J$ J
  58.                       (t ,(intern repeat-room)))
    8 P3 f* E( T: y( z& M3 H/ ]
  59.                   '((t living-room)))))& t. H9 I& w& p6 n4 q4 M
  60.     (def-room-raw (intern room-name) prompt doors)" r. `) w3 I% S+ i( n
  61.     (def-room-raw (intern repeat-room) question doors)))
    8 b. [2 y+ _8 Q9 L' N7 ~' i

  62. 7 o7 W% b7 h$ R+ h( r% }; A
  63. (defun def-tutorial (&rest prompts). q& L4 o! K5 P/ c% ~
  64.   "批量生成教程房间。"+ V6 Q7 l) I" [" t! ]4 W
  65.   (dolist (prompt prompts)" N9 o( S, T6 T$ F- q* N
  66.     (def-tutorial-room prompt)))
    2 T: Q9 N8 q5 e/ m$ F
  67.   z- [: i5 h: }1 l
  68. (def-tutorial8 X8 Q7 x- r7 J8 P) T5 S
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。+ y0 H+ [& |# g7 S0 H
  70. 1. 教程! P5 X* N1 b' ], |3 d" _
  71. 2. 入门(3x3)+ W4 d8 C' V4 G: A* M
  72. 3. 初级(5x5)6 t5 @7 h) l* C
  73. 4. 中级(7x7)
    5 J  D+ r0 j  U& g" t
  74. 5. 高级(9x9)3 b0 f* S, @+ z
  75. 0. 关于作者
    ! Y9 {6 m" x9 k0 m; q, j! g3 R% t
  76. 请选择1-5开始新游戏:; V6 x( e& b& q5 Y! K" I# ^
  77. 您现在正在游戏大厅里。! f5 l5 ?9 l0 E9 a/ l: x4 ^
  78. 请输入“2”进入入门级房间"( j4 V1 x  w2 `' z- I
  79.   "  ①②③8 E+ Y) n5 f, R' G' p* M6 w
  80. 1 ■   R* H6 T2 C% K2 T) l  I. ?" W
  81. 2■■■
    ' n/ N6 _/ V  }) o* @7 i1 r
  82. 3 ■   r/ }* r8 D1 H, P5 s! r
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    ) l$ K$ c' k0 k# E  \2 M
  84. 请输入“22”来关闭第2行第2列的窗户。"( R& a3 {$ G) {+ z) `) B( W. }
  85.   "  ①②③
    3 y. ~* |2 q5 ]7 B( @/ ^! b0 g
  86. 1   ) L; E( G- M4 C% m( o0 G
  87. 2   ( x. A0 Q1 E8 n1 T) x4 z( A
  88. 3   
    $ L/ C) B" w' Q4 p+ }2 R
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。' e/ R$ k6 f4 t- z
  90. 请输入“11”,试着开启左上角的窗户。"
    , e0 o9 x0 s* x2 X# c9 k
  91.   "  ①②③
    ( z* d' U. X2 Q2 n
  92. 1■■ 
    8 E& K" b3 J% _. j
  93. 2■  
    . ^' O1 P9 e, k4 O. x" M
  94. 3     f4 V( l! m. `+ L; b  }9 X
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。8 X' ^  F6 H8 P" M8 h& T0 ]1 q: y" s
  96. 请输入“13”开启右上角的窗户。"1 b- J$ z5 ]7 S7 S3 F
  97.   "  ①②③: B: u7 W8 q/ K
  98. 1■ ■
    7 s- P/ d) j! ^0 S) \! T
  99. 2■ ■
    , y. l2 F6 A2 e5 u' b. e% i
  100. 3   0 S* Z# p$ b9 Y0 ?( z( ~# U2 z
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。4 Y3 A! D* W) D# ?; ~
  102. 请输入“31”开启左下角的窗户。"
    $ C1 [6 b- ?9 {8 x1 e7 I
  103.   "  ①②③* f3 u+ G# k% q# r) U
  104. 1■ ■. W) `. X$ C, P" p. N$ g1 N6 [
  105. 2  ■- X( [) n, t, f0 Y
  106. 3■■ 8 T) o  R) v0 e6 A
  107. 此时,总共有5扇窗户被开启了。; g$ G, y3 D8 _0 `( K
  108. 请输入“33”开启右下角的窗户。"
    ) _/ e# `  L* ~8 [, @1 e8 U# n
  109.   "  ①②③0 q9 s6 P8 R3 h& B" y
  110. 1■ ■- q7 v6 S) Q2 u: D1 D
  111. 2   . o; T/ x9 U1 f% g. S1 T
  112. 3■ ■9 j: q2 r1 G7 g' Y
  113. 现在,只有四个角落的窗户被打开。
    2 r" I; Y, n' u+ a- B
  114. 请输入“22”完成最后一击!"3 q9 P# y4 p7 h- D6 ?# A
  115.   "  ①②③! _- d0 V$ p' ^) e# k. @
  116. 1■■■. M3 Q' g7 v% ?4 Q! i3 [
  117. 2■■■4 g' t  h% m4 ~' Z) l
  118. 3■■■
    4 b" L9 ]8 [$ W5 [
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    2 l& n! E1 V/ ~0 l

  120. % B! y3 \3 i3 y; V6 R4 W7 F$ Z
  121. ;;; 棋盘+ @* `1 e1 j  a# h
  122. (defconst *wechat-5x5-white-chess* 122885 M! U+ [& R! N# j
  123.   " ")8 u/ A0 ?6 C! ~7 N
  124. + B( t* i. r, R8 A- J6 u
  125. (defconst *wechat-5x5-black-chess* 9632' H- O- v' S  q$ ^1 }* X+ X
  126.   "■")- [. q( e" B: D. h8 j. i
  127. + ~- y6 G: l' K8 V
  128. (defmacro with-board (&rest body)
    & t4 j: j" ~7 |
  129.   `(with-temp-buffer7 b+ m- i6 M% m: p- N
  130.      (unwind-protect
    9 u- u* ~" ^8 p2 j% ^7 @
  131.          (progn# @) W2 O, m2 E7 b) P; J$ P
  132.            (if (session "board")( \8 i5 Q5 t* J8 C) D, Q3 ]
  133.                (insert (session "board")))
    - r3 S4 `# h* V- G* \2 v
  134.            ,@body)
    ' |$ |2 E: d/ q% R1 c3 g
  135.        (session "board" (buffer-string)))))# `0 D+ m4 I: |
  136. 6 ^- X. i* m% H( i  C. o
  137. (defun board-init (size)+ ]7 ^0 Z# w) M, S$ U# q
  138.   (session "size" size)) Q5 s2 w3 S8 i) |# s: Z
  139.   (session "step" 0)
    8 F/ X0 l* q( B* a
  140.   (erase-buffer)
    - ^" z1 D/ F# K1 v
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨")); N9 ?* o# K. U- v; D0 S
  142.   (dotimes (row size)$ M" o, h( |2 t! Z% V8 E
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))  B: L2 m( I+ s: `! r. m& m* K

  144. % H; n! z6 g" m* Q' m8 _* ?
  145. (defun board-contains-p (y x)& g$ E3 s$ N6 h/ ]
  146.   (let ((size (session "size")))
    - |4 G" k0 m  w# F: n8 x
  147.     (and (<= 1 y) (<= y size)
    : i6 E- m4 q3 [1 ~# T
  148.          (<= 1 x) (<= x size))))  [6 x% f# a& E* \4 }

  149. 9 ~6 e/ n3 z2 L4 v0 R
  150. (defun board-toggle (y x), i4 w* q! N1 U3 Y0 E8 J9 N
  151.   (when (board-contains-p y x)
    ) e6 L; `5 i. c7 j
  152.     (goto-line (1+ y))( T5 P, d% N- n  t& x3 h+ ?; A1 b
  153.     (beginning-of-line)
    / C4 i8 r, S/ m2 |5 k1 Z
  154.     (forward-char x)
    , V6 q- G+ y1 I4 C+ n
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    $ R: K3 j7 X6 [3 s7 ~5 N& Y
  156.                 *wechat-5x5-black-chess*
    6 G: D4 Z& ?% S. d! t( Z+ N$ r0 F
  157.               *wechat-5x5-white-chess*))2 `! G: j( e7 Q- x) _
  158.     (delete-char 1)))
    : {0 f+ C- ?- q- B
  159. 5 e. B  `9 K, F- x
  160. (defun board-put (y x)1 G0 V0 r* I) }0 E. Y
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    ! b, s3 F* e; }$ P
  162.     (board-toggle (+ y (first dir))5 v* o6 v" X) N' f- M! q
  163.                   (+ x (second dir)))))
    4 R6 W( u. l, M

  164. ' I+ U# C7 \3 {) x! B8 B- P- ~: C) }5 L
  165. (defun game-over-p ()! y3 D" J; K- l- ~+ g4 h; |
  166.   (beginning-of-buffer)
    ! E$ P+ M# J1 O
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))' [6 U5 Z1 b9 \4 \2 ]
  168. ) a2 A' U0 ~" ?/ Q
  169. (defun board-show ()2 G, ], l; E4 K" s
  170.   (with-board1 f3 q4 v" I9 ~/ |& B
  171.    (concat (buffer-string)
    1 T3 {8 F- x' [9 }4 C7 A% e
  172.            (if (game-over-p)% R! ]- X$ y0 l/ a9 g9 C, G3 x! J
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))8 C, C6 r- ?) q/ f5 Y! z
  174.             (format "第%d步" (1+ (session "step")))))))
    % r/ I! s& ~' j" x9 |  H; D
  175. " b2 n5 _0 E4 I4 s0 _' Q3 u* F
  176. (defun board-position-parse (cmd)' K# h) L' ?2 L3 f
  177.   (if (= (length cmd) 2)  v4 _% Q5 J6 _
  178.       (list (string-to-int (substring cmd 0 1))
    9 h- U6 x) N8 Q0 |6 q
  179.             (string-to-int (substring cmd 1 2)))+ R! v2 c4 R- E9 v0 O% z# s  v2 }
  180.     '(0 0)))1 [3 X" g9 ]8 N' a# {0 R) f
  181. 5 p6 r0 N7 |$ }% ?
  182. ;;; 游戏房间
    2 @. t( i9 F% _  Q
  183. (defun game-room-init (cmd)" [$ V" Y- M0 Y  z
  184.   (let* ((middle (string-to-int cmd))
    1 x$ D/ e9 _  M& i+ l
  185.          (size (1- (* 2 middle))))) d$ B- b* W  ?
  186.     (with-board
    7 x: }5 G* e$ l1 q
  187.      (board-init size)+ }; R% l4 l- M) Y
  188.      (board-put middle middle)))- |2 B# W5 D& m& m* z
  189.   'game-room): A' T. Y) v% V: H0 _3 Q
  190. " ~" S) n. s3 }$ g6 Z
  191. (def-room game-room1 h+ i1 M( w' C5 V2 i' d
  192.   #'board-show
    9 ~8 T5 X# D5 ~# {
  193.   (t (lambda (cmd)2 K  N: I9 E. Y- G& B
  194.          (with-board
    . \. Q0 b' |5 A4 Y6 _( g9 S' Q
  195.           (if (game-over-p)
    ( |* B" Y/ G0 [! c9 D" A
  196.               'living-room+ M: q& m! r% i7 A
  197.             (destructuring-bind (y x) (board-position-parse cmd)- D& J+ p( |: s7 O
  198.               (when (board-contains-p y x)7 h) u7 @8 y, x
  199.                 (board-toggle y x)
    6 ~  ?; H9 @( \% K
  200.                 (session "step" (1+ (session "step")))); h# ]8 h- n  h' d& A2 f. r3 T. t
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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