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

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

  1. 7 B  U4 x- T9 {" p6 B5 }
  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;">;; 定义新的游戏地图2 {: y/ `* C0 G" J& n
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL( w% `+ B: [/ ^5 l1 s0 I
  4.   'tutorial-room-0)                     ; 默认的入口
    7 K& o+ s& U3 O5 O5 e' {

  5. 6 P, e7 i5 J9 [/ v
  6. ;;; 游戏大厅: W8 D8 \5 c& I) i( p' t5 d% W% w
  7. (def-room living-room
    3 _' I5 r* i  @. L6 R! A) X
  8.   ;; 进入该房间后的提示语3 V. i* c/ r( S
  9.   "1. 教程. E1 z; T) D# i8 W
  10. 2. 入门(3x3)
    - A+ I9 X+ F( \  X+ i2 x6 a
  11. 3. 初级(5x5)" r$ C4 T) V, x' u# a
  12. 4. 中级(7x7)
    / t6 R, Z* ~. L
  13. 5. 高级(9x9)
    1 S% j. E# V/ f1 p
  14. 0. 关于作者, W( j9 F' V/ o# ^6 z/ p' P& V" X+ l
  15. 请选择1-5开始新游戏:"
    ' l4 i4 t% m8 X
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名; a3 c+ p( T* k- N9 h' i
  17.   ("1" tutorial-room-0)
    ' g, r. G! B7 I
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    3 j8 p1 r: c$ R  ~" s/ G+ w" _- C
  19.                                         ; 相应的返回也可以为函数,动态返回房间名; O+ E8 b4 l$ v- o9 ~' }- |: X
  20.   (t living-room))                      ; 如果条件为t,为永真
    ) O2 X/ B, E( [( t! V7 B

  21. 5 ~# C+ p2 U! ~
  22. ;;; 作者信息
    , K" B" n' g, f2 Q
  23. (def-room about-room
    3 j# ?" ~" w# W1 J
  24.   "作者:redraiment
    : @. v" _" K8 h" c- b
  25. 微博:http://weibo.com/redraiment
    6 D' r: l) f9 k, \( b# q
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    ; e% a6 `0 b) a/ T, U0 ?, D0 }
  27. 请输入任意数字返回游戏大厅。"1 ]& {& A: Y8 P* w3 e# F' E
  28.   (t living-room))" q; v  W1 l, h9 s1 y3 K: I1 t
  29.   ~! d# m4 A: _, e5 m
  30. ;;; 教程
    & d! }; C+ @2 V2 u
  31. (defvar *wechat-5x5-tutorial-rooms* 0: p% H, m( K% N2 ?& [
  32.   "The number of tutorial rooms")$ L* R7 h2 S5 e

  33.   g4 D# G$ m" C% C$ L5 {+ B) g7 E
  34. ;;; 简化教程的定义
    ( e, F- t3 ?2 i/ o1 q
  35. (defun string-last-line (content)% d  G/ c4 `$ V% K/ m6 ?% p, |
  36.   "多行内容组成的字符串中的最后一行"
    : ?! }" K, {" L- f
  37.   (with-temp-buffer
    , @, Y  W0 F' D% Z4 G4 [- {
  38.     (insert content)
    ) Y' V1 \/ e0 Y4 ?: ~7 |0 N
  39.     (buffer-substring (line-beginning-position)
    & h1 |% ^3 r, H0 c
  40.                       (point-max))))5 ^. u( g- Q6 D$ D6 B. ]

  41. # y! A. `( @: Z* n2 s
  42. (defun def-tutorial-room (prompt)
    * ]0 o- w% k4 r6 \3 e! [; n1 Y
  43.   "根据提示语自动生成教程房间。' c. L5 f. p* h0 Y6 p# e

  44. 8 s2 n7 e* }2 O& ?
  45. 1. 提取最后一行作为问题;
    ) v8 Q2 n  @2 t* W
  46. 2. 分析问题,获取期望用户输入的内容;0 Q0 h  G- {1 o( \* |
  47. 3. 定义教程房间和重复提问房间。"+ _5 a. g2 n" q& A
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))8 N3 Q' \" H- \6 F( P+ A; W3 S
  49.          (repeat-room (concat room-name "-repeat")): F9 p9 O1 q) O: t9 f) c
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))( k6 F' Z2 }+ G( `8 S% R% n  E
  51.          (question (string-last-line prompt))3 Z) q/ R/ B0 C% M( I6 M( z
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    2 A" h7 f* _6 k; m8 I
  53.                      (match-string 1 question)))* s2 s" k4 w$ W
  54.          (doors (if except6 Q! U2 M1 m2 j' {8 p3 L
  55.                     `((,except ,(intern next-room))' Y) Q1 Z( `" k& M- `
  56.                       ("q" living-room)
    3 d5 r+ K3 ?$ G
  57.                       ("Q" living-room)
    ! T5 I( `1 i; H, l
  58.                       (t ,(intern repeat-room)))+ s0 Y' d- R9 J! X3 C  j% ~% v
  59.                   '((t living-room)))))
    9 v; l+ I' f0 p# r
  60.     (def-room-raw (intern room-name) prompt doors)
    ) M1 ^0 b( g( x& ?2 t8 b- X! O
  61.     (def-room-raw (intern repeat-room) question doors)))6 @2 w3 ]9 p) m8 c: Z" p

  62. 0 U# F% V6 Y' x/ c
  63. (defun def-tutorial (&rest prompts)
    3 f, t2 w, e1 [: n" Q2 a+ W
  64.   "批量生成教程房间。"
    # V, [* y& i* @
  65.   (dolist (prompt prompts)
    " f1 @" h! P! e; U$ H- r
  66.     (def-tutorial-room prompt)))2 o9 u% y2 @$ h/ b& _8 S

  67. ' R; k* ~+ e, |
  68. (def-tutorial% @" K4 N# s% U3 W9 u
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。0 ?2 _( u. _4 I' x
  70. 1. 教程4 {& t; \0 b1 c  v, |: n  ?% u  _' g
  71. 2. 入门(3x3)
    0 u4 k! i/ @& V7 V  {
  72. 3. 初级(5x5)) o/ u8 W2 D# x- s7 q
  73. 4. 中级(7x7)
    , [2 H7 T# Y: ?( u  Y8 {
  74. 5. 高级(9x9)
    , q% P# _% {* H9 I3 p* ]
  75. 0. 关于作者
    8 O7 h+ `1 d" r0 Y; ]- O
  76. 请选择1-5开始新游戏:
    ) k3 ?5 P- T$ \6 E! b3 j3 G! K
  77. 您现在正在游戏大厅里。
    " E* S# ~) |: T
  78. 请输入“2”进入入门级房间"# n/ T" a6 ~6 H! v( I/ c+ L1 ]
  79.   "  ①②③
    4 g( V7 g- {1 d3 z. s4 q- ^
  80. 1 ■ 
    % ~3 E* T8 D9 \2 W5 S
  81. 2■■■7 S7 h3 V2 y) s! ?
  82. 3 ■ 
    0 {" x! r, w- u: J
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    0 K) \3 X6 s; @" e: j0 R
  84. 请输入“22”来关闭第2行第2列的窗户。"4 Y5 W: ?; n+ `  Y7 A
  85.   "  ①②③2 e* a1 S! D1 }" Y7 h: Q) O; t; B
  86. 1   
    7 R  i$ M0 |% F6 r
  87. 2     i2 C9 X8 d  i. G2 o6 x! ?8 o
  88. 3   1 H9 d+ X4 u6 v5 t* h/ o4 Z4 F
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。5 q; O! n( {! M9 V
  90. 请输入“11”,试着开启左上角的窗户。"6 C/ u; V5 r* z) P7 f2 M6 W0 @
  91.   "  ①②③) O" f; t* J& Q& ]1 ]& O" J
  92. 1■■ 
    . X& I9 F2 t4 b
  93. 2■  ; x/ h4 }; |( H& Y6 s! G  x
  94. 3   9 l7 {' b2 c# S- f" o/ G5 t" V
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    4 q. ^  t9 n+ w
  96. 请输入“13”开启右上角的窗户。"7 Y- N7 N7 x% T" l$ ^/ G5 T
  97.   "  ①②③" N* }4 B! Y& k
  98. 1■ ■
    & k* S1 y' s' l& S# p% S
  99. 2■ ■
    5 X' B1 |$ u% ~- c; n! b
  100. 3   * d5 z8 c3 ?9 P. {- o
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。8 G& q1 r0 U( g9 T
  102. 请输入“31”开启左下角的窗户。"9 [. t' v9 A1 R( @; o* |
  103.   "  ①②③
    5 g: g3 n0 |/ }1 j1 [: H! K6 @# o
  104. 1■ ■. i7 V* o" V) E8 }0 m
  105. 2  ■5 e, a9 T% h5 u5 _: g* t
  106. 3■■ 3 J- a4 X- h9 w) x
  107. 此时,总共有5扇窗户被开启了。
    0 _# X& `1 r# A" P3 `% @0 u$ J
  108. 请输入“33”开启右下角的窗户。"
    $ E% N/ d0 R; o9 c
  109.   "  ①②③0 g* `& }- M; w
  110. 1■ ■
    + X1 z% X6 j) v- {& t/ I! Y( c
  111. 2   
    " }+ r8 L4 o* T8 \/ q2 T
  112. 3■ ■
    6 f/ n# t. V9 y% w/ x
  113. 现在,只有四个角落的窗户被打开。" C% Y- J+ y. @/ T# _
  114. 请输入“22”完成最后一击!"
    # ~# V# H# Y# f4 D' q0 R
  115.   "  ①②③
      z- r8 M0 H, L) k; K) y6 d% q# P
  116. 1■■■
    - Q1 p7 l1 W4 J# \' T5 l
  117. 2■■■
    9 J: h. t! g$ j
  118. 3■■■0 J+ m0 z6 N1 M' z
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
      y0 z6 P. ]5 k, Z
  120. + K6 r1 ^$ [4 F* y
  121. ;;; 棋盘3 ]8 ^' y; A: a1 k
  122. (defconst *wechat-5x5-white-chess* 12288
    ( b) W& L9 ]' S  l; a! B
  123.   " ")
    6 g% j; j, i) v; C# y* ?% G5 Z) n# ]

  124. ) {- K6 ^* f8 H# ^8 @+ h
  125. (defconst *wechat-5x5-black-chess* 96328 L/ Q, w& i! R$ j4 w2 D3 z
  126.   "■")
    + r: x# n$ v' N. x$ t

  127. : E: W+ h( w" Y% f' o4 j2 c2 `
  128. (defmacro with-board (&rest body)/ J( u  i1 V1 K
  129.   `(with-temp-buffer. w% x" m  R  n" ^9 g& o3 C
  130.      (unwind-protect
    * I' ?2 }% C/ v4 @
  131.          (progn
    - ~1 p: P5 [+ s  c
  132.            (if (session "board")1 M1 V4 @7 d3 O0 I# N
  133.                (insert (session "board")))1 S- ^' r7 ~! r6 t0 v+ Y
  134.            ,@body)+ i# E8 k0 T! r0 A
  135.        (session "board" (buffer-string)))))$ @: u( f0 S# Q6 e; t1 ?# |
  136. + w0 F# x. U" n1 ^) ]  @8 H! x
  137. (defun board-init (size)
    6 m0 B; x8 C% b0 ?4 ^/ K
  138.   (session "size" size)
    ' \; u! |+ \$ r/ k# @5 V; e5 L
  139.   (session "step" 0)+ k. n# e, C% K. D
  140.   (erase-buffer)
    + t. p4 x- I" u8 @' D
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    ( Z* O, a) N, W! \9 y4 X& p
  142.   (dotimes (row size)/ N) V  y) H) ^' {; H
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))1 T9 d$ ?3 z0 W

  144. 0 [1 v+ b4 N2 I: V/ z( P, g; z# m% `
  145. (defun board-contains-p (y x)
    4 x4 k- ^  B2 C: V3 \) f
  146.   (let ((size (session "size")))
    * d. V$ B5 B- z
  147.     (and (<= 1 y) (<= y size)
    ' a  B% @2 C& I/ r. G# Q
  148.          (<= 1 x) (<= x size))))
    0 c# I' W$ z- @8 ]
  149. 4 q2 h2 R$ i+ ?6 {
  150. (defun board-toggle (y x)6 I( U2 v% X1 h" n% z
  151.   (when (board-contains-p y x)# h6 d% `% O) q4 y* n+ P
  152.     (goto-line (1+ y))
    # W0 w$ o# W9 m' e$ y# P8 W
  153.     (beginning-of-line)/ v1 t/ I- n0 m( R3 J0 C6 h3 x
  154.     (forward-char x): j& n# _) S7 ~1 y
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))( v0 b4 r5 U" o5 @
  156.                 *wechat-5x5-black-chess*
    / i" ]5 ~# |  f  ~
  157.               *wechat-5x5-white-chess*)), ]$ J* N2 ?% B2 j
  158.     (delete-char 1)))
    $ h6 W) x* [9 v! W  w

  159. , T* m* ?( u, q* a
  160. (defun board-put (y x)
    ! s7 b) {' l' W* v8 v  |: `# z5 G' r
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))* k1 k9 W0 [9 r9 c/ B. M
  162.     (board-toggle (+ y (first dir))
    5 p$ W  ^$ s. [' U
  163.                   (+ x (second dir)))))
    , B' ]7 D" f+ x  M& ^1 p: h) k
  164. # R$ I" H6 A$ H; D( {5 m  _
  165. (defun game-over-p ()
    / X: X: `5 t. @  p  _
  166.   (beginning-of-buffer)
    $ x: c) Z4 J# K- [' J
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))  `* r' X. ~7 O2 q$ |; G, p

  168. % H+ m9 f1 l. U; \; {
  169. (defun board-show ()5 m0 I, m5 l" k8 a: `$ O
  170.   (with-board
    . Q, `6 Y" W) d4 B" T
  171.    (concat (buffer-string)
    1 h8 ~2 V# n* c  q7 z
  172.            (if (game-over-p)
    # `* j; u( w0 }
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    8 I# ~2 l, k, \1 j/ L
  174.             (format "第%d步" (1+ (session "step")))))))
    6 h2 r+ R/ T$ T+ j5 R/ e

  175. ( g2 ]5 s; k: i& s: x5 j5 Q
  176. (defun board-position-parse (cmd)8 J; g& ^; a( z. w
  177.   (if (= (length cmd) 2)9 v( F- x+ j. c2 U) L
  178.       (list (string-to-int (substring cmd 0 1))
    : s! }: G. |5 J$ ^/ R
  179.             (string-to-int (substring cmd 1 2)))8 T$ v, S' l2 d- \7 Q3 @# r
  180.     '(0 0))); X0 c, I* [3 x6 J2 s

  181. 3 `: n/ i: B- M# M/ t
  182. ;;; 游戏房间8 V/ a8 s8 L* w5 o
  183. (defun game-room-init (cmd)
    ! n/ z+ J1 O6 S
  184.   (let* ((middle (string-to-int cmd))
    ' M2 e' A9 d2 P
  185.          (size (1- (* 2 middle))))
    4 G3 v. B# o/ ?+ n3 h- ^4 y0 ?' q
  186.     (with-board: f, [% r; X+ ]6 N* D3 ^
  187.      (board-init size)
    4 e$ E+ x' k. P3 g2 p: [
  188.      (board-put middle middle)))
    7 R- t4 J3 s# X; t. o( W; t
  189.   'game-room)2 [+ |* [  D- U1 @

  190. 6 \8 r6 |& _4 Q+ Y8 u+ A
  191. (def-room game-room
    7 L2 Y. \$ c" b, m3 \, f
  192.   #'board-show2 q0 [$ p) y" |4 b! ^# A) S4 K
  193.   (t (lambda (cmd)3 P4 w3 `4 U1 V+ Y4 O8 I
  194.          (with-board2 |! k9 `; ~. ]$ H8 K
  195.           (if (game-over-p)* ~2 v9 h0 j& {6 X% O. U
  196.               'living-room; t4 K1 a# x- D# B
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    8 B: o! w) p: Z( Y9 S. j
  198.               (when (board-contains-p y x)
    % `3 @4 @7 I* k( h) F
  199.                 (board-toggle y x)' f5 a0 R" r3 k# R9 t: h/ l
  200.                 (session "step" (1+ (session "step"))))6 I5 \  ?) H4 K) m
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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