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

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

  1. : b$ j' b" K8 m, W, H
  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;">;; 定义新的游戏地图, n  u2 }$ c1 u0 f9 Q: `
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    . c& O2 @& y. ~! Y7 ?
  4.   'tutorial-room-0)                     ; 默认的入口
    ; e- F! p% t/ V6 C2 N
  5. ( t0 G# K" Y2 h2 k. u, O% k
  6. ;;; 游戏大厅! q! `4 e+ W- F- q
  7. (def-room living-room
    - a) w0 {( d1 t5 G, A
  8.   ;; 进入该房间后的提示语# Z. {1 n8 h3 w/ X
  9.   "1. 教程% I1 f5 R. I8 d8 k9 N  @/ n
  10. 2. 入门(3x3)/ d0 x  A) Q- t% C
  11. 3. 初级(5x5)
    ' x9 s/ p* {2 o, o
  12. 4. 中级(7x7)
    6 ~/ m  y4 m/ ]2 j- |
  13. 5. 高级(9x9)9 n* t* d& r# \. r$ k( e
  14. 0. 关于作者% v' I5 y9 [) V+ p9 i
  15. 请选择1-5开始新游戏:"
      |& x! p" n0 C# O; z
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名! L2 I4 w' {4 }7 B; ~
  17.   ("1" tutorial-room-0)
    & F$ S  _9 s' p2 g2 r! @; X  D
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配8 x. |( _* u# V$ x
  19.                                         ; 相应的返回也可以为函数,动态返回房间名4 |2 c. k% g2 t; T* I! L
  20.   (t living-room))                      ; 如果条件为t,为永真9 @1 D, S" X9 o8 _( Q) J4 I6 R$ \

  21. ( `& E7 h  J/ S' {' n/ n  K4 ?+ X1 j
  22. ;;; 作者信息
      z/ s3 y4 A! W8 F
  23. (def-room about-room" h6 e6 D. j  I8 N3 |+ U, x
  24.   "作者:redraiment6 e: ~7 `* ]( y8 [
  25. 微博:http://weibo.com/redraiment
    + p( n* u" u9 Z5 Y% J
  26. 有任何建议,欢迎在微博或微信上联系redraiment。* m& D% Z! V% R* B# l: J, D
  27. 请输入任意数字返回游戏大厅。"' u  x) n& u) k/ t5 U
  28.   (t living-room))
    7 w. r9 G1 \2 N/ C- Z# W6 z# h, Z
  29. ; s1 I- S( N0 I: [' c0 `9 ?
  30. ;;; 教程0 p1 f2 |* R5 z& F5 j$ j
  31. (defvar *wechat-5x5-tutorial-rooms* 0! V6 s* }( U* l
  32.   "The number of tutorial rooms")$ [: u+ t: t" S& y0 a5 {
  33. # J( K* U, F2 I. y
  34. ;;; 简化教程的定义
    7 Y6 R, b3 b1 ?" S8 V  `7 E/ {9 W
  35. (defun string-last-line (content)
    $ X  T+ C3 A8 }, `6 f% a
  36.   "多行内容组成的字符串中的最后一行"
    2 l. v! E/ ]0 b2 @: M1 I0 j" m4 Q
  37.   (with-temp-buffer5 Q6 ?. Z# w& P. ~3 P
  38.     (insert content)
    7 C% t7 h  i7 x) W3 C5 ~
  39.     (buffer-substring (line-beginning-position)
    8 @1 M7 k) A* t( j( m, J# k
  40.                       (point-max))))
    / ~5 Q0 |1 A! t$ Z& W) m% a2 G

  41. ! q) q' U9 X" `' [
  42. (defun def-tutorial-room (prompt)3 L. s3 D' [5 j
  43.   "根据提示语自动生成教程房间。
    * }* Y7 Q+ ]9 U# |1 p9 R" U
  44. * v& d9 t/ U* O: p/ {
  45. 1. 提取最后一行作为问题;- x* T. m; }; R, B4 `- z% w( r" \
  46. 2. 分析问题,获取期望用户输入的内容;
    , d* ]  A' E* M8 k- U- C& J" ^
  47. 3. 定义教程房间和重复提问房间。"8 `5 N7 }9 {& J. s, m% X5 u
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))8 S+ Z/ S) o# A
  49.          (repeat-room (concat room-name "-repeat"))
    0 Q" w5 J* P  B' A( X5 X
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    . W+ z- _( G- E, T0 E
  51.          (question (string-last-line prompt))& G. ?" w$ Q; w* h0 f: R! W2 s
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    ; |$ ]# Z9 S# _$ ^' S, q- c
  53.                      (match-string 1 question)))
    ! v) a# X) i% d* v  r6 G
  54.          (doors (if except
    - i7 c" L  V' j/ M
  55.                     `((,except ,(intern next-room))
    " J7 @3 n1 k  d8 d
  56.                       ("q" living-room)7 r8 D3 j" E$ {, t: L/ ^
  57.                       ("Q" living-room)( i1 c$ B* u7 C8 ^( ^
  58.                       (t ,(intern repeat-room)))7 R8 z( V7 I9 z$ M
  59.                   '((t living-room)))))
    ( `. @8 C) z& s+ c4 h2 Q" N
  60.     (def-room-raw (intern room-name) prompt doors)
    ) F2 y: Y/ ?. g+ @3 k8 x
  61.     (def-room-raw (intern repeat-room) question doors)))
    " b: {/ O1 t3 T  e$ n

  62. % Q0 `! c# k0 }! T7 y  R
  63. (defun def-tutorial (&rest prompts)5 M9 r' A. W  `. D# }
  64.   "批量生成教程房间。"* _5 u& b& I) Q: H* h
  65.   (dolist (prompt prompts)
    8 S( B( J! k# J
  66.     (def-tutorial-room prompt)))
    # I) V" \& [5 K0 ^. P/ v. F) _4 T" {$ U
  67. . f. i! K1 t/ |0 G
  68. (def-tutorial& Q- L  l8 y9 b, v2 z
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    6 l: K- M0 u; s8 p' e& u" |
  70. 1. 教程7 S6 ^1 f  T9 v, l
  71. 2. 入门(3x3)- W3 t' m: n  g7 G8 q5 S, B
  72. 3. 初级(5x5)
    3 D$ j( u& y, n$ M
  73. 4. 中级(7x7)
    6 `7 f% O2 g, N# x9 l1 A  X
  74. 5. 高级(9x9)/ @- Y0 N# S1 W
  75. 0. 关于作者1 A$ C7 G& Z* O: g  r0 Y
  76. 请选择1-5开始新游戏:
    ( `& C8 j* I7 u# ?" [
  77. 您现在正在游戏大厅里。, B) o, U& n# f4 `: k3 w
  78. 请输入“2”进入入门级房间"  I$ l! {0 d9 i' z0 E
  79.   "  ①②③
    7 ^8 q# [  Q+ V+ B1 N
  80. 1 ■ 
    4 E* \! c% E5 t
  81. 2■■■0 D9 P3 B) I0 Y! u
  82. 3 ■ 0 u7 n: a% K/ b: m
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
      ~% y7 |6 _1 x1 ~- r- S
  84. 请输入“22”来关闭第2行第2列的窗户。"2 Y- Q/ Z2 ]6 |$ e$ w( e
  85.   "  ①②③
    " H  k1 S0 l0 [. L/ Z
  86. 1   
      Z) R$ H+ H( ]" L8 }& w
  87. 2   
    # K% T3 N  j: x9 I1 w
  88. 3   % {2 G' D5 S5 a3 w3 w
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。1 w1 ?/ T* A1 C- ]* n' K9 F
  90. 请输入“11”,试着开启左上角的窗户。"
    + C5 [) `9 ]- `7 C4 j
  91.   "  ①②③$ t  B$ O8 {1 ?7 c7 C) I0 u
  92. 1■■ 8 l( h* J5 \( b, {! j
  93. 2■  , A  Q( ^9 H3 h: `
  94. 3   
    8 N" i9 x9 m1 g' f- {
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    % N6 q2 @7 o* d7 K" g
  96. 请输入“13”开启右上角的窗户。"+ W* W% f! {# A5 K' x9 u1 S# H  d
  97.   "  ①②③
    1 |- }! ~" h+ o, d% _% n
  98. 1■ ■
    ! X  S2 P* E  ~
  99. 2■ ■
    . k. i1 I7 w- O9 d+ A" ~
  100. 3   6 }' h& c- ]( z+ G; d) Q) W
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。, p, F; j# I$ e4 F) {5 I
  102. 请输入“31”开启左下角的窗户。"% c; k# p( }2 K% |3 a  v
  103.   "  ①②③* V' L9 ~. R6 f3 [" I% ?
  104. 1■ ■( R3 ?( h8 g/ m' |, u: G
  105. 2  ■2 [6 e  i6 D% Y$ I
  106. 3■■ 
    ) C( {/ ]/ d( S$ Z2 N$ S+ |* U  n3 I
  107. 此时,总共有5扇窗户被开启了。8 d  ^+ n: {: H; S
  108. 请输入“33”开启右下角的窗户。"8 r% Q- o& _1 [2 w' d1 z! G
  109.   "  ①②③* I+ [9 Z" t3 [. n% c6 U
  110. 1■ ■
    * x) z" y! [1 H& k4 n
  111. 2   0 \' H; Y4 m  R2 G2 E  O
  112. 3■ ■
    . I: S2 Q& H/ c4 }
  113. 现在,只有四个角落的窗户被打开。
    + W1 u4 O& J8 k
  114. 请输入“22”完成最后一击!"6 @4 s' N. p. G, D% t
  115.   "  ①②③9 g+ B) Z) [; A' f3 P! s* R( d
  116. 1■■■
    ( U' U  a- t+ o! t4 g
  117. 2■■■/ K% U6 ?3 s' A+ C9 x# L- z
  118. 3■■■
    + e2 z6 h- j: D% s
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    1 P  Z, k) D0 ^( t9 \
  120. 7 |, L$ y7 Y  n0 w$ I. M
  121. ;;; 棋盘
    1 i$ C$ ]' E& c6 X; f& f
  122. (defconst *wechat-5x5-white-chess* 12288; Y0 e, ~; J% j5 M& D& h
  123.   " ")
    : u& g- k3 Z- ~, B" T
  124. 1 U7 B, w: U  j7 O3 d+ ~) Y4 u0 }
  125. (defconst *wechat-5x5-black-chess* 9632. o3 N0 r0 s  L$ a+ b1 Q$ V
  126.   "■")* M" @0 i$ d/ P$ D
  127. ( y$ a0 Q6 y% Y2 \4 F( I- Q% H# D/ B
  128. (defmacro with-board (&rest body)! k% o0 ]2 v6 ~2 U" s3 m* ?
  129.   `(with-temp-buffer: B& s# `0 W& p- _
  130.      (unwind-protect
    4 v' H  }0 Y+ M& T  J( Y
  131.          (progn
    " I+ E( F5 R9 S7 K1 {
  132.            (if (session "board")6 \' i! x! u+ L+ t; ?; j
  133.                (insert (session "board")))/ a- @& c  ]5 Y4 u' M
  134.            ,@body)3 @; [# _& V. O! v
  135.        (session "board" (buffer-string)))))# }% L0 u, U; n# W4 O1 ]
  136. : Q$ W1 C7 E0 U& W  H' Q, ]" D& D' |
  137. (defun board-init (size)
    6 }1 t' B2 T: ^! H
  138.   (session "size" size)
    ( R" L- U4 b5 g7 X- z
  139.   (session "step" 0)
    / T2 _9 z* c3 U0 J5 r
  140.   (erase-buffer)
    3 H1 o, s' u1 ]5 L6 V
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    % a$ P; _; l4 \7 X4 \' }% o0 z
  142.   (dotimes (row size)6 F+ _* B7 a5 j
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))0 G4 X- o& N6 [# ?; D
  144. * W+ e- W! t' W4 u$ [
  145. (defun board-contains-p (y x)& [% @8 V1 l9 e8 ^; R
  146.   (let ((size (session "size")))  ]) P! y* x8 ~; p" x+ G8 V: |! f* a
  147.     (and (<= 1 y) (<= y size): p% d$ s0 U+ r$ l. V* B7 \
  148.          (<= 1 x) (<= x size))))5 H* [* R7 C- \: U! R6 Y
  149. % H% ]5 e8 G8 U1 _
  150. (defun board-toggle (y x)
    % q1 o: C5 Y  k; {9 U
  151.   (when (board-contains-p y x)* [3 F; G; ~' B5 \5 s2 c+ [* k
  152.     (goto-line (1+ y))
    - g  r+ q! g( P1 b% W
  153.     (beginning-of-line)
    % _) \7 q2 {( ?5 d0 l4 L* B
  154.     (forward-char x)
    6 ]; r( t3 \. L1 e
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))2 ?7 q$ t6 Y$ e" d6 h# J6 ]! g
  156.                 *wechat-5x5-black-chess*
    / I( U3 L: ]2 [8 g3 }" o; d5 N2 Z5 ^
  157.               *wechat-5x5-white-chess*))" j& D, [2 v/ ~: }1 O
  158.     (delete-char 1)))
    3 {1 j+ ?! X/ R8 z

  159. 9 h6 q: X5 M; s; n' W! L, J
  160. (defun board-put (y x), H4 I$ k5 P" c0 d* b, w- K
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0))). A4 n! z2 f- C2 F; s! N8 ~# g
  162.     (board-toggle (+ y (first dir))
    . j1 M7 p/ L/ s
  163.                   (+ x (second dir)))))
    ( P( C* W0 r4 u) h5 z
  164. ) a" R( d7 v4 @+ u
  165. (defun game-over-p ()
    & b* ~6 a* F8 I7 m
  166.   (beginning-of-buffer)0 C6 e3 r1 @' N5 H, R
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))" L% u! o; ^. r6 t& a1 j' E
  168. % o3 n9 _: W; b# y: A" g6 z2 v- ]
  169. (defun board-show (); B. W' ?1 Y9 N0 {5 P8 n3 f+ U; `
  170.   (with-board8 U3 j$ U5 S" v1 j: C, m/ @+ M
  171.    (concat (buffer-string)
    ! h7 ?& T5 Y& A" N7 x* w& }
  172.            (if (game-over-p)4 m7 m5 l7 X" ^4 e$ ]& C& {
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    ; t* d/ A* l: O1 _7 [
  174.             (format "第%d步" (1+ (session "step")))))))
    . `8 D: |  V2 k7 C! T- S  p2 @; {' {! e

  175. ' Q. {0 `6 U! t
  176. (defun board-position-parse (cmd)7 Z, S5 O+ a$ k/ M2 A1 A# E
  177.   (if (= (length cmd) 2)+ `* H! \" E$ L( \4 t7 |* t1 ]+ k
  178.       (list (string-to-int (substring cmd 0 1))
    6 }  n" _7 j- f' N2 ~$ J9 n) b" `4 W
  179.             (string-to-int (substring cmd 1 2)))
    . ~3 Q( x: L9 F/ q
  180.     '(0 0)))) P" {; i' G+ I! `5 R+ s0 h* x

  181. 0 i. h: F2 D+ N9 E7 B! `
  182. ;;; 游戏房间" A' O* u' r* U3 o  O3 ^
  183. (defun game-room-init (cmd)
    4 u8 i, q5 W9 U* j4 l6 h4 A6 j& I
  184.   (let* ((middle (string-to-int cmd))
    " H2 C. c7 _! d* k5 r/ t
  185.          (size (1- (* 2 middle))))# }# r) c1 w! O2 L: H  k
  186.     (with-board
    7 W3 {2 n1 i( G" C4 r5 p8 I% T6 O
  187.      (board-init size)
    9 @+ ?9 N* n% z9 K
  188.      (board-put middle middle)))0 }: d# V( w+ W" M0 w+ s
  189.   'game-room)
    - W# C% ~$ C9 e: A4 [7 Q! n$ I
  190. # R" f$ W* Q3 s# N0 c
  191. (def-room game-room
    5 S+ G! u3 }9 f( o/ \: B7 i7 I% {
  192.   #'board-show" [- i% e, {. Y/ u) `
  193.   (t (lambda (cmd)
    6 \' ~& U9 g1 I  e) u
  194.          (with-board: A; }  A) Q: T0 U+ K; P
  195.           (if (game-over-p)$ j- Q* g, d  k- H4 B& B) C! f
  196.               'living-room
    . n/ x! _, e8 D% j2 a2 ?- r
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    * z1 J3 I' g0 O# R2 D$ C
  198.               (when (board-contains-p y x)
    ( G1 L; E% }4 C/ D9 }& \7 h
  199.                 (board-toggle y x)
    , y/ c9 i/ Q' A. v" k+ m6 l% h1 Y
  200.                 (session "step" (1+ (session "step"))))
    ' A; H+ J* K. S
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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