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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
" F6 P7 @0 g$ \2 Z借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. , ^- M( C7 t2 I1 w4 t; S6 T
  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;">;; 定义新的游戏地图  a- q  r9 x$ T
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    4 w/ ~+ g5 [# g6 D/ j
  4.   'tutorial-room-0)                     ; 默认的入口
    ! V4 u, M0 y9 C! U3 E0 L3 R. L
  5. + O2 q( Y3 T5 B1 C' C4 D8 C
  6. ;;; 游戏大厅
    * g) R3 b! j  f
  7. (def-room living-room) d8 Z. d9 d8 [% X7 E/ C( T- ^
  8.   ;; 进入该房间后的提示语( {5 n; {' W  R2 [' [& G" v
  9.   "1. 教程
    ; a3 @, n8 b2 `; z! x7 K* ^7 A
  10. 2. 入门(3x3)' F9 j5 i; B) g$ K' V6 h6 i' S
  11. 3. 初级(5x5)
    / A. P5 V& W" p, Z  {
  12. 4. 中级(7x7)& s8 v' }8 s% @8 G; F
  13. 5. 高级(9x9)
    - Q6 A7 y0 b, g6 T+ N
  14. 0. 关于作者$ u- z0 z7 K, a/ v1 o
  15. 请选择1-5开始新游戏:"8 Z* d/ t/ L. ~0 S+ v
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    4 A( N5 ]- I- X) W  h6 l! J
  17.   ("1" tutorial-room-0), G# r$ \& s# i" x1 b
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    # X/ `3 u; C) E' s* Y, K3 t% V( }1 k! V
  19.                                         ; 相应的返回也可以为函数,动态返回房间名2 s1 _) D& v0 ~5 n
  20.   (t living-room))                      ; 如果条件为t,为永真0 n0 V- C6 A+ V; k2 u: B! s
  21. * E4 s% P4 s' \- B4 ~, o
  22. ;;; 作者信息0 U& m" e1 L; T* F6 ~
  23. (def-room about-room
    & s- J- t1 Z* N/ i) }% K) z# ?
  24.   "作者:redraiment1 z$ e5 ~9 `. @1 g( c- O. F
  25. 微博:http://weibo.com/redraiment- X2 `- S5 O4 A5 E8 p
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    * Q3 }- N3 P1 h
  27. 请输入任意数字返回游戏大厅。"3 `3 C* f9 v; M; @. ]) \( w
  28.   (t living-room))/ r" Q7 B) S- _0 J- ]
  29. 0 \( m- [1 L+ f+ Q" }
  30. ;;; 教程: a0 Z- V6 X: P$ H
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    7 N# {9 z# i! w5 Q
  32.   "The number of tutorial rooms"), G) Q/ D6 `0 G5 v+ ~6 w7 F9 W
  33. 6 n- H: }, D: [
  34. ;;; 简化教程的定义
    3 F3 x$ @9 ^" x. y, [
  35. (defun string-last-line (content)
    7 H% N6 G% e0 a/ Q
  36.   "多行内容组成的字符串中的最后一行") I7 y/ ~0 g" U
  37.   (with-temp-buffer1 N0 p  D) ]" a
  38.     (insert content)
    ! c: J: u! n4 e( b" o
  39.     (buffer-substring (line-beginning-position)
    , f1 Y# O7 Q+ C+ g2 L$ c6 S
  40.                       (point-max))))
    8 x; A% O' k1 s- U  X2 a

  41. : ^" t$ ]/ o( l4 B$ i
  42. (defun def-tutorial-room (prompt)) d; E$ w& E- w4 C5 N& w# v
  43.   "根据提示语自动生成教程房间。
    4 }5 ?- e$ l; _6 J4 s- F
  44. 4 t; z, w( g2 {7 h1 F
  45. 1. 提取最后一行作为问题;9 E2 P, l# S, b* S
  46. 2. 分析问题,获取期望用户输入的内容;$ H% z- w* d8 ~
  47. 3. 定义教程房间和重复提问房间。"8 \3 x  X4 A3 o" W# b6 y
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))6 d) k" C3 A" Z
  49.          (repeat-room (concat room-name "-repeat"))
    8 k4 j/ u  y# Q7 v$ k$ o  l
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*)))). a+ @6 a0 D2 d6 g8 W$ A3 R2 x8 S
  51.          (question (string-last-line prompt)). Z% B/ g2 N9 J5 A5 |
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)4 [1 x2 G" Y6 o; O
  53.                      (match-string 1 question)))$ k* ], ~% c" o  ?) V6 r
  54.          (doors (if except
    0 T& `5 ]4 K; `$ R6 `. y
  55.                     `((,except ,(intern next-room))5 G' B6 S% o3 ~2 ?+ J
  56.                       ("q" living-room)
      V& \( L0 x3 S- r
  57.                       ("Q" living-room)
    6 r6 j. x+ }) r: v/ C
  58.                       (t ,(intern repeat-room)))
    5 x' f" B; h) L/ j- \. R
  59.                   '((t living-room)))))
    . I# R' I. I' c6 v( ^
  60.     (def-room-raw (intern room-name) prompt doors). B6 {9 Q) y, m8 d5 i
  61.     (def-room-raw (intern repeat-room) question doors)))3 _+ g+ `; v" _

  62. 1 ^, y/ i' X, K/ m* L: z) L
  63. (defun def-tutorial (&rest prompts)
    0 ^" \7 y& X( `9 t" M& b5 G- a
  64.   "批量生成教程房间。"
    + L2 T7 Z9 ~+ A, r/ o" M4 K; A. C
  65.   (dolist (prompt prompts)3 d' b7 v! {) N7 p/ i( h/ U6 U
  66.     (def-tutorial-room prompt)))' \# s, z, b/ d, @9 C0 H, ?
  67. ! H3 _/ h( N0 |1 ~4 z
  68. (def-tutorial+ a# z5 l9 p8 [( }
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    & A! H* Y1 j8 t  N; Q: e# n
  70. 1. 教程
    9 s4 s, G  e, Q) y- d2 \
  71. 2. 入门(3x3)
    % ]; ~; b$ ~! [! f6 B7 }$ X4 j! O7 z7 ^
  72. 3. 初级(5x5)
    & C. |; G/ o8 F" i* Q; y
  73. 4. 中级(7x7)0 a2 a. j' z7 n
  74. 5. 高级(9x9)
    ( O7 _3 ^6 ~6 O8 U8 ~1 O% {
  75. 0. 关于作者! F3 \) x6 T9 _) U
  76. 请选择1-5开始新游戏:
    : G1 r/ {0 c4 a
  77. 您现在正在游戏大厅里。& N' K2 \# M6 F! H
  78. 请输入“2”进入入门级房间": M' W9 B! c: d. T" `% G
  79.   "  ①②③0 I$ \# o2 Q0 C; w5 y
  80. 1 ■ 
    , [, c2 M% @% g- b" ]6 ~
  81. 2■■■
    5 M, ]+ }8 c  v5 Z" ~  C
  82. 3 ■ 
    ) ?7 `2 t7 I0 y) w4 [2 s! |* {
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    + f- y3 y8 Y6 h; c  G2 @' m% o
  84. 请输入“22”来关闭第2行第2列的窗户。"5 f& K; o2 ]3 Q' E6 [2 C' x9 L
  85.   "  ①②③
    ) ~' ~: D' c# K& C/ e: e/ F% z
  86. 1   4 g) v0 L6 H$ ~& h( U
  87. 2   - e* d2 }: h7 H' J
  88. 3   & Z+ y) Q0 D% [! m
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。, l- C% d* A0 D% X. y, ^" r# q. _: W
  90. 请输入“11”,试着开启左上角的窗户。"
    0 x7 N/ Z/ h% D
  91.   "  ①②③
      d% y% b; H$ x. X
  92. 1■■ 6 l0 ~' H5 ]9 K
  93. 2■  , g- ]5 F/ G* Z. [, u& s1 v! e1 k
  94. 3   
    0 O+ A$ P9 A  b, n! ?7 B
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    * G8 o5 R6 v- T0 E8 x6 y; M
  96. 请输入“13”开启右上角的窗户。"
      g$ O1 m" A, r4 I
  97.   "  ①②③
    / X% k/ s& g) P) r7 Q' B
  98. 1■ ■
    ) C( s4 w+ K9 r$ X3 _8 y' p( s
  99. 2■ ■
    7 c( q6 j8 G# a# }; X
  100. 3   
    0 N! B# L3 T$ _& U4 n7 o/ ]6 f
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。" E- o2 O9 D" o$ y4 @( I
  102. 请输入“31”开启左下角的窗户。"
    6 E3 |8 w! L# n" Z, n
  103.   "  ①②③" w3 ~9 V9 L) d  Y  E5 p5 P
  104. 1■ ■; [' x( T9 n5 l3 b8 y
  105. 2  ■! `' x: v+ \; B! q5 b. k
  106. 3■■ 
    % v' `! P3 y- G2 p
  107. 此时,总共有5扇窗户被开启了。3 _- e4 a0 O7 U1 T, H9 r
  108. 请输入“33”开启右下角的窗户。"( U# G- L% y( A7 c* l$ C0 d+ O
  109.   "  ①②③! X6 p1 T3 d/ A  k
  110. 1■ ■
    2 \( T- k7 K! h/ x% _
  111. 2   
    1 ]2 l2 X5 I  l& j2 Z: m
  112. 3■ ■
    1 h) f5 U* d4 [
  113. 现在,只有四个角落的窗户被打开。
    3 d' z/ c6 D" E6 Y- y2 C; Q
  114. 请输入“22”完成最后一击!"
    & i4 f/ O0 B$ S+ k' M) v) P
  115.   "  ①②③
    ) A: r, K2 w* M: m0 {4 V- s
  116. 1■■■
    " _+ K0 E, v9 J" k
  117. 2■■■
    5 i. a# J. [. e( }# t- Y
  118. 3■■■
    + N& m! v* c+ w8 j; a4 F. R
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    7 l5 {( a2 z( M+ Z! Y
  120. $ U3 l5 r6 q8 g+ b7 b
  121. ;;; 棋盘$ w3 e" F4 E+ I/ E  r7 @
  122. (defconst *wechat-5x5-white-chess* 12288% q4 l: h$ i8 @
  123.   " ")
    - J: u  h* _( ?6 M4 t5 ]
  124. 3 Q: _7 u# Z. d7 o8 N# M) k
  125. (defconst *wechat-5x5-black-chess* 9632- n* c/ n  V8 J4 h" P* A
  126.   "■")5 _  v" u. ^) b0 G/ \

  127. ) ^  R' d* K8 I2 `5 V2 M6 G5 D
  128. (defmacro with-board (&rest body)
    ' f* \" o8 b1 o+ |8 v  b  u
  129.   `(with-temp-buffer: ?& c' F( S' n5 U- s  U3 A
  130.      (unwind-protect$ ?( @3 L* F% H& Q
  131.          (progn2 K; r7 e, I4 F" y
  132.            (if (session "board")' ~$ j* M( M. H0 @
  133.                (insert (session "board")))
    2 N* o" S- ]4 J2 z9 e
  134.            ,@body)
    . u: A: G% o2 m& y
  135.        (session "board" (buffer-string)))))
    , v! U( @, n7 f0 ~- [8 ~

  136. : T% ]# q, h* t- m
  137. (defun board-init (size)
    0 w: h6 J0 Q. A# J0 J
  138.   (session "size" size)
    ( C9 q- t* w. w6 K
  139.   (session "step" 0)( w) z. {0 e! m) z. u$ g
  140.   (erase-buffer)1 R8 O* r( ?/ }  l: a& a" f+ [
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))' B9 N. M: ?( w
  142.   (dotimes (row size): I, b  i) w! y* ]' V
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    9 k, d# v6 D2 N
  144. 1 u5 `. b1 D% R+ B8 u% K7 S1 q
  145. (defun board-contains-p (y x)
    + D% N' y: C7 d5 z- h
  146.   (let ((size (session "size")))" g; U4 w, c, V' M$ [# [# Q  x' Z
  147.     (and (<= 1 y) (<= y size)
    - c7 J- h5 D1 i
  148.          (<= 1 x) (<= x size))))3 p% f: |+ o: w% D  t
  149. ! b8 L6 n5 n; s8 W1 |% o; \7 E7 U1 L, S
  150. (defun board-toggle (y x)
    7 b  B( y3 ]& z% i% D* d
  151.   (when (board-contains-p y x)
    , P9 [* e8 a6 l
  152.     (goto-line (1+ y))  m8 P# p8 ?6 k; J1 K
  153.     (beginning-of-line)% \5 M% O/ ^" |3 K
  154.     (forward-char x)
    - c* `" y3 ?; \
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    8 e4 g1 }. L- k
  156.                 *wechat-5x5-black-chess*
    : _) P+ b. x1 f* C* P# ~
  157.               *wechat-5x5-white-chess*))
    ; L# W2 ~0 }3 H- K
  158.     (delete-char 1)))
    1 E& l3 k3 x* ?0 m" N8 S9 m
  159. 5 w) v  ?' J$ L  q
  160. (defun board-put (y x)# s  K0 t2 S; w" m  Z
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))# C. k% c8 b! v- e' q; y$ M0 C) o
  162.     (board-toggle (+ y (first dir))
    & t  Y) u2 l: D. c$ k; C8 L
  163.                   (+ x (second dir)))))' ^( y. B3 B* W1 P- l$ K* u3 S7 R
  164. ' ~) ~# U- j% \
  165. (defun game-over-p ()2 T$ U1 P% k" F' u9 X3 E
  166.   (beginning-of-buffer)5 m4 h; f' B+ u. U8 {/ m% x2 _0 f+ V
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    ' M3 L: [% ]! |8 g0 W! w

  168. ; B! U' L: g2 h& c7 ?
  169. (defun board-show ()
    : n' T. t% y! o) a0 ?
  170.   (with-board( k) F0 E; ?  X
  171.    (concat (buffer-string)
    # l9 u8 |4 x' ^. V9 }! b9 \0 ^
  172.            (if (game-over-p)6 }1 x7 v+ d7 V6 m+ G) h& z
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    / K* {8 a* j, n$ E1 E7 S& K
  174.             (format "第%d步" (1+ (session "step")))))))
    + S) n& R5 z( O+ A# ]' z3 y
  175. ! K8 m& @9 M+ d
  176. (defun board-position-parse (cmd)
    3 }; U4 J8 P% F6 ?( c9 f  M
  177.   (if (= (length cmd) 2)
    2 X/ R) r1 B; X0 X2 ~1 D8 |
  178.       (list (string-to-int (substring cmd 0 1))
    5 y* y0 |8 H& f4 v4 t7 M4 p& l
  179.             (string-to-int (substring cmd 1 2)))
    1 |" |2 M# r0 S
  180.     '(0 0)))4 ]9 p' b" L; F2 s( Q& D
  181.   e4 c, B8 m# F: Q
  182. ;;; 游戏房间. \$ b+ f2 T: X5 E+ j9 a2 d6 |
  183. (defun game-room-init (cmd)
    : @& [1 ^4 a  j( t) l* C
  184.   (let* ((middle (string-to-int cmd))
    3 O# v3 ]$ F1 b7 v! i; k9 z, m
  185.          (size (1- (* 2 middle))))
    # _3 B7 @4 i$ l+ g$ Z4 R( F! B
  186.     (with-board, v) ^' H8 f+ O% @# D
  187.      (board-init size)/ d) R7 P9 [0 K' `3 l
  188.      (board-put middle middle)))5 J/ {% @0 o$ l6 K% _
  189.   'game-room)
    ! P1 D, R& a  @6 M. c/ T% G
  190. & ~( Y" t6 i! D5 s
  191. (def-room game-room
    , b& B+ m' r/ S
  192.   #'board-show
    8 k5 P  E8 e+ ?0 y) J1 B; x
  193.   (t (lambda (cmd)9 j; ^5 f% O9 K1 z
  194.          (with-board- L; }6 E. z& L, S
  195.           (if (game-over-p)
    2 @. b: l: k4 e$ R1 i, v
  196.               'living-room1 v, [$ G8 j: k( h
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    % |" e+ ]8 g% {( }9 g
  198.               (when (board-contains-p y x)- U0 F% }6 b- s5 G
  199.                 (board-toggle y x)5 D* S& F9 z/ _9 @( q+ d
  200.                 (session "step" (1+ (session "step"))))
    ) n8 |5 V# W9 j2 x; z
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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