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

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

  1. , S% ?  i; Q9 H, Y7 u8 k1 u
  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;">;; 定义新的游戏地图9 j% A) r, i* A  l
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    % Q% a  m" M$ R& C" J/ f
  4.   'tutorial-room-0)                     ; 默认的入口1 b$ a6 A5 U$ e/ k
  5. 0 r- v' G0 h7 P8 l  o: m0 t
  6. ;;; 游戏大厅
    % s7 i* N2 r. v& i
  7. (def-room living-room
    # Z5 V2 ~, \/ L
  8.   ;; 进入该房间后的提示语+ ~- c, X# R: j  c0 K
  9.   "1. 教程3 ]. ?9 p9 X' M7 A
  10. 2. 入门(3x3)- a% ]& H9 t2 c* Q
  11. 3. 初级(5x5)
    - |4 H5 D: G! t$ i3 i8 l( l
  12. 4. 中级(7x7)  ?' x' F# j; G0 t
  13. 5. 高级(9x9)
    / l& q% a# e" F  U6 |  u( P
  14. 0. 关于作者( f6 a/ `. l5 v
  15. 请选择1-5开始新游戏:"0 _/ H0 P9 Z2 y
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名9 d* U0 E: E) V  j# C, \
  17.   ("1" tutorial-room-0)
    1 h- q) D5 ?) v3 o; c. b7 ]6 n5 F
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配1 J$ [2 V% o% u( h5 p+ e9 O
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    $ v8 h3 Y5 k, b' q% g
  20.   (t living-room))                      ; 如果条件为t,为永真
    6 v# l6 o9 D# o  s: j8 r* `3 G

  21. 1 r# y2 D( O% G: A( T
  22. ;;; 作者信息. q$ O+ ?5 `  a% X- T! r
  23. (def-room about-room
    1 G6 K1 M* T: ^) H* B5 X8 k3 k/ Q
  24.   "作者:redraiment
    8 O9 x$ D$ n( R
  25. 微博:http://weibo.com/redraiment
    / ~; O' t( L' u! V3 q4 O
  26. 有任何建议,欢迎在微博或微信上联系redraiment。' w3 Y/ w& u7 a, d8 E
  27. 请输入任意数字返回游戏大厅。"
      l: X% r% W# d: {+ Y
  28.   (t living-room))0 W9 x- N7 W& q+ d
  29. . [$ `7 e( T: r$ w$ Z
  30. ;;; 教程
    0 Z7 U: p; Z. e( w
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    9 s5 P4 v  y0 J* f
  32.   "The number of tutorial rooms")( v: x1 {& R" o, x3 U7 ]
  33. + r8 l9 w  r) V. t1 E& [3 N4 X# P5 D
  34. ;;; 简化教程的定义
    - v) d% N& g: H$ l
  35. (defun string-last-line (content)% W# ]$ n* \& C+ }: w! ?& ^
  36.   "多行内容组成的字符串中的最后一行"
    5 {$ M+ T- {4 X' ?1 ?% @. `7 |
  37.   (with-temp-buffer
    7 _4 W3 {- S; p& ^  A9 x* i1 I" i
  38.     (insert content)
    - x& U/ t7 Q! `6 ]; J( d
  39.     (buffer-substring (line-beginning-position)4 B3 ?, x  [" c. i
  40.                       (point-max))))/ x8 e' P0 W% r) Z6 ^

  41. ) w9 V0 A- V3 h$ b# N
  42. (defun def-tutorial-room (prompt)$ X7 ^7 e0 R5 w% t9 ~8 K
  43.   "根据提示语自动生成教程房间。
    8 K, P/ k+ D1 w0 s* l

  44. 3 t1 l8 g& f: A: r/ ~4 p
  45. 1. 提取最后一行作为问题;% R$ x. i! w. Q* {# x
  46. 2. 分析问题,获取期望用户输入的内容;* w! ~/ {% N/ o( m
  47. 3. 定义教程房间和重复提问房间。"& m7 A& c1 V( l9 Y+ D0 J
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))/ v. f( a2 G+ f9 K
  49.          (repeat-room (concat room-name "-repeat"))
    / L, s4 W* G! w6 \+ R8 ^
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    , l0 v0 r! U* E6 b9 T+ p" E. ]. n
  51.          (question (string-last-line prompt))/ O' @) @: O, h& w6 d
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    " ]+ m8 Q9 n  f1 Y) F
  53.                      (match-string 1 question)))1 `. \, X3 D0 f1 A; J
  54.          (doors (if except
    6 c9 |/ @: u/ t# q4 t
  55.                     `((,except ,(intern next-room))
    , Z( g( o+ i5 `, G6 f: U
  56.                       ("q" living-room)
    # |1 @* t" ~: n; [# i% H
  57.                       ("Q" living-room): o0 h4 ^/ v, I7 o" z
  58.                       (t ,(intern repeat-room)))5 v  ~2 }) ?# l5 c, Y1 b! E5 M/ F" y
  59.                   '((t living-room))))), b+ h8 m$ T; u5 E3 y
  60.     (def-room-raw (intern room-name) prompt doors)' t" z- T" U$ a' _/ j
  61.     (def-room-raw (intern repeat-room) question doors)))' {: l' @8 c" a! @2 O7 z$ V! ?
  62. / p- L+ A7 K3 g+ Q* D/ A6 ]$ ]
  63. (defun def-tutorial (&rest prompts)% S! n6 u6 [4 ]1 i
  64.   "批量生成教程房间。"6 N( k6 x" s$ {# ^( x, X% j1 \, X
  65.   (dolist (prompt prompts)) X; M8 \) N3 l- l1 \  n8 {0 z
  66.     (def-tutorial-room prompt)))
    * [% Q& T4 Y' b. t! w  F3 O$ Q

  67. 3 }' x0 B7 N5 L( E" t
  68. (def-tutorial
    , n. z" O0 [' D% a& J* ]$ Y
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。8 i2 _& F' g. c7 i: Q4 Z$ K: L) E
  70. 1. 教程9 i; w0 E" E8 @! Z' b, A( J/ I  i8 |, O
  71. 2. 入门(3x3)% j9 Q. y+ E" q; R1 R& i
  72. 3. 初级(5x5)% V  r6 ^% P( q* p
  73. 4. 中级(7x7)# G1 |! l, v! K4 {. X( o/ e1 g
  74. 5. 高级(9x9)
    + T+ h6 Y0 S$ i2 f- N9 s  j
  75. 0. 关于作者; S: T) |4 ^2 H' i9 W  e
  76. 请选择1-5开始新游戏:
    . s7 j5 a8 i* o' u5 N* Q
  77. 您现在正在游戏大厅里。
    / o& i: W9 P3 B5 s! `# n& o. p
  78. 请输入“2”进入入门级房间"
    0 Y6 _2 l/ I# h$ P8 G
  79.   "  ①②③, |, J4 F( c1 B6 O& G; P. ?2 r& n
  80. 1 ■ 
    # }' `4 m1 _0 J5 Z
  81. 2■■■
    # q5 l/ x8 l# V6 I6 q
  82. 3 ■ 
    # y! c, S5 O+ ~8 h$ C4 [+ x
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!3 P; B3 _' `& M$ n8 ~( F+ U7 _3 A
  84. 请输入“22”来关闭第2行第2列的窗户。": ]5 |  U* q- u1 l
  85.   "  ①②③
    0 }1 z% i- |4 D$ P4 Y" d% l# C  ^
  86. 1   2 w. L6 Z3 E. W2 N! X
  87. 2   
    . y3 k3 F& _" A2 M8 M
  88. 3   
    % V- ^4 b; Z+ N7 ^5 Z' R  w3 O
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
      y$ k6 S9 }" m" u. ^$ v9 W  Y
  90. 请输入“11”,试着开启左上角的窗户。"6 z2 ~+ B" d/ x3 f9 Y; Q) J1 ~  g
  91.   "  ①②③
    # {7 L" f9 \$ _" A3 K! N: R
  92. 1■■ % F% O, i8 A( G' I  ?6 J7 r
  93. 2■  7 p6 m' k- p* ?$ Y
  94. 3   
    5 n+ Y+ O  G1 m
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。5 V8 V5 d. u" h5 O
  96. 请输入“13”开启右上角的窗户。"
    , T* D0 Q$ d4 n: t0 F. |
  97.   "  ①②③
    - J8 K4 W8 V- T9 m% s  I3 T
  98. 1■ ■: z" }2 P2 B) ^) X
  99. 2■ ■( H0 g- B2 T4 e; o' F, J
  100. 3   + O5 g8 o, F& Y" X  T/ e
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。: K. J. [& [7 M# [( V! e
  102. 请输入“31”开启左下角的窗户。"/ H) C" W0 |! B
  103.   "  ①②③6 s3 C& y* V9 e
  104. 1■ ■
    ! T5 ]& c9 I0 d* o" ?
  105. 2  ■' f$ l/ [# m! O
  106. 3■■ 6 h6 n/ o' R' |0 F7 h0 z) ~
  107. 此时,总共有5扇窗户被开启了。
    . v  A8 ?7 R9 S. t! q0 Y3 P
  108. 请输入“33”开启右下角的窗户。"
    & ]7 ?) ^; b! n# T) V
  109.   "  ①②③
    5 [+ q& i6 L2 n$ |
  110. 1■ ■
    5 N4 N+ M" {: Z  F% }2 C" ~# k6 P1 [
  111. 2   & a" H- E; o+ H- Y
  112. 3■ ■" T" B8 O, ?+ G# |  r
  113. 现在,只有四个角落的窗户被打开。0 k: ~; ?' g9 W# Y# _5 R2 |4 p
  114. 请输入“22”完成最后一击!"
    1 r) w' v$ Z/ H- \5 f
  115.   "  ①②③
    1 ?+ k9 K. Z5 T5 R
  116. 1■■■! I8 P3 `7 k# \$ r( h2 B
  117. 2■■■
    2 X) Y' D: o( ~: y
  118. 3■■■
    " q1 U; Z3 a* W0 V3 Q3 w% j
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    7 C7 w! a3 c4 ?% N
  120. 8 n2 {! w/ f4 S6 H
  121. ;;; 棋盘
    9 _. v% G8 j; G8 c& w
  122. (defconst *wechat-5x5-white-chess* 12288
    ; N  B% R+ K: c* I
  123.   " ")7 R9 b2 s5 ^# F( E# G

  124. 5 w7 C, {# l0 i% h
  125. (defconst *wechat-5x5-black-chess* 9632
    ' o: G' I! G% l+ I
  126.   "■")
    2 t. q9 Q' r1 s9 y+ l) d8 N
  127. 9 G! a2 c* z2 S' U' j
  128. (defmacro with-board (&rest body), a: E* @% p  t3 \3 Z  L
  129.   `(with-temp-buffer- c% I% Y* D5 j
  130.      (unwind-protect
    : i7 u! q/ e6 @1 i
  131.          (progn
    " y: U2 j. w  ]4 g1 p8 |
  132.            (if (session "board")
    + M, Q' M( H, Z& |- k
  133.                (insert (session "board")))
    ( _: t5 F" j8 s7 Y( X% n
  134.            ,@body)! G" S; P- z9 {& ^/ r% Y& o
  135.        (session "board" (buffer-string)))))
    # [7 A1 N) w$ R- f7 p( E

  136. 0 A5 Z) a/ g1 Q9 ]
  137. (defun board-init (size)
    ! B! H( f( ~" a3 `; b
  138.   (session "size" size)
    1 M" Z: j  @2 S* ]
  139.   (session "step" 0)
    9 w* h  N  D0 i# s7 b8 h; `
  140.   (erase-buffer)# L: ]; `) s8 s- j/ j# L8 r
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    3 q+ E% s3 o, m* F( k+ z
  142.   (dotimes (row size)# l2 O4 B! f5 M
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    $ G3 l4 O- J4 \* i% U
  144. + F4 D  i" z$ q! {& X
  145. (defun board-contains-p (y x)
    7 H! u' A2 F3 p
  146.   (let ((size (session "size"))); S! @! x, K, b
  147.     (and (<= 1 y) (<= y size)
    , e% V: h  E- Z0 T
  148.          (<= 1 x) (<= x size))))
    4 o/ [5 P" o5 M1 U* T

  149. 2 i( V) G9 O5 S
  150. (defun board-toggle (y x)
    $ X2 u8 H4 l% s& m* q4 j) i
  151.   (when (board-contains-p y x)
    , d$ @0 e, W- G9 ?' \& M' \, y
  152.     (goto-line (1+ y))
    5 K) C7 M! \! p% w$ w
  153.     (beginning-of-line)
    ; D2 a) m4 n% x' A7 z
  154.     (forward-char x)' L  o/ f( b, M2 w: C/ {6 v+ y( q* y( g
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))5 ^! r* D: u& ^8 M" I
  156.                 *wechat-5x5-black-chess*
    ! Y( ]$ {; H0 q$ a, ~9 T
  157.               *wechat-5x5-white-chess*))
    ' H8 M$ [5 K2 }% V% P
  158.     (delete-char 1)))+ Q7 l7 s, T; h2 K$ I! r

  159. ! i3 @  x; W# w9 r! F  ~1 C
  160. (defun board-put (y x)2 {4 U2 X9 }* i2 X5 k' Q
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))% c, }/ j$ Y3 {& x4 p7 n
  162.     (board-toggle (+ y (first dir))7 n/ z/ A3 @) r3 q* f5 \9 O" U& {
  163.                   (+ x (second dir)))))) D2 P: u0 r2 @- r, O* d6 _

  164. - H) Z) p- F8 w! ^
  165. (defun game-over-p ()6 S( F! J2 }  [8 D* y
  166.   (beginning-of-buffer)9 J' k! V) n9 ]" A
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    $ m" N1 H. ~% `# E
  168. 6 t4 y, v; }* M6 D# n
  169. (defun board-show ()
    5 N( l) f% {) O; O; s: ]3 u
  170.   (with-board
    7 c7 @/ I5 }3 K; F( O# X( @
  171.    (concat (buffer-string)
    8 w' q! T" O4 F' _* X! \
  172.            (if (game-over-p)6 @1 c, O2 {+ ~. d# t
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))! E* o$ J1 F4 {8 F: @
  174.             (format "第%d步" (1+ (session "step")))))))6 t. g0 A* O) ^: i) f

  175.   C& t! U" F7 H1 _: F  e
  176. (defun board-position-parse (cmd)
    * x. r; f* ~2 K* J: x# M
  177.   (if (= (length cmd) 2)
    . c6 ?5 Y  C4 J- [6 p0 T1 E& i+ v( G
  178.       (list (string-to-int (substring cmd 0 1))( @8 S7 H; i& Z
  179.             (string-to-int (substring cmd 1 2)))$ M3 M: c$ x: H% j$ ]6 I& I
  180.     '(0 0)))
    . o+ |8 y; l* w) F! O% K. z

  181.   j. h6 a2 u) l6 e3 c
  182. ;;; 游戏房间
    " ?9 N1 O3 i  B% c; `# _6 u
  183. (defun game-room-init (cmd)( P1 o" x) ]0 ~4 e( }
  184.   (let* ((middle (string-to-int cmd))  A0 m, O1 S# d, I* h
  185.          (size (1- (* 2 middle))))
    # l6 I+ a, d' j  c6 Y$ k( q
  186.     (with-board/ b% x# h& _9 h" I, f" v
  187.      (board-init size)9 X# r* L- G% ]2 x
  188.      (board-put middle middle)))
    ! ?) _' R$ P/ W5 X" [' W
  189.   'game-room)* U0 D, n/ L9 W7 k+ \8 r* n9 t
  190. : Z0 E8 v" F- c" d- `
  191. (def-room game-room
    & F$ y6 U9 E  [/ o
  192.   #'board-show
      J: c7 K5 t: @
  193.   (t (lambda (cmd)  `2 M( t8 v1 U; ]4 }5 K' J3 n1 h$ D' X
  194.          (with-board
    : p0 _2 T" v" ^; T  l( X4 \5 i
  195.           (if (game-over-p)+ @( S! ^- c  A! {) s
  196.               'living-room4 s3 h+ E, ]  \$ D8 D
  197.             (destructuring-bind (y x) (board-position-parse cmd)) K& n8 M  Y/ c# Y  n
  198.               (when (board-contains-p y x); j9 a0 b* r$ K; g9 U4 i+ y5 i
  199.                 (board-toggle y x)% Y/ x3 L( S: ^& j; [- Y
  200.                 (session "step" (1+ (session "step"))))# r  h$ f+ D/ t5 O0 \( L& g
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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