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

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

  1. 2 |' s: M% j0 r9 G$ y. o( G
  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;">;; 定义新的游戏地图4 j% s, ^, @) Q, a7 z9 a3 f
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    . h4 v" E) h' a  U! X0 K
  4.   'tutorial-room-0)                     ; 默认的入口/ U& j- s0 J2 P8 P0 b/ F& O( Z

  5. ) y. R: z, N' D
  6. ;;; 游戏大厅
    ! F0 F7 x, W  Q* f9 \9 j
  7. (def-room living-room
      Q& S, Q6 l1 Z$ N! b
  8.   ;; 进入该房间后的提示语
      A, F( F9 I9 ?, ?2 V0 ~  }. o5 u
  9.   "1. 教程
    ; ^5 ?9 Z& Y3 \- D
  10. 2. 入门(3x3)
    * A3 U1 Z! f/ @* o  U
  11. 3. 初级(5x5)
    * u" y, x  ?4 r$ Z. w
  12. 4. 中级(7x7), j5 x1 Z9 i' N
  13. 5. 高级(9x9)9 G) S4 h# C3 Q) ]# C0 D
  14. 0. 关于作者4 Q" e& ~- c' C2 [
  15. 请选择1-5开始新游戏:". U6 r7 |* w8 X+ a- f
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    4 h! Q8 b# G2 K: P6 M# q7 ~
  17.   ("1" tutorial-room-0)
    8 V/ l2 y) F9 e6 z' S1 `) f. t
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    & w* y4 S* G4 T' \8 i9 a- F3 k- |
  19.                                         ; 相应的返回也可以为函数,动态返回房间名$ n$ T+ n8 u8 r
  20.   (t living-room))                      ; 如果条件为t,为永真# q/ y4 V' h3 B3 B- s6 E
  21. ) {' Y: {1 e8 P4 s8 z2 s9 M' L4 Q
  22. ;;; 作者信息
    % e. H/ F2 c5 q/ w; e$ b5 U
  23. (def-room about-room* k! O* r) V( x- u
  24.   "作者:redraiment
    ) e. j) I2 k' \
  25. 微博:http://weibo.com/redraiment
    2 {; f& a5 Q: o7 s. p
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    7 ^$ B" a, b* P' [+ |
  27. 请输入任意数字返回游戏大厅。"
    1 ~& e9 h& w4 Y1 ?. }  p! ^8 f
  28.   (t living-room))
    6 ~) I% ?  _7 V

  29. ! g/ O- o* F4 B5 Y7 `
  30. ;;; 教程, Y4 W' i3 Y1 \7 t- }
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    2 @% N+ }2 `' I( I  \, q
  32.   "The number of tutorial rooms")
    4 u% E, z1 q; k& @6 {7 n

  33. 4 ?9 p, R7 W1 q9 r
  34. ;;; 简化教程的定义% t( x+ c  M  `% q! f% J
  35. (defun string-last-line (content)1 v$ M! x9 z; Z( E% T! J, I2 h4 @
  36.   "多行内容组成的字符串中的最后一行"' O6 _( m4 x$ B0 J. d
  37.   (with-temp-buffer1 D, a* U, [9 j6 v7 b7 J. [3 I
  38.     (insert content)
    8 s/ }" u5 f& U
  39.     (buffer-substring (line-beginning-position)
    1 J2 b+ h1 `) G7 a
  40.                       (point-max))))
    5 I3 p9 U/ g+ C- X2 M8 ^+ U
  41. * d& X6 F( L- _$ ?8 R; {
  42. (defun def-tutorial-room (prompt)
    $ J" `2 w. ]# z; }3 m6 K
  43.   "根据提示语自动生成教程房间。  D$ `1 Z* n. |
  44. 1 D( Z. P' `% ^4 n- [& K
  45. 1. 提取最后一行作为问题;
    ' X+ [. z: r: m" S! r+ F
  46. 2. 分析问题,获取期望用户输入的内容;
    , b6 y7 S3 j3 L$ o9 w% G/ J
  47. 3. 定义教程房间和重复提问房间。"5 R- E1 W- d. @. N2 L/ R
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))4 z4 E3 u" e/ R' |9 C( ]7 D, O
  49.          (repeat-room (concat room-name "-repeat"))0 I! i$ G2 ~" F
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))( h5 K+ _: ^/ M' z  Y1 _- [
  51.          (question (string-last-line prompt))
    ! A3 b% K2 v* F2 G: y
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    " q& c$ J4 w0 R0 K& m; q
  53.                      (match-string 1 question)))
    3 N% n$ M) Z! \* b
  54.          (doors (if except* g, z( {3 F) X' G+ F  J/ n2 @
  55.                     `((,except ,(intern next-room)); f' V) }& }$ }* F
  56.                       ("q" living-room)
    % l; S0 J- m0 K- y* e- m+ u
  57.                       ("Q" living-room)3 y* i/ D& }- e2 M# K
  58.                       (t ,(intern repeat-room)))
    1 C- w0 b9 m7 y
  59.                   '((t living-room)))))" a$ e9 H& ~3 o( |
  60.     (def-room-raw (intern room-name) prompt doors)4 X" Q: Q4 Z- d) O; n5 s/ p
  61.     (def-room-raw (intern repeat-room) question doors)))
    ( U2 \1 w- p" W" w8 P8 O7 `6 k
  62. ) q0 p" \& r1 t4 J. I! s  A- N
  63. (defun def-tutorial (&rest prompts)
    $ k& t% R7 C, f9 E
  64.   "批量生成教程房间。"
    ; Z# I+ j" _% I+ x3 \3 Q
  65.   (dolist (prompt prompts)$ e* ^9 A' C1 ~& |: H- V9 G! {  k
  66.     (def-tutorial-room prompt)))  ?7 T- P; ?$ T
  67. ; c9 T! O0 Z# L
  68. (def-tutorial
    8 y3 W4 u0 A# P
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。+ Z8 i9 m; f$ m: A! V" J/ ?7 I- ?3 U
  70. 1. 教程$ \# L. ^4 l* d' k; L
  71. 2. 入门(3x3)- L" H0 j( ]2 n. r9 g
  72. 3. 初级(5x5)
    , ^3 y* p7 q* _- ]4 B+ O" Z
  73. 4. 中级(7x7)
    . {& V1 }0 p% |$ v: t, I7 z
  74. 5. 高级(9x9)/ L$ ]6 o5 X9 _5 _& W
  75. 0. 关于作者" D+ c, A. p' d0 |# o
  76. 请选择1-5开始新游戏:7 c. Y9 s) P+ z% E7 T
  77. 您现在正在游戏大厅里。/ I3 g5 A  p+ M# n/ M+ d8 \) Y
  78. 请输入“2”进入入门级房间"
    + N/ M6 b' O% {# N
  79.   "  ①②③2 w$ P+ C# `' R8 T% W
  80. 1 ■ " h* q: J* Q- T- g; h' ]* N
  81. 2■■■9 v8 G3 P. c% @* b
  82. 3 ■ . D3 o1 y2 }( W+ t2 b6 J
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    4 V0 o7 m) W- |7 l+ B
  84. 请输入“22”来关闭第2行第2列的窗户。"
    $ i" t# i! r+ ~4 r/ L( G
  85.   "  ①②③3 u3 n1 n$ V- c6 v  t  u- i
  86. 1   , Q2 T7 U9 ?4 Y" U8 K
  87. 2   % ?2 W9 ~( N4 `: `
  88. 3   
    $ b. [' k4 U6 p7 C2 t' S( }/ D" v
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。: M1 w& ~: Q, @8 {; F5 s( p% j
  90. 请输入“11”,试着开启左上角的窗户。"0 G  R% Y6 V  h4 E* p, o& r( u
  91.   "  ①②③* V3 P& L, u" h1 b' x. l1 h
  92. 1■■ 
    % ~# z0 ^8 Z' z- s' l# R
  93. 2■  
    : W' @1 b7 Z1 g* ?
  94. 3   
    4 g8 @6 i# _6 C; ]
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。; {) }  g$ ~4 _, p. m9 w* Q$ G
  96. 请输入“13”开启右上角的窗户。"+ b1 o$ i* O- T) p' O6 S- G
  97.   "  ①②③3 x3 @+ U9 }0 {# B1 r
  98. 1■ ■2 ?' [9 T3 T* N& L! W- p( \5 V
  99. 2■ ■
    . P- a6 S8 E  C
  100. 3   + b1 b# K9 d$ m  m% z$ k) n7 F
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    + m4 }' ~8 U- A( R9 D3 `: R
  102. 请输入“31”开启左下角的窗户。"% w: o4 a9 d! @& K
  103.   "  ①②③
    ! `* I, N! [0 u
  104. 1■ ■
    1 W8 f' x- u, N4 w
  105. 2  ■
      ~3 W2 t# R4 U; a
  106. 3■■ 4 b% w4 T0 Q3 j- g7 d
  107. 此时,总共有5扇窗户被开启了。
    . [: h4 e( d5 g1 ^) A8 {* z" r
  108. 请输入“33”开启右下角的窗户。") o1 u, V; A6 }1 S! O1 Q
  109.   "  ①②③1 D) G, N1 E- h/ I2 f
  110. 1■ ■
    0 r0 F9 |9 Y) ^" F+ y
  111. 2   
    8 ^& e1 `- Z9 ]3 ^
  112. 3■ ■% r- ^+ `4 C* f. E- O* _  L+ Z
  113. 现在,只有四个角落的窗户被打开。6 a2 q0 q! i6 K8 \
  114. 请输入“22”完成最后一击!"
    ; Y! a4 u6 Q' S- n/ A3 x
  115.   "  ①②③
    % B' b9 z! J# m8 l  i5 \
  116. 1■■■! W8 E0 ?, n$ D: J2 P' k( N0 K/ y
  117. 2■■■
    $ B# W( S% V: i! M9 N& f; [# `" L
  118. 3■■■
    9 z! |/ Y, n9 D; @4 {
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")# L' A& a2 E* Q
  120. & k) S+ F) F5 N) L, l; D$ n( E
  121. ;;; 棋盘
    + O$ E$ o' [6 j2 ~0 `( ^
  122. (defconst *wechat-5x5-white-chess* 12288  D6 L! I5 B$ T( p  F: x
  123.   " ")
    6 s# h- A! V3 N
  124. ; i5 ~% M1 `. p
  125. (defconst *wechat-5x5-black-chess* 9632! m) H* b- Q3 H! {
  126.   "■")" A( ?0 _7 L5 q
  127. ! D9 H2 k0 W( u
  128. (defmacro with-board (&rest body)# n6 v& ~1 ^$ ]' ~
  129.   `(with-temp-buffer
    , t& ]/ Z* E" i9 R7 _  P
  130.      (unwind-protect
    : c" O. K; B0 R& t6 w* J* m/ W
  131.          (progn
    : S* E- ~8 d( |+ y2 E1 W
  132.            (if (session "board"): K' S3 A$ A* d. l! E: n* R
  133.                (insert (session "board")))
    5 P* m+ U3 L0 ], g& t1 G, z0 e
  134.            ,@body)
    3 U' d+ p1 B. x: c5 u
  135.        (session "board" (buffer-string)))))0 l- l- @- c8 J& u- n9 }; t; _

  136. ; o, O6 o8 Y- v- N5 @  j  T4 a
  137. (defun board-init (size)0 s4 E' ~$ H. o7 w6 ?: W6 M9 n
  138.   (session "size" size)
    % U8 j, H- n" R( a! B  v! t
  139.   (session "step" 0), T# m5 v% J0 Y6 Y
  140.   (erase-buffer)
    * q' y  G# E: k2 \+ x. U+ r
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))# D" w; W4 K5 `9 z; D6 k
  142.   (dotimes (row size)) T: ~$ }2 C0 ^6 K4 u; x6 G' B$ `# m
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    ! B, C6 n0 t8 u. k1 I2 o# _
  144. - [2 f9 o4 d+ ?6 r
  145. (defun board-contains-p (y x)5 R1 F; J4 b8 M, N5 k
  146.   (let ((size (session "size")))& c/ O, n9 j0 H. v; O# x4 H
  147.     (and (<= 1 y) (<= y size)
    $ k9 T4 ]" x2 b* o+ h
  148.          (<= 1 x) (<= x size))))
    ; F! V0 U* I  v( \; o4 g
  149. % ]+ Y' `4 O% P) T# O; ?+ o. v
  150. (defun board-toggle (y x)1 _, h3 h6 f  P" A/ P. i2 }: ]
  151.   (when (board-contains-p y x)- e- P# e+ _* I' x1 C7 T- N8 L
  152.     (goto-line (1+ y)); R9 D  S; ~- i; m
  153.     (beginning-of-line)7 N, M5 W" D4 T  D- A
  154.     (forward-char x), \* z2 ~  x: U5 X) \4 m
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char)); b& b; l6 u9 X9 d! `3 S4 [: M
  156.                 *wechat-5x5-black-chess*
    5 D0 O1 J# k& V1 I: a. F+ t2 Y
  157.               *wechat-5x5-white-chess*))
    4 V7 D6 a: c  Z/ Q
  158.     (delete-char 1)))
    : {; `& s7 F- b) A8 V
  159. 5 H$ E  Q1 P) X$ C
  160. (defun board-put (y x)
    + R' P( b. U  ?* o
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0))). ^4 V: P0 f1 A1 g
  162.     (board-toggle (+ y (first dir))
    ; ]1 k! ?2 b2 L1 j
  163.                   (+ x (second dir)))))! n$ M0 n( P% |* D' }

  164. $ a* q1 G3 }% |9 @
  165. (defun game-over-p ()6 ~/ g( a7 v2 _0 [# G2 E
  166.   (beginning-of-buffer)" ~( ^4 y; ~2 J7 b7 y& L
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))4 p- M3 k3 P6 ?- u

  168. ( {3 k. S6 x: l; z& R& {' v; ^0 @
  169. (defun board-show ()" t* c6 j7 R4 c4 x3 a4 C
  170.   (with-board" ]! I, g" i! U) A3 i+ ?. w. V4 c* V) N/ P
  171.    (concat (buffer-string)
    * _' d6 L. v! M/ U$ H, d
  172.            (if (game-over-p)
    , k# f2 ?4 {  y, O' g
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    % |3 Q8 M$ w, h
  174.             (format "第%d步" (1+ (session "step")))))))% i5 m5 j- z: f: x6 s; h

  175. 2 Y* E: R  x: S& i2 ~8 v
  176. (defun board-position-parse (cmd)
      e  R, K: [3 I* ~
  177.   (if (= (length cmd) 2)# V! |5 i9 e) z/ d1 W
  178.       (list (string-to-int (substring cmd 0 1))+ z3 o3 E+ ]& E" E1 v- G3 Q
  179.             (string-to-int (substring cmd 1 2)))
    7 Q! w* d# C5 P
  180.     '(0 0)))
    # J$ z8 q2 s, p* c, w! H; v! n& i
  181. * b, c( `1 b: _2 F. q
  182. ;;; 游戏房间
    % @- E  ]5 q) v- ~# [
  183. (defun game-room-init (cmd)& }$ u5 F" u: U. B# c, E
  184.   (let* ((middle (string-to-int cmd))' p* D3 j& [( H8 z3 C5 H" ?
  185.          (size (1- (* 2 middle))))8 k" e: x# c' R/ K( ?& h5 D
  186.     (with-board; f  U' Y0 h$ u6 d
  187.      (board-init size)
    3 ^: [4 z: F& G0 M: o
  188.      (board-put middle middle)))/ s( i5 L! |8 ?1 e+ F8 P; G% ]+ E7 ]
  189.   'game-room)
    4 k0 ?4 s5 T% x) |4 D/ g

  190. 1 U& \. @, _. K8 ]4 ^( W  }
  191. (def-room game-room% ~$ z8 L6 c* X- n, [
  192.   #'board-show* s* z1 W9 N2 E2 s: i* \3 w
  193.   (t (lambda (cmd)% l6 s2 y, N0 \3 P/ p' Z1 h
  194.          (with-board
    * h. j5 u: R4 N- W
  195.           (if (game-over-p)
    6 c/ E& N! K' B) U
  196.               'living-room6 |. A' t% X3 |! @& K! O8 o
  197.             (destructuring-bind (y x) (board-position-parse cmd)8 w/ ?- b/ S( O- k
  198.               (when (board-contains-p y x)
    4 g9 H5 O* h' O, b
  199.                 (board-toggle y x)! v" q% v$ S. |1 `7 V; t+ x8 E
  200.                 (session "step" (1+ (session "step"))))
    1 S' N" K# z7 P+ u
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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