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

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

  1. 5 J: t/ \* Y% v% h2 _
  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;">;; 定义新的游戏地图$ m) z4 d7 P- [$ \
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    + }& I5 r7 M" x; i: _5 F
  4.   'tutorial-room-0)                     ; 默认的入口
    . ?, }6 s1 P% v8 u* M2 u

  5. % {' O. R' k7 T$ ], g
  6. ;;; 游戏大厅0 c" U9 Z$ s! D" X( c
  7. (def-room living-room& x; v/ q. r  V  J) A3 x% ?; e
  8.   ;; 进入该房间后的提示语
    - o4 c7 ^- |# k3 B2 l' e) n  L# r
  9.   "1. 教程1 o- G0 l# g  w; M; h) S7 C
  10. 2. 入门(3x3)0 ]2 y2 p, ?1 L/ l4 v9 y1 W3 M
  11. 3. 初级(5x5)
    ' q( E" S) s6 R/ E0 g$ k
  12. 4. 中级(7x7)# W. m+ t1 T- r5 Z  w
  13. 5. 高级(9x9)9 H$ z1 ]* H2 I
  14. 0. 关于作者
    8 p: j6 @# z, f  l+ V
  15. 请选择1-5开始新游戏:"2 k1 @  [& i% r; w
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名* D# ~# m4 d- y
  17.   ("1" tutorial-room-0)! e. f# x( v9 H+ M- w9 i3 s
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    / y2 F0 M/ E6 m  C& ~; Q* @- ?7 b
  19.                                         ; 相应的返回也可以为函数,动态返回房间名0 I" c5 |- c6 Z; R; Z% g
  20.   (t living-room))                      ; 如果条件为t,为永真
    & w$ ~8 P1 O0 s8 {
  21.   ]4 n- w: S! V6 k9 D
  22. ;;; 作者信息6 V- v9 `( p+ Y: x6 L! c& h- }
  23. (def-room about-room8 l- Q5 v; Q' r) S( m4 ^) p
  24.   "作者:redraiment4 p8 v4 K7 B4 p# d8 Q- R
  25. 微博:http://weibo.com/redraiment8 ~" R# A. J( ~3 C: _- K9 C
  26. 有任何建议,欢迎在微博或微信上联系redraiment。$ D4 N9 L$ Q9 A6 [
  27. 请输入任意数字返回游戏大厅。"
    0 t3 x3 z* b9 m9 \7 `1 T: t
  28.   (t living-room))
    : f! y9 m& c( g
  29. & V$ u- P) g& F7 I
  30. ;;; 教程
    3 k* |! c6 E( s& d( L0 T
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    5 I% s( z3 e9 X2 A- c; O" b& T0 G8 l
  32.   "The number of tutorial rooms")
    0 t0 O$ ?5 `6 ]. k& [. T! Y
  33. 4 D2 K8 _3 n: ]. }3 ^& L
  34. ;;; 简化教程的定义
    + ~0 G. G) H  G% v2 J
  35. (defun string-last-line (content)! t; ]& M% n5 w9 t# E, q+ |+ ]
  36.   "多行内容组成的字符串中的最后一行"
    . s  a6 q; j9 d7 F
  37.   (with-temp-buffer
    " x( A$ n- G$ E9 K8 }) Z
  38.     (insert content)
    1 ?# Z( E; {; D4 O4 x
  39.     (buffer-substring (line-beginning-position)
    , [9 d6 |2 m! i/ W0 x/ E
  40.                       (point-max))))
    8 n! ~( L/ d# G( q

  41. 1 o% M1 S3 n6 [- [6 Y
  42. (defun def-tutorial-room (prompt)0 i$ \" B  V! m
  43.   "根据提示语自动生成教程房间。
    ( U1 [$ Y, |# v

  44. 7 V7 g& z1 t+ k" U. D3 G; `
  45. 1. 提取最后一行作为问题;
    * e' [% v/ Z; U9 u/ }  v, i  P
  46. 2. 分析问题,获取期望用户输入的内容;
    # Q% ~* v9 |4 Y: ~% U% L. B
  47. 3. 定义教程房间和重复提问房间。"
    1 }) l, P0 U" V) H3 A
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*))); G7 A; Y% P9 F# E- w  V) x
  49.          (repeat-room (concat room-name "-repeat"))
    ) z% \4 A, z( _) K9 q2 l! q5 N: [# j
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    1 P" H! _- s2 {* W8 P2 x! x' a- R
  51.          (question (string-last-line prompt))7 a2 V- r8 i8 X$ C. B, g1 K. K/ @/ y
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)* E- l5 |: d# c0 G
  53.                      (match-string 1 question)))+ A8 _. W& A* f$ l9 K9 H- S
  54.          (doors (if except
    + y  w! g0 X% @6 Y" e. _
  55.                     `((,except ,(intern next-room))
    1 v  n1 s2 V9 k, g8 B+ y/ H2 D
  56.                       ("q" living-room), o. f5 i3 H! s* g
  57.                       ("Q" living-room)$ Z" A' w. _' q+ y% A4 b
  58.                       (t ,(intern repeat-room)))
    7 B/ ]4 n6 m7 @
  59.                   '((t living-room)))))
    : [2 }+ {6 O6 w& J- ^4 Q9 X
  60.     (def-room-raw (intern room-name) prompt doors)
    % T: l  }& [6 ~# e$ X
  61.     (def-room-raw (intern repeat-room) question doors)))
    # ]- C$ c" U# {

  62. 1 y* L  R* e$ O8 l1 Q9 W
  63. (defun def-tutorial (&rest prompts)# K: m2 t/ ~! L2 ]
  64.   "批量生成教程房间。"/ ?0 A4 R% o$ o3 ^; n3 H8 ?
  65.   (dolist (prompt prompts)
    . J" a: C* Y6 d" M6 l* R4 W( N: g
  66.     (def-tutorial-room prompt)))
    , S! y' D1 F& d. j9 t0 l8 L

  67. % @4 ]# V" D- P
  68. (def-tutorial7 M% n; C, j) k$ O. H
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。2 u( E( v  \% _6 j. k; G4 K
  70. 1. 教程: R' d9 \0 a/ [1 Y' v2 K% a1 z
  71. 2. 入门(3x3)
    8 _  D4 M1 a. V, Z
  72. 3. 初级(5x5)
    9 `7 k. L; n1 ~
  73. 4. 中级(7x7)) G  ^. j! _. {4 e% F
  74. 5. 高级(9x9)- [+ Z4 i8 k% p" `& g5 I
  75. 0. 关于作者
    % N. P) ?! K* V% m4 {/ Q, O
  76. 请选择1-5开始新游戏:  }& Z) S: G& `
  77. 您现在正在游戏大厅里。# t/ H! Z- V0 [, D" p- a( f! ~  e
  78. 请输入“2”进入入门级房间"- m3 I6 o; V7 N# e! Z
  79.   "  ①②③5 I/ Z6 }6 U( o' l( U, p5 }
  80. 1 ■ 
    7 y7 z, D8 `( q
  81. 2■■■
    : X; d% ?) f9 x& Z# u
  82. 3 ■ 4 _  M& g, z7 C6 ~5 k( l
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!) F9 \& L" e$ ^% A  @7 ^9 _+ n
  84. 请输入“22”来关闭第2行第2列的窗户。"' l" E% L0 s6 S$ l# {
  85.   "  ①②③% ]% Y+ d7 s" r
  86. 1   
    , z8 E8 ]4 F2 Q* [) ?) l
  87. 2   
    ; ^: A9 r5 ^! M
  88. 3   
    ! {; m& Z! x. f3 D+ s) `* ?: L
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    + c6 f5 L2 L+ m0 m8 c3 W
  90. 请输入“11”,试着开启左上角的窗户。"" ~! C/ Q6 t- O7 }
  91.   "  ①②③6 [6 y- Q# n0 y& s
  92. 1■■ ' w0 y. M- o  E$ a1 b, s: {- M1 V
  93. 2■  
    . C4 a) ~, K- n( c2 y- c/ @, g. V
  94. 3   1 a4 J+ R' q6 W. C% W, O
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。( ?/ T' d% C8 ~
  96. 请输入“13”开启右上角的窗户。"
    ! X2 o. e) [. y5 V) G% k! d( j# w
  97.   "  ①②③4 ?1 b7 k1 W  I3 D8 `: C/ W) b
  98. 1■ ■  ~# Z  _$ j3 ?6 y* k$ y
  99. 2■ ■
    9 p" ^1 W( s& ^- l! u- s2 y
  100. 3   8 ^: P, \1 C; Y4 j5 \+ z' a
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    : S' {0 m) w- `2 u! y: y* h" w- J
  102. 请输入“31”开启左下角的窗户。"0 {, j/ d4 b- ]1 p9 x7 w
  103.   "  ①②③) X( G( y2 n: \: D( X7 X3 E
  104. 1■ ■' S0 l5 O$ E& c, k
  105. 2  ■
    7 ^$ R( N* A# m' [1 S
  106. 3■■ ( }4 ?! e+ i/ M0 F# Q* j% i
  107. 此时,总共有5扇窗户被开启了。1 e9 z- g* F- u; D/ r
  108. 请输入“33”开启右下角的窗户。"
    7 S0 I; B) e$ c2 G8 |5 t+ y, l
  109.   "  ①②③
    0 Y* d8 V* R% ]; Z: x  G% Q
  110. 1■ ■. m1 @& K  r" O; y4 H* p/ `8 J2 g
  111. 2   
    % v3 R; b6 d2 n0 }6 J
  112. 3■ ■
    0 l8 n* S/ p" M3 M* o% b
  113. 现在,只有四个角落的窗户被打开。
    ; P# U: s" r! V# }& V. Y' u  E
  114. 请输入“22”完成最后一击!"7 j3 n8 s# i: w
  115.   "  ①②③4 a8 G3 J6 |- L% h  m4 X' @
  116. 1■■■) [6 q* R  O6 @( h6 p  ]
  117. 2■■■
    ; F5 y; a2 Y: |1 H
  118. 3■■■
    3 P1 ~7 I/ y4 u+ o* O9 X# B
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")" z3 Y" v# u0 f: [/ E0 a5 ?) v
  120. * g; P) n7 w: A! H" O( }  z7 _
  121. ;;; 棋盘4 w: s4 ^0 `; m
  122. (defconst *wechat-5x5-white-chess* 12288
    1 L+ c, ?0 N$ J- ~) u
  123.   " ")
    ' O0 R+ I7 l: x" L4 Y0 z6 @
  124. + K1 `) K2 i3 j2 j- ~9 L
  125. (defconst *wechat-5x5-black-chess* 96328 d$ e% x" `6 F) T0 V0 P4 T
  126.   "■")7 @4 n% u) i0 a: f5 ]' ]
  127. 3 ?1 R: y' S7 @2 L
  128. (defmacro with-board (&rest body)
    0 ~" `2 t6 c4 a
  129.   `(with-temp-buffer
    ( g- A) k- D% p6 D5 b
  130.      (unwind-protect# M  l  O$ v7 c  T( O- t; Q
  131.          (progn
    4 b* W) b+ t0 h
  132.            (if (session "board")
      V$ c2 f" F9 ?! o6 U
  133.                (insert (session "board")))+ ]/ T5 g% ^. _6 i# I) G! e
  134.            ,@body)3 R; U5 z  X. c0 r; Z+ e6 [
  135.        (session "board" (buffer-string)))))
    6 r# u) G. |: A" T) K/ c

  136. , ^4 ~$ D2 C, k. L" V, @
  137. (defun board-init (size)
    0 D/ _: D4 K8 `+ d- }6 i
  138.   (session "size" size)
    * j8 W& j- p" i: a
  139.   (session "step" 0)
    - o5 a% C9 Y) |: C9 \5 F
  140.   (erase-buffer)* ^- q. t& T# g! E
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))- B2 Z8 g* K) I3 E4 _! o& f1 s& r
  142.   (dotimes (row size)
    & Y" a, n5 ?& b" x8 j7 D3 l
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    ( i2 h! ^# d& C0 G* x$ c" A
  144. - M4 K) s, P7 @# s* Z% e7 w
  145. (defun board-contains-p (y x)" `% k) n$ K, i
  146.   (let ((size (session "size")))( i% L0 |* A5 B* g. W* ]& C2 [
  147.     (and (<= 1 y) (<= y size)! z2 `8 y# P2 [" ]" L9 T
  148.          (<= 1 x) (<= x size))))
      l5 ^$ d/ p( @9 m8 e

  149. 9 S# v2 ]2 F' U# R' y
  150. (defun board-toggle (y x)
    1 \, }% o( k! ~, ]! |5 ^. j
  151.   (when (board-contains-p y x)& c5 V( G$ U$ r6 B4 ^: ~
  152.     (goto-line (1+ y))) A. V8 g. s( Q" p- \
  153.     (beginning-of-line)
    # `. q1 K6 g& |. V, {
  154.     (forward-char x)
    3 d% W! s# _3 L8 [  _$ C, h
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))0 J* D6 t+ Z7 W* O
  156.                 *wechat-5x5-black-chess*
    : j! [0 K7 i6 E3 z3 v, B& m0 D! @- |
  157.               *wechat-5x5-white-chess*))
    . A+ R% G4 y* V6 E5 d/ V" h/ A% \( B
  158.     (delete-char 1)))1 f3 D5 K: r+ [# L" g
  159. ) U$ W& L: ?" [" \& g* S1 N
  160. (defun board-put (y x)' [' l% ~% o' g$ V% K8 S
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0))); l- d) D. }& ]% b8 _2 t
  162.     (board-toggle (+ y (first dir))
    9 t! K1 ]+ h* _5 z: X1 R
  163.                   (+ x (second dir))))). `# ^2 e+ q1 l& _* \$ W
  164. ! O7 ]- A% T0 I& G3 Z9 U
  165. (defun game-over-p ()3 }. y/ Y6 ~4 o
  166.   (beginning-of-buffer)( a# k9 K, V$ h# I
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    7 |& C8 [! B" S! S( X8 k5 d

  168.   s: u: ?2 G  R1 e) p
  169. (defun board-show ()
    $ A0 G9 g9 P+ m, [
  170.   (with-board/ E1 ~' L* g/ L) L. e+ o
  171.    (concat (buffer-string)
      a7 p7 V7 p% e8 m) t3 P+ A# {( {. m
  172.            (if (game-over-p)9 X1 D6 A- o1 {
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    5 u2 `  j& Y* d3 d1 s6 E
  174.             (format "第%d步" (1+ (session "step")))))))
    / U$ k' f3 s" s; Z7 B/ q
  175. 1 m3 U% @0 t0 C% K: P+ X" J
  176. (defun board-position-parse (cmd)1 q0 [! i* H# U* E6 C% {- x# ?
  177.   (if (= (length cmd) 2)- S) K1 `/ b9 X0 C, e% y
  178.       (list (string-to-int (substring cmd 0 1))
    4 J) c! v/ u8 s% U
  179.             (string-to-int (substring cmd 1 2)))
    5 Z% r1 X8 J: F2 p
  180.     '(0 0)))$ W0 o% Z* X( L" ]% |  H. B. O

  181. 0 N. r/ \! o/ n1 {$ f. Y, G+ Y
  182. ;;; 游戏房间
    % {5 \; R% U% w* J+ U# E
  183. (defun game-room-init (cmd). Q' b( i- |' Z1 }+ n# H
  184.   (let* ((middle (string-to-int cmd))
    : n4 ~; H( B: y2 z
  185.          (size (1- (* 2 middle))))2 e3 S: u3 _& q+ ~# i) S  B8 d  q% V- v
  186.     (with-board0 e1 Z0 X: D9 _2 T0 k
  187.      (board-init size)
    : u' C1 _  h* d* @
  188.      (board-put middle middle)))+ ]8 [' H  ^' `% C# o
  189.   'game-room)% _9 I/ V2 T" j+ o6 d2 y6 s1 }! M' @
  190. - U% U3 M+ x4 s+ u9 F: X% ~
  191. (def-room game-room+ q+ k; W* F. e6 X* @* h6 H2 _
  192.   #'board-show
    : Q( r0 ^% H6 z5 B
  193.   (t (lambda (cmd)) H4 K. W% [9 O
  194.          (with-board
    2 D4 }' H  |9 L( i# k
  195.           (if (game-over-p)
    : ]. k2 k& w5 `- y% C
  196.               'living-room2 Y- L- z% B8 c& l
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    5 m3 p6 @) D& C! e2 J) v
  198.               (when (board-contains-p y x)
    / [$ d+ ^1 Q; d. j
  199.                 (board-toggle y x)
    4 N4 ~" P  h% B+ B; A8 P
  200.                 (session "step" (1+ (session "step"))))
      A: [7 h' ?+ Y. f
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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