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

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

  1. , h* s/ [+ E# z1 K" x
  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;">;; 定义新的游戏地图' w: P, B$ Y; y+ t
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL8 A* j+ j* L( Y9 c- g) K2 V
  4.   'tutorial-room-0)                     ; 默认的入口
    . h* H' }/ D6 X0 H" B: P; o- s4 ~

  5. ; A  o! j( M" I& o
  6. ;;; 游戏大厅% w+ N, w5 s" x' c+ u! ]
  7. (def-room living-room, C: G' Q/ @2 N  T2 W- x$ a
  8.   ;; 进入该房间后的提示语
    3 d9 A5 S; s6 ?7 [% G
  9.   "1. 教程
    ( w- Q# E" l' f! C
  10. 2. 入门(3x3)
    - Y8 X9 Y. _$ f
  11. 3. 初级(5x5)
    . G% I$ P$ q0 C7 z/ d6 D
  12. 4. 中级(7x7)/ I! i$ m! a' |, D6 F, H- ~6 b
  13. 5. 高级(9x9), _: Q* b+ j4 A
  14. 0. 关于作者! w0 P0 i( ~! I- V8 U
  15. 请选择1-5开始新游戏:"
    ' w+ z4 [8 J: d
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    ) m" W2 |9 h- G9 v" O3 H' u
  17.   ("1" tutorial-room-0)
    ; u- _+ m2 N! A; B) z/ \: Z
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    - Q9 `- h8 |* m! k4 A: y
  19.                                         ; 相应的返回也可以为函数,动态返回房间名1 N* ~* w, ^& Y; C5 g% V
  20.   (t living-room))                      ; 如果条件为t,为永真
    # b, d1 S; T1 }( A- r# |
  21. ; E, |- W6 S8 I/ h% K
  22. ;;; 作者信息; M. W' W  j& K- {( a, Z! s
  23. (def-room about-room
    % @  @7 c5 V; s1 i8 L& R
  24.   "作者:redraiment
    $ L1 b( L7 z1 C$ B4 Q+ b. S  Q
  25. 微博:http://weibo.com/redraiment% ]% Z) b) B" h% l2 X# h( }
  26. 有任何建议,欢迎在微博或微信上联系redraiment。. d$ |# w2 t9 h/ `' C1 j) E# n' R
  27. 请输入任意数字返回游戏大厅。"
    1 o, {9 }: |2 T' }- f
  28.   (t living-room))
    8 E/ e& ~& K6 [8 W; B2 K
  29. 6 v- z# N8 C8 p6 S9 j8 Z9 S
  30. ;;; 教程
    ! U3 p; B# J1 o( c# \
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    * N7 M. U# `: A/ W; M
  32.   "The number of tutorial rooms")
    8 M+ |, t# h9 O$ l! v. E
  33. ) f. f3 z7 a7 i- Y, d( |7 g2 o
  34. ;;; 简化教程的定义: M8 T1 y, U# r+ x
  35. (defun string-last-line (content)
    1 y8 L$ N" ~! b# [: z& k
  36.   "多行内容组成的字符串中的最后一行"
    . ]9 y$ r4 X) v% w- c  W6 v+ |( N) M
  37.   (with-temp-buffer
    ( A1 L! Z, x! L
  38.     (insert content)+ V0 y/ S$ i, \& _& `+ P5 i
  39.     (buffer-substring (line-beginning-position), O% ~( k( }, O+ \
  40.                       (point-max))))# p2 q' j7 L( z& _( U& Q# T

  41. 5 p: M( R! a0 c
  42. (defun def-tutorial-room (prompt)# M! ?" b7 x2 K4 b: v
  43.   "根据提示语自动生成教程房间。
    7 X5 k7 d, c; S7 c* E8 M; a, {* l
  44. , x, F+ y4 ]( h2 O* [( i
  45. 1. 提取最后一行作为问题;/ q: X+ D" [( B6 [+ x% J( e
  46. 2. 分析问题,获取期望用户输入的内容;1 |0 r; `; h& l3 t% [8 c4 Y
  47. 3. 定义教程房间和重复提问房间。"9 Q& M0 e* D% I* S1 z& K7 D
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    ' f" x3 n2 W+ J
  49.          (repeat-room (concat room-name "-repeat"))& C  q4 ~2 @* Y" n+ F2 T- |
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    * |/ w& W" E9 l7 T, L( `( n
  51.          (question (string-last-line prompt))
    " `& `9 ~7 O2 @
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    8 ^: r+ Y3 Y2 d1 x: U
  53.                      (match-string 1 question)))
    5 n. C# l- I  ]3 Z7 B9 m8 Z# U
  54.          (doors (if except7 H1 K) z& r! v3 B+ ^) J
  55.                     `((,except ,(intern next-room))
    5 M, @& k3 ~6 X9 a% g
  56.                       ("q" living-room)
    ( ^4 Z, j1 a5 H  E. J) s+ r
  57.                       ("Q" living-room)
    * O$ P! P+ H+ E  [' y  N
  58.                       (t ,(intern repeat-room)))* S; }& U/ h: n9 y
  59.                   '((t living-room)))))
    2 x& r. ?' v8 `2 ?& N
  60.     (def-room-raw (intern room-name) prompt doors)
    7 K* b! L$ B+ A
  61.     (def-room-raw (intern repeat-room) question doors))), Z0 o* Z" G% p# P; c) m. Y/ z! i

  62. 9 {. A$ h- X- P  b: i4 m
  63. (defun def-tutorial (&rest prompts)
    9 l% `- U8 W6 X! Q3 z0 O3 W- F! C1 k6 N
  64.   "批量生成教程房间。"
    1 Z# H- d# H! ^2 K7 T# ~
  65.   (dolist (prompt prompts)
    9 k. A8 y9 F" }  q) S/ K: ^
  66.     (def-tutorial-room prompt)))
    % A2 P6 j; A9 Q  n: k. _; G% l# Q

  67. 5 A5 D# a* p6 u8 f  w+ K
  68. (def-tutorial
    % [" P( M+ d& [/ K. Q2 p
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。! M7 B; T. u( x# L# B7 t* h
  70. 1. 教程  c! ?! c0 u9 `& e8 {! c% @0 H8 i
  71. 2. 入门(3x3)
      S# W7 d$ i9 L8 Q4 a
  72. 3. 初级(5x5)
    % p1 g3 J7 {8 E% Q# _7 G4 |
  73. 4. 中级(7x7)
    6 w- a8 Q) U& g4 u  S
  74. 5. 高级(9x9)
    - F8 m3 D' ?" B5 ~: J+ F' w! G4 ^6 }
  75. 0. 关于作者9 \% d( u4 b2 J  L
  76. 请选择1-5开始新游戏:7 V7 n0 m$ E) l8 B" P
  77. 您现在正在游戏大厅里。+ F( v9 X# ?; j( C9 W9 W2 y
  78. 请输入“2”进入入门级房间"
    3 ^* z6 H5 E* q/ N" s9 s8 S
  79.   "  ①②③, W) P" V/ J6 {) K: `+ e0 \7 E
  80. 1 ■ 
    0 I+ E+ `% k4 L$ d
  81. 2■■■
    1 H8 _/ `3 x+ X) a
  82. 3 ■ 
    ! @1 A3 E: e4 @' R8 D' r
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!# J6 {0 b9 T8 o; C: z$ U) m/ ~
  84. 请输入“22”来关闭第2行第2列的窗户。"
    : G) T; S: ?( j3 h
  85.   "  ①②③7 U* V/ t8 T3 z% m  k
  86. 1   
    : w! `/ V/ s1 ]* M8 \
  87. 2   ' H& x) w1 C: w! i, F
  88. 3   
    8 n+ ?" v  f3 {4 v/ @
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。' ?1 @* K6 u0 f0 C
  90. 请输入“11”,试着开启左上角的窗户。"
    4 t4 j0 S* N$ z6 X. ]/ a5 _) z
  91.   "  ①②③( @! C) G9 ?. k& I, ?) f$ V* J& j
  92. 1■■ 
      P% S. Z1 h, {) k7 J" z
  93. 2■  * Z2 l& f1 P) H
  94. 3   
    % V; o, s4 |9 Q
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。% }4 }7 U% N# u
  96. 请输入“13”开启右上角的窗户。"* d. |' ^; Z& w& p1 A
  97.   "  ①②③
    % K6 r8 J. c" f; h( o/ g
  98. 1■ ■) |) l" a  B. b4 V1 O) n3 y
  99. 2■ ■2 I  H6 e* z0 Q, B6 |5 f6 ]
  100. 3   
    8 n5 i* Q2 C# x9 \
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    * W# n* f! r9 f" x2 S' ?) P
  102. 请输入“31”开启左下角的窗户。"' v+ n4 r  `( a" I
  103.   "  ①②③1 x, u5 [) c' D5 f  `7 a! n# W
  104. 1■ ■
    2 a) o" w7 O" j
  105. 2  ■: M9 G) K$ o6 b, v. [
  106. 3■■ 
    - k/ s7 @) n6 W! l$ B" J) O
  107. 此时,总共有5扇窗户被开启了。( ~/ B% u# q$ E5 P2 H' W5 M
  108. 请输入“33”开启右下角的窗户。"
    ( h2 Y/ @9 R8 c6 k4 P0 i  }
  109.   "  ①②③' J! o# w$ \& _- t& x
  110. 1■ ■' F  A: m+ h  Z. i' x7 O
  111. 2   
    6 b+ s7 w; C) m% g2 Q' h
  112. 3■ ■6 d' b5 c- ?/ s3 a7 w6 s
  113. 现在,只有四个角落的窗户被打开。
    . M2 P( _: L  {0 s
  114. 请输入“22”完成最后一击!"3 J$ B% d2 C; [4 v2 {6 ^$ {
  115.   "  ①②③
    - f7 M7 j8 A+ ~* J
  116. 1■■■
    ' _/ X0 h+ W+ h5 ]3 q/ _; G8 R0 `
  117. 2■■■
    9 o2 A" t, [7 Q. ~% J
  118. 3■■■* g* g/ X+ ?8 V# c& D
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    4 k+ }- T+ P- S  F* t+ y

  120. 3 g: ]. G. ^# ?8 L% [
  121. ;;; 棋盘% s  C  M7 }( \+ u
  122. (defconst *wechat-5x5-white-chess* 12288  Q3 f7 f# t5 v2 p# l
  123.   " ")
    0 J1 @9 d% [1 P( b; e
  124. 9 U* j! T/ x; H- e% [" y
  125. (defconst *wechat-5x5-black-chess* 9632
    % Y0 |% m" R( g4 C! @; G
  126.   "■")* T8 U. Z3 u* L) ?0 P: i
  127. % `, }1 ^* }6 X2 c" m
  128. (defmacro with-board (&rest body)
    1 T9 @: U- j. }
  129.   `(with-temp-buffer
    ) Z% H' D0 m# @. t
  130.      (unwind-protect
    0 Y: d' P* E* ~% ?: N$ o, E
  131.          (progn" U( F! p/ j2 [. H/ [
  132.            (if (session "board")
    ; R+ R4 r0 p7 Z5 p# h- c
  133.                (insert (session "board"))); ~* m% W/ S7 P3 s. b) K
  134.            ,@body)
    ! C) Y2 D9 g) p! A
  135.        (session "board" (buffer-string)))))
    7 P8 C' K9 R3 w0 T

  136. ' |- q9 Z0 E& u3 \/ O+ P
  137. (defun board-init (size)# ?* }6 t8 I4 l2 n3 N% Z7 L: @& H
  138.   (session "size" size)% E: K2 v) {5 R' K* m- [, T$ t
  139.   (session "step" 0)
    5 K- m. C* V0 W% V
  140.   (erase-buffer)
    , @( K# |2 q; }: K# _) |" j
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    1 b9 K) z* a+ B. X+ m0 E
  142.   (dotimes (row size)
    1 R& |6 U( i5 `# i$ L
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    ! g- h  f# s+ G. R- V* A

  144. ; J4 }! ]2 @' w' V! t
  145. (defun board-contains-p (y x)+ s  F2 S1 ~& N% Q5 N4 h
  146.   (let ((size (session "size")))4 X/ Y1 M4 k4 n9 w4 V3 z
  147.     (and (<= 1 y) (<= y size)( l: b# ^( C1 n3 g, R
  148.          (<= 1 x) (<= x size))))
    . Z7 u& b6 r6 ~1 [  \4 Y
  149. ; t  y% e$ o' d8 V* a) t& h* r
  150. (defun board-toggle (y x)
    ) n1 x0 [+ C# ~: L, |
  151.   (when (board-contains-p y x)# n" a( O# x+ J  D7 a
  152.     (goto-line (1+ y))
    9 ^/ E: e1 @8 j5 V" N1 d
  153.     (beginning-of-line)
    # w0 J, K$ F7 g% L2 f! U) o
  154.     (forward-char x)5 x9 C: b  z0 M/ t
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))6 D* i7 u4 t* J
  156.                 *wechat-5x5-black-chess*5 t% Z" W! s/ J& B# |6 d. y' k
  157.               *wechat-5x5-white-chess*))+ S0 L% Y$ {) ~, |$ u# A2 p  P, h
  158.     (delete-char 1)))
    4 w4 \$ c: T9 O) S+ t

  159. & v  o2 P4 m; d. Q2 g
  160. (defun board-put (y x)5 q/ o* U* ~; d5 F
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    6 W2 P# S3 c4 ]0 `1 N
  162.     (board-toggle (+ y (first dir))
    7 v7 L( E2 r/ ^# s
  163.                   (+ x (second dir)))))
    $ o( G* j  c2 k$ O  x
  164. ! _# w& s1 ~5 c
  165. (defun game-over-p ()
    ! T7 X) x0 D4 @  r8 G" v
  166.   (beginning-of-buffer)
    % w! D1 Q/ \9 c
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))( B" F# q- y1 s
  168.   ~' W" g. U1 R( `/ ]  W' D# ?/ W
  169. (defun board-show (). t. }2 g2 S, u9 g5 i/ W. G4 j
  170.   (with-board6 }6 S; K/ C8 v6 V: G( e% D0 |
  171.    (concat (buffer-string)+ S+ F. A- t$ P4 s
  172.            (if (game-over-p): q/ I% [0 Q  H. [
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    + J/ @' L6 a3 M* f: y, c
  174.             (format "第%d步" (1+ (session "step")))))))7 z% J' K/ W0 X% |0 K6 i

  175. 3 c6 W) P) _3 P* ^, c) p, \
  176. (defun board-position-parse (cmd)
    5 S1 w) d# Y5 Q; @
  177.   (if (= (length cmd) 2)! b& [  ~: o' x
  178.       (list (string-to-int (substring cmd 0 1))
    " z6 E$ A) t2 g
  179.             (string-to-int (substring cmd 1 2)))
    3 @: M. {3 w- P1 d' s
  180.     '(0 0)))
    / b; h" o7 J& h' p2 T% F' w

  181. 1 `4 x( p. f: Z# P% e1 |5 X& S
  182. ;;; 游戏房间1 y5 {: E# O$ C/ h( B, W
  183. (defun game-room-init (cmd)
    ' I7 x, ~. a, p6 H! i9 P
  184.   (let* ((middle (string-to-int cmd))
    ) Q4 i1 r' ^) y- ^' _# u
  185.          (size (1- (* 2 middle))))
    : p( J' b/ x) F
  186.     (with-board+ z& v) g6 Q6 k3 m5 L
  187.      (board-init size)
    , s) A* ~( |8 ]- E
  188.      (board-put middle middle)))8 a) w( d4 R- |8 y* ^5 X7 g& @
  189.   'game-room)+ h8 u% e2 ?1 o
  190. . q. ^* K$ ^, Q& O
  191. (def-room game-room
    6 z$ b! x6 S. C) L6 o, \
  192.   #'board-show( `/ u% l/ g9 {: r$ m
  193.   (t (lambda (cmd), r1 B* R/ w, m+ b8 j! `! S7 f4 @
  194.          (with-board
    ( D+ H  ]; ^6 b
  195.           (if (game-over-p)
    : r2 S' y  d9 t
  196.               'living-room2 |, i+ T- J/ m9 }- J
  197.             (destructuring-bind (y x) (board-position-parse cmd)$ B' ]( N; ]& g) X
  198.               (when (board-contains-p y x)
      a' F1 v: R" Y, M5 B
  199.                 (board-toggle y x)) p" Q$ F6 g# v6 G9 L
  200.                 (session "step" (1+ (session "step"))))
    0 H5 ]6 E3 k" n# V
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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