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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
8 c* ^2 s) q) U- g' E借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. 5 U3 C! K+ K. L7 e) 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;">;; 定义新的游戏地图5 v1 e9 B! a; \0 X' u
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    ( }$ u# e; A* s8 ~" V, I4 Z
  4.   'tutorial-room-0)                     ; 默认的入口
    / _' M" [( R5 }9 Z

  5. 9 q6 \* j' f3 p. m, J, X
  6. ;;; 游戏大厅
    - @1 m& j! r+ a: s6 l
  7. (def-room living-room( G; [6 B' C3 ]
  8.   ;; 进入该房间后的提示语
    / i! N  X- `' v) T7 G% T% R* Z
  9.   "1. 教程; n1 v) s: r; ]( Q* u2 G7 U
  10. 2. 入门(3x3)% O: x- g2 I* W; ?. l9 O
  11. 3. 初级(5x5)
    . h$ }2 S+ B5 ~; i0 _, Y9 t
  12. 4. 中级(7x7): D: E1 x9 A! g+ D% C5 Q
  13. 5. 高级(9x9)0 \  i8 l& @* [) d: O
  14. 0. 关于作者
    2 v( C: @6 u0 H
  15. 请选择1-5开始新游戏:"9 z0 G  `% e8 o% X
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名4 T; ^  O( C" v. q7 r3 U
  17.   ("1" tutorial-room-0)
    ' k7 ~- v, ^# C
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    - d2 X) @: b( E) b7 b4 d, C3 R$ ?
  19.                                         ; 相应的返回也可以为函数,动态返回房间名, V' V' g; Z8 p9 N
  20.   (t living-room))                      ; 如果条件为t,为永真7 P2 `% s/ ~$ m4 k+ f
  21. - [( h- q: O& K- d  }' K
  22. ;;; 作者信息) e% I- b% U$ r9 R  @
  23. (def-room about-room0 m. g; N4 ?' b" u" r$ T
  24.   "作者:redraiment
    9 ~7 A# s" j  g, r; k
  25. 微博:http://weibo.com/redraiment
    ; H* M, M% f% m" s+ E; N
  26. 有任何建议,欢迎在微博或微信上联系redraiment。) U6 S2 f  n- p- n! A
  27. 请输入任意数字返回游戏大厅。"9 d- k# C1 u  J4 q  \: S
  28.   (t living-room))0 S( N) w5 G' `( w
  29. 8 w6 M3 O3 E$ S3 K
  30. ;;; 教程" p7 ^" J& W8 j' J2 _6 T
  31. (defvar *wechat-5x5-tutorial-rooms* 09 K$ x- t  d( a+ `) m* e
  32.   "The number of tutorial rooms")5 [+ l% ~! @- L  |3 X$ r
  33. - e1 F+ H2 L4 C+ G
  34. ;;; 简化教程的定义
    ( Z& d: j* K( e7 t: x* h' z; l, _0 @
  35. (defun string-last-line (content)
    ' D" X; f, i& S) q/ Z- R* _  ~
  36.   "多行内容组成的字符串中的最后一行"5 h. M3 s, k5 i8 o( H9 E
  37.   (with-temp-buffer! @- W( K* l0 b. z
  38.     (insert content)% O: D3 I% {( k/ J* L* z4 h
  39.     (buffer-substring (line-beginning-position)
    % X; I- Z9 D% ^; J% ^( T
  40.                       (point-max))))2 c% U: Q& F4 d8 x& W5 G

  41. 0 b+ L' c4 e8 Q. q8 I. x
  42. (defun def-tutorial-room (prompt)
    2 g# i, m' R8 I3 I7 Y4 d; J
  43.   "根据提示语自动生成教程房间。
    / A* \1 o8 M$ _* j0 Q$ M8 G
  44. 8 `# r3 V; ?, y  @2 t* I5 @6 R
  45. 1. 提取最后一行作为问题;3 y, D' d& J8 z/ B: E/ t
  46. 2. 分析问题,获取期望用户输入的内容;
    , \( T4 ?( z5 f9 j3 N
  47. 3. 定义教程房间和重复提问房间。"
    / ^& A, ?" K/ |: O" F8 D
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*))): M* m5 \7 h+ k3 n& a! {
  49.          (repeat-room (concat room-name "-repeat"))# Q8 M4 q* [) v9 f
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    + s) w2 d4 u; i# w, W  |5 V/ _" Y
  51.          (question (string-last-line prompt))
    6 ]3 t: j2 F% |
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)7 x' w/ ^( t* b( H/ v
  53.                      (match-string 1 question))). y  P5 h2 N6 C' ]- j
  54.          (doors (if except
    : w' R; d5 o* K) W
  55.                     `((,except ,(intern next-room))
    % i" U" \) [  x9 Y, Q" f
  56.                       ("q" living-room)4 Z9 ~8 T( w0 ?  o; {, N3 |1 b: O
  57.                       ("Q" living-room)4 _0 M# N8 ]' u& V6 Z) v
  58.                       (t ,(intern repeat-room)))
    6 b2 P4 Q$ z* `1 x$ [7 y6 a
  59.                   '((t living-room)))))
    - p8 F# m$ |' r+ P& l- u
  60.     (def-room-raw (intern room-name) prompt doors)
    " b) ?0 N3 o( D1 n  W( q  v2 R2 u
  61.     (def-room-raw (intern repeat-room) question doors)))5 i, [/ W, ?% ^# E, |7 f) t. M" M

  62. - C- \3 v' U0 r
  63. (defun def-tutorial (&rest prompts)) D, R4 X2 z& o" m2 V" W
  64.   "批量生成教程房间。"; y# u; I" \1 i
  65.   (dolist (prompt prompts)4 R+ R4 w% N: |, W- Z4 T7 X
  66.     (def-tutorial-room prompt)))" A! H( [  M, ]$ X
  67. ) a* H; c, J: d. i! s/ T7 X/ E
  68. (def-tutorial
    ! T0 ?- i2 {7 t5 e, p1 `9 d; T
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    . x  Z! |3 p  a/ ?3 n6 T
  70. 1. 教程( Y) ?- x, g5 ~8 f
  71. 2. 入门(3x3)
    $ I3 B7 y' z& q& ^2 l! R7 U
  72. 3. 初级(5x5)' w# b4 H1 h9 }
  73. 4. 中级(7x7)
    " @; M( X* U# @, W1 y! ?, b
  74. 5. 高级(9x9)# J* w: h. s" l
  75. 0. 关于作者% i, w! I; t) S
  76. 请选择1-5开始新游戏:, Y0 w/ S2 |- A% y
  77. 您现在正在游戏大厅里。7 ^" E, X7 h! V, N
  78. 请输入“2”进入入门级房间"
    & l% [6 D( k" R7 b6 X6 X
  79.   "  ①②③
    9 V* T7 W+ S' U7 z( A6 G% l
  80. 1 ■ . R% k4 ?: @: l& @$ G
  81. 2■■■3 H' O& n: \  }( B+ I( H
  82. 3 ■ 1 r4 s3 o; [& X. _
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!) ]2 b- @0 S6 e4 r6 w
  84. 请输入“22”来关闭第2行第2列的窗户。"
    . E* U' u  @' ?5 J. Z$ i! {
  85.   "  ①②③+ m8 k4 C1 ]$ x( ?- g
  86. 1   4 l6 _: k' i* p2 M+ H6 M/ T
  87. 2   , I  {4 ~1 @; R) y( x  I% B
  88. 3   
    5 O/ {* v+ Y8 [6 D
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
      N& G' h1 D5 g6 _1 |
  90. 请输入“11”,试着开启左上角的窗户。"
    8 }" v' Y- m! J
  91.   "  ①②③
    : K  \, \) R' {
  92. 1■■ 
    ' {. _' `+ c$ J, {
  93. 2■  + y3 x8 [2 \! g
  94. 3   
    ( a* I5 L+ m$ \& ~+ a! Q$ J) R* ~+ @
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。+ p* {4 ]9 A* Z  p" P" X6 w) b
  96. 请输入“13”开启右上角的窗户。"
    9 d$ `4 a, ]- c: q  V
  97.   "  ①②③
    7 w4 z/ _4 K3 x, u
  98. 1■ ■8 R* f' j! v, p1 O. n0 ^: w# R
  99. 2■ ■2 V0 N8 U  |+ s: ?" |5 @% m0 y) e
  100. 3   ! \! A) U% Y$ @( W. I7 q
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。( i/ f8 p/ v! ]9 I
  102. 请输入“31”开启左下角的窗户。"% G, b. u$ }$ ]. l0 |0 K
  103.   "  ①②③- S  ~3 M( h% U" U' s: C
  104. 1■ ■
    5 G8 S+ Z& e' H/ m3 g) H# R
  105. 2  ■( p) o1 N6 y6 U6 @; P0 R1 }
  106. 3■■ ) J2 r4 X' ]4 y; ^7 A- [4 A
  107. 此时,总共有5扇窗户被开启了。, p) Z9 }7 C, S
  108. 请输入“33”开启右下角的窗户。"
    + o9 X) ?: S- H, {
  109.   "  ①②③# {6 Z% F1 O# c- L& c% f% u8 a
  110. 1■ ■7 \' ~0 g0 A/ U; A! r
  111. 2   
    # z1 [' P8 w$ F9 H
  112. 3■ ■7 [4 G8 S) o/ a# J
  113. 现在,只有四个角落的窗户被打开。
    5 v1 J/ i" q5 B% N- F" e
  114. 请输入“22”完成最后一击!"
    6 v* h  I1 M! E7 U# l, _
  115.   "  ①②③
    1 o4 M$ y1 d1 v4 L8 o2 A( [# P" x4 Q
  116. 1■■■) ~+ k: Z8 k, ^0 w2 f
  117. 2■■■
    . D0 t8 R1 o  R/ j5 x; X1 _& e
  118. 3■■■
    4 h$ j" Y' V6 M- M% D, t/ I1 a
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!"): Z' M& ^+ @; i; v( F6 R) J
  120. , r! g# Z4 T" X% N; q& A2 N
  121. ;;; 棋盘2 H) N5 L& T) `' [: H* h4 A
  122. (defconst *wechat-5x5-white-chess* 12288
    ! a& m8 d: a2 D' g0 L; }; W7 [
  123.   " ")
    * o  s! N2 n& S

  124. 5 i* ^4 N" [! O7 Q! |; G
  125. (defconst *wechat-5x5-black-chess* 9632
    - D. S$ Z+ Z& R! W: e/ X/ @" U. T
  126.   "■")
      u" L2 v9 D+ C3 x! U

  127. " n# q* i) M: N
  128. (defmacro with-board (&rest body)
    9 ?$ u- G4 D9 E; j4 s( Q
  129.   `(with-temp-buffer
    - T& K$ C6 J& l3 j# j
  130.      (unwind-protect2 ?  S' @; y% ~: W  ?) t
  131.          (progn) k' E% v7 L0 d, k0 X
  132.            (if (session "board")
    2 d9 a( E0 F/ W; q
  133.                (insert (session "board")))
    % H# H6 Z/ E, r0 T8 s4 f9 I
  134.            ,@body)
    . C4 r5 M6 @# u, }
  135.        (session "board" (buffer-string)))))
    % A$ f9 J; w8 T  U
  136. 5 ~. P8 h1 R6 V, A* W7 B7 }5 M
  137. (defun board-init (size)0 j* C/ p5 t$ E
  138.   (session "size" size)
    ' J* d, u5 P; ?% X7 [
  139.   (session "step" 0)  |. C4 }8 f- A7 Y6 }& o
  140.   (erase-buffer)- S* `/ g+ _) u; W6 G7 r
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    0 _" q& X$ b: f/ j
  142.   (dotimes (row size)5 T4 l3 a: g$ U2 f
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))* J' x% V7 T9 f4 X& f
  144. 9 ~( O: M/ D( w4 x# c# N& M$ T
  145. (defun board-contains-p (y x)
    : [6 l% ^. N2 ]3 I2 F3 M* W
  146.   (let ((size (session "size")))
    % V0 u7 Z# a/ f6 [% H
  147.     (and (<= 1 y) (<= y size); N& h$ R  C5 i
  148.          (<= 1 x) (<= x size))))# P" L* Z7 }9 F; C& I8 j2 o

  149. . G  i' f8 d" S
  150. (defun board-toggle (y x)
    2 F3 G6 q) p- h+ n0 }5 |
  151.   (when (board-contains-p y x)
    9 }) ]( i) b9 _
  152.     (goto-line (1+ y))$ s. V+ l! @' s# A8 ]
  153.     (beginning-of-line)
    ) g- B- @% \9 z: u
  154.     (forward-char x)
    1 n# G' p4 P, h4 I7 o  i) s
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    8 g6 t; O9 M5 Z' r+ K4 t, E$ [) T
  156.                 *wechat-5x5-black-chess*
    ; h4 Q1 R4 s5 ^8 L& J* C. C
  157.               *wechat-5x5-white-chess*))1 R/ s$ F# }. W/ W' L: \$ A
  158.     (delete-char 1)))
    & K; J5 U) ]/ A1 w, Y0 T) C8 A9 y

  159. ' C; ]* q/ w( B4 `" B! b
  160. (defun board-put (y x)
    % l! b! K% B0 N
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    - w- X' n1 r! ?: R6 E& l: m7 F
  162.     (board-toggle (+ y (first dir))
    # e* ?+ i& [& G; A
  163.                   (+ x (second dir)))))
    # I- ^# E$ u' h7 K+ B4 \* E
  164. # F% a6 x, s# a4 O' w$ }2 [) E. P
  165. (defun game-over-p ()
    6 Y2 g' P* i3 E- ^
  166.   (beginning-of-buffer)2 r  I: n  Y/ |
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))1 g# W* A* g3 X. E6 T: g% T+ `
  168. ) u, Q8 m! Z  P
  169. (defun board-show ()
    4 f% ~. f, \' |
  170.   (with-board" u7 i5 S  y+ x/ O+ `! X
  171.    (concat (buffer-string)* ?' {# [* m4 j* m7 m' U1 D8 t) H
  172.            (if (game-over-p)
    $ J: o* r; e9 @# @- S5 j8 H
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    3 f  T$ b7 p+ @0 S5 y  x( Z& F
  174.             (format "第%d步" (1+ (session "step")))))))
    ! a7 i8 L4 d1 O! }8 @; `
  175. 1 }7 s) E) e3 S, }) x  y
  176. (defun board-position-parse (cmd)# I6 ?; `, I7 h4 z
  177.   (if (= (length cmd) 2)9 S- k2 d! B3 p0 Z$ \1 V) g7 q
  178.       (list (string-to-int (substring cmd 0 1))
    ; K" D* V& T# l, l" _/ j
  179.             (string-to-int (substring cmd 1 2)))& _7 C6 ^5 l- n; T3 @: U
  180.     '(0 0)))3 V& r$ w! _1 a+ T; c+ q

  181. 8 p5 w9 `. Q0 b0 F" T+ p
  182. ;;; 游戏房间  U, O1 \6 i$ M0 j
  183. (defun game-room-init (cmd)
    ) [, d& f/ N1 ]/ Q5 M; E1 {
  184.   (let* ((middle (string-to-int cmd))6 e6 G0 \: D! i; m  f3 Q
  185.          (size (1- (* 2 middle))))9 f" o8 b; ~1 P) _( Q4 t
  186.     (with-board# ~- r* N5 `! |; \0 |+ K, v: J
  187.      (board-init size)
    ) m- C7 G% D' m7 I( z* q% I
  188.      (board-put middle middle)))0 }* e# }' O( ~) W" X$ X
  189.   'game-room)2 g# M! A1 g2 \
  190. 1 P$ t6 `3 X: T" b! e& E3 U
  191. (def-room game-room
    7 ]3 G0 K) p7 l9 K
  192.   #'board-show* M. J- j  B3 R/ V! I% B
  193.   (t (lambda (cmd)9 s& Z& O8 Z1 e% b( ]9 G
  194.          (with-board0 x1 d4 z+ N: v4 n! \" F; A% i
  195.           (if (game-over-p)
    ' K4 a3 _" l: j8 E3 [0 Z
  196.               'living-room+ ?4 b* i) Z- y9 a) A& d) g5 X
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    0 g0 O2 M% F- Y( K' f
  198.               (when (board-contains-p y x)$ b' d! C5 `$ f, w5 }& h/ z! `( t6 S
  199.                 (board-toggle y x)
    / E1 _0 T) O$ ?# g. l- C3 \
  200.                 (session "step" (1+ (session "step"))))& [/ C$ w% |' m% _1 A
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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