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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。3 C1 z/ j- ]- f8 X
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. ) g( t) j- x0 p7 Y1 O
  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 M. i5 C4 h/ B: d; ]: }9 u
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL9 _. ^9 w( Y) \
  4.   'tutorial-room-0)                     ; 默认的入口
    ; X# ]6 r( i6 }

  5. ) T/ z" O2 a( y9 v6 I
  6. ;;; 游戏大厅  c( F; m' m9 T4 F
  7. (def-room living-room9 _+ q9 S+ I* i' d% T2 Z% s- [
  8.   ;; 进入该房间后的提示语
    ) \. N1 k5 H9 Y4 ?* d
  9.   "1. 教程. |6 q4 Z' Y4 y
  10. 2. 入门(3x3)
    . y1 Q  X6 u0 e4 a1 P; j
  11. 3. 初级(5x5)  w& G- B. L( x% K6 H8 r
  12. 4. 中级(7x7)
    ) t. L) T# E! b4 W- V
  13. 5. 高级(9x9)
    1 \+ {9 Q8 s! K; a% L* [
  14. 0. 关于作者
    - {4 \$ |8 ^; W, l& A8 [! u% p( {
  15. 请选择1-5开始新游戏:"
    , H9 Q$ V& O- N* c" X
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名8 {3 K3 J+ P* [1 _
  17.   ("1" tutorial-room-0)2 K2 g% ]! _6 @" X
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配+ w9 c9 C2 [) ^
  19.                                         ; 相应的返回也可以为函数,动态返回房间名  R$ K- ^4 s' e( k
  20.   (t living-room))                      ; 如果条件为t,为永真
    2 I9 B' H6 |# R* y9 M. M

  21.   L9 w' G3 t+ q5 f7 n
  22. ;;; 作者信息
    & E3 ~3 z4 J. a( D
  23. (def-room about-room
    ; V: J0 M: D0 @  _6 ]9 @
  24.   "作者:redraiment
    / u5 ]( b; D- D5 r" H
  25. 微博:http://weibo.com/redraiment' Q, d8 Z! I& ^8 M" U: R4 y9 `3 y
  26. 有任何建议,欢迎在微博或微信上联系redraiment。6 b& Z: N2 F) _4 o5 b8 V1 t( q9 e
  27. 请输入任意数字返回游戏大厅。"! E8 U! V- S- x0 o% i
  28.   (t living-room))& P1 o3 `2 M* N6 M$ r

  29. 7 w# U4 T# _2 S0 p) s$ j* `3 T
  30. ;;; 教程' e2 a9 Q; j* p& p/ a" t
  31. (defvar *wechat-5x5-tutorial-rooms* 08 k/ u( r' {8 B' L% B% @& y7 e% I
  32.   "The number of tutorial rooms")
    0 B+ A6 }# G& U! a" a! X4 d/ s, o
  33. 5 z: y& E" D/ C) y% n" M
  34. ;;; 简化教程的定义# x* S5 r1 n# \
  35. (defun string-last-line (content)
      o& i1 J, H* @; [7 G
  36.   "多行内容组成的字符串中的最后一行"3 M' ?6 g# T& @1 U+ b0 Z2 Q
  37.   (with-temp-buffer
      O6 _% H. w% x+ a" ~7 n
  38.     (insert content)
    , ^* T9 J: @7 E# B6 t8 J
  39.     (buffer-substring (line-beginning-position)! b# u. i+ c* k. @8 K' n7 `  O
  40.                       (point-max))))
    , s% R) R) K8 X7 K0 a, r' Z" F
  41. ! d; a+ R- Z+ w* [7 m0 n) J
  42. (defun def-tutorial-room (prompt)
    " ]0 |  l0 B$ E
  43.   "根据提示语自动生成教程房间。
    3 R( v3 s& S0 ?. v
  44. " L- C2 S1 s# }8 y- o# D
  45. 1. 提取最后一行作为问题;
    ' J. F% N1 I# s+ y+ y
  46. 2. 分析问题,获取期望用户输入的内容;
    ( n3 I2 {/ R8 G- H  t4 q: j
  47. 3. 定义教程房间和重复提问房间。"
    ' ^# G, [. }7 {. v7 r' Z& q0 t
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))2 Q* j: F- f2 i
  49.          (repeat-room (concat room-name "-repeat")). B  Q5 _5 D7 T: J
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    8 f/ O0 Z6 Y9 v  y0 p- E& T
  51.          (question (string-last-line prompt))6 q- ?0 N7 v0 y2 T2 s5 Z
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    7 ?* h2 {% z& E/ H& S  n
  53.                      (match-string 1 question)))$ J1 ?) i' z6 R. @( M/ Q) N
  54.          (doors (if except! v0 I9 L) {" ]
  55.                     `((,except ,(intern next-room))6 @  G8 }- e0 t; j  G' l) b) c" Y
  56.                       ("q" living-room)
    5 M6 ], x8 h- t9 A( L) w  w) |! r
  57.                       ("Q" living-room)
    8 F/ v( u1 g! N) H  v. a
  58.                       (t ,(intern repeat-room)))
    : y( R- _5 [! u$ o* ~2 ^
  59.                   '((t living-room)))))
    3 b, ?* u) o, `: N/ y
  60.     (def-room-raw (intern room-name) prompt doors)! `0 l" t0 f, l& s2 i- l
  61.     (def-room-raw (intern repeat-room) question doors)))$ A2 y4 r/ N4 j  p& m3 F; s

  62. " a, G$ p" d; ?" X& @9 a# S
  63. (defun def-tutorial (&rest prompts)& i5 R/ v7 K+ w$ |+ O& d( u$ e
  64.   "批量生成教程房间。"0 J5 g4 A; h! T- |& U0 V
  65.   (dolist (prompt prompts)( ?, i7 a3 C: N' K
  66.     (def-tutorial-room prompt)))
    + `2 a$ v) K5 z

  67.   B& H4 [: N$ F7 T$ I! C- {
  68. (def-tutorial
    ! |- F8 N  I4 w
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    9 y' S( ]/ N7 I* n6 q1 L
  70. 1. 教程. L0 c3 W3 y# M( ]
  71. 2. 入门(3x3)
    ( @+ [  l  @$ ~( m2 l" d9 x: O
  72. 3. 初级(5x5)" u% w. k% _$ b7 H
  73. 4. 中级(7x7)
    , S: h5 m+ f$ ^8 ]% {3 N7 U
  74. 5. 高级(9x9)& {7 ?8 `  J8 q8 l7 k1 M1 f
  75. 0. 关于作者
    5 Z( k! d" |0 q4 [
  76. 请选择1-5开始新游戏:1 ~+ J( b# l1 N  y1 w0 U2 {) ?
  77. 您现在正在游戏大厅里。
    # p1 k  g; y4 _# u( z5 J
  78. 请输入“2”进入入门级房间"
    + Z: G- r0 u% R- x: E
  79.   "  ①②③
    2 H: y8 R- W5 M: }: v# ]
  80. 1 ■ 3 k* ^' H; t; H$ b$ f9 P
  81. 2■■■
    * ~8 C2 m% [* s+ v" z
  82. 3 ■ 1 L; H- L% U6 D
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    $ y# m7 j' \! R6 N; q
  84. 请输入“22”来关闭第2行第2列的窗户。") W  t4 c, b$ t: L* G2 h
  85.   "  ①②③
    ' K: w; k* @, B8 s& p
  86. 1   
    . S3 s; D' L, }) K
  87. 2   1 R8 I* {8 t( Q! Q; B+ v" `
  88. 3   
    $ P7 v5 n7 E, z9 `: T+ O( ^* i
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。4 P5 z9 o( e$ @, d
  90. 请输入“11”,试着开启左上角的窗户。"
    . P. `: D4 |; g  r" Q/ n$ u0 F
  91.   "  ①②③
    . Q; @. w8 m9 Q. C& n& X. s2 j- M
  92. 1■■   y  f- m) u$ _9 @$ W7 [
  93. 2■  
    8 B' C* Z# a6 `/ G9 _2 H! U5 N
  94. 3   / r, M# `8 m& E% ~/ l! q& [. s
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    % l, A; E3 [7 r
  96. 请输入“13”开启右上角的窗户。"
    0 D; ~9 `7 ]9 Y6 Q/ A% x
  97.   "  ①②③
    0 i- @, ~1 W5 X/ v8 d, }% K
  98. 1■ ■
    ! c7 ]+ b- M) @0 F* o0 v
  99. 2■ ■2 M# h  @0 o; X# s# c
  100. 3   ; ?  D) X. o0 X# r& h
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。+ U9 Z1 m! a  b
  102. 请输入“31”开启左下角的窗户。"3 A5 a  X4 x- d
  103.   "  ①②③. x5 ^' ?9 `, y0 L& `8 Q# _9 _6 `
  104. 1■ ■3 o8 j& ~7 C. @/ L4 s+ s
  105. 2  ■9 }: L. l: n) l- z  t% Q/ i
  106. 3■■ : `. ^: E% _0 o( A
  107. 此时,总共有5扇窗户被开启了。
      c% Y2 d3 u8 H' g" N  C1 M& U
  108. 请输入“33”开启右下角的窗户。"7 f$ E$ z  b) u; f0 H
  109.   "  ①②③! K1 T- }8 D: F, @' k
  110. 1■ ■& k& T& m& Y2 j0 I4 ^+ `( I
  111. 2   
    + o3 k8 |  @6 b5 \
  112. 3■ ■8 `& \& w' j/ i+ B. Q! b
  113. 现在,只有四个角落的窗户被打开。
    ) U& J4 d) m* A+ b
  114. 请输入“22”完成最后一击!"
    6 |2 n* Y/ N& \
  115.   "  ①②③0 g" I: W; P, f$ C8 f
  116. 1■■■3 V4 [, N5 |4 e
  117. 2■■■
    $ Z/ Z/ M7 S; D
  118. 3■■■, x+ H8 J, H+ G: L! m
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    ' F6 t4 \2 F/ p3 i* b: T

  120. + H" d/ R1 V3 H0 X( G* \) E
  121. ;;; 棋盘
    * l8 X# o( W- `( r/ O
  122. (defconst *wechat-5x5-white-chess* 12288
    / U1 s6 g; P9 y8 f# w/ `( _
  123.   " ")
    ( m2 `+ m* o4 Q3 n
  124. 0 _+ _9 y3 K4 i( h4 E& |3 d( Z& ~
  125. (defconst *wechat-5x5-black-chess* 96324 E, O3 V, S0 W) D, {) A
  126.   "■")
    $ R) T5 B  h4 L+ g# u' }  O

  127. & K% \/ u' l" b- \4 r  Z' D" j! ?% L& M
  128. (defmacro with-board (&rest body)
    3 d) t$ T. N5 O7 k) q
  129.   `(with-temp-buffer) |4 k/ K% a2 L) }# F
  130.      (unwind-protect& }0 u- ]& k( l3 H; |& `4 G! r8 ?
  131.          (progn
    / O& n8 H* k; q  a  `
  132.            (if (session "board")) H! D+ T7 Z) z; r. H1 S
  133.                (insert (session "board")))
    . l! U( R$ B" w) d9 H" c/ s
  134.            ,@body)
    : j' `; F( c0 x
  135.        (session "board" (buffer-string)))))
    / t6 P/ w" d6 u6 ?0 F! Y2 a: M% c$ Z

  136. 5 R# p! X1 D2 c: k" z4 t
  137. (defun board-init (size)% d& g% U' `  U
  138.   (session "size" size)/ k% y* r  a! X" W2 j8 C( b; d$ u3 C
  139.   (session "step" 0)
    + L$ l* H- f3 w" w( A6 Q1 a5 v6 C
  140.   (erase-buffer)
    % V3 ^8 l( z5 {
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))* Y/ ]6 q2 ?+ B! n3 s
  142.   (dotimes (row size)
    9 s: T2 C! `8 A8 K
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))+ h, B6 G9 i# |4 E- i: P5 h
  144. + q7 K( S& Y: t- V
  145. (defun board-contains-p (y x)/ k+ r: J$ z$ C) H
  146.   (let ((size (session "size")))0 }. e3 u! n$ h/ O0 u
  147.     (and (<= 1 y) (<= y size): S% o7 H( b5 v9 {
  148.          (<= 1 x) (<= x size))))
    " L' S3 n# E  u0 m. A- |9 d

  149. 6 ^4 ?7 a& L) ~# T9 x; {7 I
  150. (defun board-toggle (y x)
    * ~0 z4 s# C' m5 t% H* I3 r
  151.   (when (board-contains-p y x)
    - r0 U8 k, s1 i8 p! k
  152.     (goto-line (1+ y))# }( z5 T7 ]5 L) W; J, T$ G: s
  153.     (beginning-of-line)
    ; `& H" U  v/ k
  154.     (forward-char x)
    ' k) ?3 i4 m9 @* c2 b# A
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    ( P+ t6 H7 b, }5 K1 V
  156.                 *wechat-5x5-black-chess** d8 R& C+ H- U4 f- J5 G
  157.               *wechat-5x5-white-chess*))& B6 F& z% e/ C. K% ?3 o2 R
  158.     (delete-char 1)))2 }( ~0 u0 M  J. T9 W) X  y2 U( e

  159. ! f; F; g5 [% Q8 N
  160. (defun board-put (y x)1 |' O6 t& p* z% X2 ^3 A
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    ' k$ f" E. |9 G8 P7 X  i
  162.     (board-toggle (+ y (first dir))  T! m8 ^1 I  _4 K3 v2 m
  163.                   (+ x (second dir)))))2 x9 Y3 B3 b1 j8 R8 l) s
  164. 3 K3 w2 V& S% I# `
  165. (defun game-over-p ()
    / [% s3 R$ N2 O' V2 }
  166.   (beginning-of-buffer)+ G% o! o. _. \3 R5 Z1 G# ~3 e
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t))); e; v6 O: c5 v0 |$ v2 k  W8 \( u

  168. ; @) i: d& l( t& D
  169. (defun board-show ()& D7 B1 N' _9 a/ p% E' E1 _
  170.   (with-board5 b' p3 `8 O2 c3 ~7 T; {7 s
  171.    (concat (buffer-string)
    4 d$ Q! @/ k2 f5 W& S
  172.            (if (game-over-p)
    ) Q$ h% c  J' s% o, Q
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))" N6 \2 c3 D: Z+ o9 S9 Y& w
  174.             (format "第%d步" (1+ (session "step")))))))" G. ]- C: z' g" p$ r

  175.   t, p6 b8 ?& V; a
  176. (defun board-position-parse (cmd)6 C7 {3 w2 O0 h+ @# W0 n  u
  177.   (if (= (length cmd) 2)
    7 V# Q( Y0 N7 o3 m3 P. |7 O+ ]7 D3 O
  178.       (list (string-to-int (substring cmd 0 1))
    % d2 d% K/ V9 V; w3 B( [8 C& @
  179.             (string-to-int (substring cmd 1 2)))
    3 `& y! H3 T6 V
  180.     '(0 0)))
    % J# C9 N+ [+ @2 _# d0 j0 h! x
  181. * ^$ ^" M6 Y4 e  |. @' P
  182. ;;; 游戏房间# x; w4 i! {! `+ X% p3 @
  183. (defun game-room-init (cmd)
    / |+ `/ J, [9 }% h' w
  184.   (let* ((middle (string-to-int cmd))7 u$ q/ q: v% A: W/ \, M! [
  185.          (size (1- (* 2 middle))))7 b  g) B+ m1 X+ ~0 s$ E& y/ W
  186.     (with-board% E3 V6 T+ {8 @6 {
  187.      (board-init size)# M- {4 Y; z! |- r+ x
  188.      (board-put middle middle))). I8 p! G/ ]; B- j
  189.   'game-room)
    2 }" U( I: K' h( ]7 }6 N, V

  190. " j5 j! b; m9 H9 z
  191. (def-room game-room
    . m! V+ o% r8 [  H* i
  192.   #'board-show7 c2 @- n) ]* q
  193.   (t (lambda (cmd)
      g0 A6 i+ f4 T; |$ K1 J
  194.          (with-board; _8 T  l7 e: a4 `
  195.           (if (game-over-p)
    2 b+ z! Y" ^1 o1 `2 c7 a# T
  196.               'living-room5 J7 E9 c' L2 {* _" d1 M' s* D
  197.             (destructuring-bind (y x) (board-position-parse cmd), X# a' `8 c: c% w' _2 Y7 V6 |
  198.               (when (board-contains-p y x)- V" @0 o0 G, B: F: ], L' U% S
  199.                 (board-toggle y x)$ j& ]- Q' q' _9 D) d7 M
  200.                 (session "step" (1+ (session "step"))))6 G& T1 w+ j  K" ?0 }, U; U6 g4 V
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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