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

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

  1. # t! j8 n8 \4 x1 y& {, i: 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;">;; 定义新的游戏地图' h) r1 A+ m3 y( ^. x# j7 M
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    ; ^9 F- H; P: Y8 z: }
  4.   'tutorial-room-0)                     ; 默认的入口
    4 n% V8 I% t+ N0 q/ z9 O! E
  5. 6 O5 a) ?! k" N+ Z
  6. ;;; 游戏大厅3 Q. U" [# B( t9 x; o4 y' O+ G. i/ \- `
  7. (def-room living-room0 g$ l2 k, }. p7 a2 B
  8.   ;; 进入该房间后的提示语
    2 I2 F: |& |$ F; ?! {
  9.   "1. 教程5 f% {( p! f; ]
  10. 2. 入门(3x3). c# q" ]# g( b! }; W# \$ ?' R; ^
  11. 3. 初级(5x5)* {( K+ b: @, _' C' P
  12. 4. 中级(7x7)
    2 Y" c! u5 W0 F% `2 C+ M: d
  13. 5. 高级(9x9)
    0 x2 |% U" E7 O2 \: F' C
  14. 0. 关于作者
    , _7 _  C* O/ n# W* G2 C
  15. 请选择1-5开始新游戏:"
    & A+ w$ J: W% w1 H1 z, H  T
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    " I$ \) H, {$ E
  17.   ("1" tutorial-room-0)1 ^/ j/ N6 C% f0 E/ t
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配3 T+ |) R9 [5 }% \% ^3 y7 f
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    2 k. J8 r$ B0 h( H: Z
  20.   (t living-room))                      ; 如果条件为t,为永真, }+ \4 {, K" F, M

  21. ; _# J3 V2 \; C0 s' e1 J
  22. ;;; 作者信息
    5 r- `9 w- M8 [+ B
  23. (def-room about-room" W# ?' o8 u2 X5 `. k0 ?  m
  24.   "作者:redraiment2 o0 h: Y1 V3 m# F( \. ^  m6 ]
  25. 微博:http://weibo.com/redraiment( F+ @7 x! `0 {
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    , M. S# u  J5 v  z% M
  27. 请输入任意数字返回游戏大厅。"
    ( O2 N% T, v6 b* y. s  c7 \
  28.   (t living-room))
    6 ]4 m7 d/ T' D$ M

  29.   K1 }) ]# a/ S
  30. ;;; 教程+ ], W2 Y9 q2 Z8 A" Y+ }
  31. (defvar *wechat-5x5-tutorial-rooms* 0: d4 T3 T# D0 O( m0 y" V
  32.   "The number of tutorial rooms")
    , |# {9 O2 c& o: s2 B. p
  33. 5 c, a, }  V( v0 H7 `3 P/ u
  34. ;;; 简化教程的定义3 [! M6 A% R$ x
  35. (defun string-last-line (content)
    1 ]0 _4 \& D4 w- I
  36.   "多行内容组成的字符串中的最后一行"1 g' x8 B! T' {1 A$ ~! z
  37.   (with-temp-buffer0 Q: W- m! ~! i& t& w
  38.     (insert content)0 h1 \& k' W8 o3 j
  39.     (buffer-substring (line-beginning-position)
    9 t+ {  W( C  ^8 D9 w
  40.                       (point-max))))$ k2 r" f' G# B7 R6 a! j
  41. 2 G5 Q, N: E  D5 D4 q
  42. (defun def-tutorial-room (prompt)7 _' Y+ w3 h9 }8 {# B
  43.   "根据提示语自动生成教程房间。
    ! P) V: z3 Q4 K1 e8 W
  44. # a+ W% y- k% l6 B, s+ P; }
  45. 1. 提取最后一行作为问题;9 n7 ?/ f: i$ B. T9 q7 {
  46. 2. 分析问题,获取期望用户输入的内容;
    - R. I. y/ |3 R+ I! C6 q* Q
  47. 3. 定义教程房间和重复提问房间。"
    # |6 q2 [" T0 O9 r1 U& g0 G
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    4 T% a$ W3 K( l+ O6 [% H# O
  49.          (repeat-room (concat room-name "-repeat"))  u, n+ f$ ^; ~& l2 ~/ w
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))2 p3 h" V3 z# i" \& R7 i
  51.          (question (string-last-line prompt))
    1 x+ N, C5 o& |2 l0 ?
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)0 I4 E5 K. w1 c$ q, z. S
  53.                      (match-string 1 question)))
    . D. U2 g* h4 I% B" G0 m$ b+ R
  54.          (doors (if except
    ! S* Q, x; `9 n& X! s  Z5 b! b
  55.                     `((,except ,(intern next-room))( ]6 x! H) C4 j
  56.                       ("q" living-room)0 t6 Y( S" i& D6 b
  57.                       ("Q" living-room)
    ; i: g& e+ b. a# d3 `9 w6 S! r
  58.                       (t ,(intern repeat-room)))5 u* x! H0 I/ j/ s
  59.                   '((t living-room)))))
    - i5 U& K" `# E. R
  60.     (def-room-raw (intern room-name) prompt doors)& h, w) Z1 A: j: |! K+ ~1 B% [9 Y
  61.     (def-room-raw (intern repeat-room) question doors)))
    ( I" P$ Z% Q$ \

  62. ) k' Z& k5 q7 ~  W
  63. (defun def-tutorial (&rest prompts)
    7 W5 m3 H/ u/ p& }
  64.   "批量生成教程房间。"( s0 B" F* Z( Z+ D
  65.   (dolist (prompt prompts); z; @1 _. P0 V2 S/ L* g! L
  66.     (def-tutorial-room prompt))); X, _- A4 F) u( E5 e  G8 N8 m

  67. , L( l, L. R/ o0 a/ c% X
  68. (def-tutorial5 |; {! N- V3 x8 x
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。5 T0 m" ~4 C. C9 c+ L! g+ }! N8 @! `' x
  70. 1. 教程4 K# x2 r; i4 {) u
  71. 2. 入门(3x3)
    & G# G$ g3 G& h# s
  72. 3. 初级(5x5)$ Q) h7 q8 G" S8 O' W$ {) U  z$ P
  73. 4. 中级(7x7)
    $ _# P+ ~) Y! d
  74. 5. 高级(9x9)
    ; l8 `( }; o! a
  75. 0. 关于作者
    5 m8 t. b2 V1 h4 r8 c9 ~$ k; I
  76. 请选择1-5开始新游戏:, T" N5 s- ^* a1 s1 Z
  77. 您现在正在游戏大厅里。
    7 c9 g; F; {! u4 C4 w
  78. 请输入“2”进入入门级房间"
    $ c! f! \4 c& s* z& [
  79.   "  ①②③* B' `8 ]" t% L  i5 h
  80. 1 ■ 
    ! ^& p) z7 G" p" x/ I
  81. 2■■■
    , s# J' J: |& q% _' r4 G% d; q
  82. 3 ■ 7 X, v) W9 c& R7 f' M& ]! Y6 A
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    5 d# k- ~, X& X
  84. 请输入“22”来关闭第2行第2列的窗户。"# q  @( W" L+ K$ ^+ _5 g6 N$ H9 L0 ~* `
  85.   "  ①②③
    6 Y0 G- `8 m% {; Z
  86. 1   2 M8 w% C0 a! j6 t
  87. 2   
    * a1 n1 q* r4 Y8 I% }' v, b9 l
  88. 3   ' l. A7 \# |* X+ J  r. z
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    1 G' ?( T* |9 B. j* J
  90. 请输入“11”,试着开启左上角的窗户。"* z& G$ v  r: Q, A, A8 ?
  91.   "  ①②③- ]7 B; \% k" x& m) L
  92. 1■■ , B! u/ ]; J+ V. x
  93. 2■  $ f' Y- F( k1 q$ ?1 n6 D' l# [8 `0 e
  94. 3   # t, K+ z1 V& {# w
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    8 F1 T# i0 x# K! `. H% r& V0 j: g; b
  96. 请输入“13”开启右上角的窗户。"
    ) W' }1 W! @7 h( l  j( v9 ?- n9 {6 u
  97.   "  ①②③
    / C: V  h: ?* ]8 g' g
  98. 1■ ■
    # Y; G3 z( |& g; V
  99. 2■ ■
    7 M# g. \6 H2 q8 }3 H* ~
  100. 3   4 x9 ^$ z5 `8 b+ U7 a
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    9 }' _4 A( S* d* v9 ?! P0 t3 w7 J
  102. 请输入“31”开启左下角的窗户。"
    ; f" c2 [# h& F9 j( L# {
  103.   "  ①②③) S+ P  H4 n& ~$ A1 y, I
  104. 1■ ■
    4 G1 i5 ?6 C) g9 h: m
  105. 2  ■- n7 E8 w+ O2 X8 j4 _5 @) f
  106. 3■■ 3 T! J' Q3 E& A2 |9 I* `$ e% u# i" O
  107. 此时,总共有5扇窗户被开启了。
    & |' z) a6 V/ n; F/ ]8 Z% o$ G
  108. 请输入“33”开启右下角的窗户。"# J! n$ y/ J  \# S! z2 h
  109.   "  ①②③
    - `# Q+ v2 b! R+ ?2 `. ?6 I" B
  110. 1■ ■: S7 @  @  G8 c, ]& k
  111. 2   ) u3 {4 E3 V& s! j+ a
  112. 3■ ■0 C# n1 h" Y$ w
  113. 现在,只有四个角落的窗户被打开。
    , y: \" M; O4 {- z
  114. 请输入“22”完成最后一击!"
    ' e- ?  b- f7 ?/ W
  115.   "  ①②③
    1 @6 A* Y+ o6 _  i# q/ {7 k
  116. 1■■■8 C0 f& a2 f3 h9 p/ B1 N" ]5 D
  117. 2■■■* O1 w, B: Q$ i" U
  118. 3■■■
    # B8 F$ A- K6 u8 ]: ]( V
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    3 d4 q' D% D( A- ^/ v/ B; ]
  120. 4 l8 L' i5 G8 B, P: X6 s. I2 a
  121. ;;; 棋盘
    ! z8 g9 o6 }4 ]$ n: d/ ^
  122. (defconst *wechat-5x5-white-chess* 12288
    3 d7 t! |7 a# ]" \/ m9 D# ^
  123.   " ")
      w. ]# s" E7 m1 o# }! w
  124. 7 w, ]' P' N, k. i+ S/ u8 \5 H) v" S8 b
  125. (defconst *wechat-5x5-black-chess* 9632# h0 R0 g) d, x) ?. v
  126.   "■")
    ! l& _* |7 d  ]. t( P5 f

  127. # G; P7 I& |" o1 n5 W# a. {
  128. (defmacro with-board (&rest body)" E9 A" t( a) |4 _9 ?, E. `" J6 {
  129.   `(with-temp-buffer; b0 ^: h! o0 F! b- L# }
  130.      (unwind-protect7 K& n+ v0 E; R5 ^: X4 x8 M( c8 T
  131.          (progn
    2 _, f% w4 u& B
  132.            (if (session "board")8 `6 @$ B" N9 j: ~, `  g0 N
  133.                (insert (session "board")))
    % b6 C2 F. `- U! ]: O
  134.            ,@body)
    2 k! d7 s* l7 o3 x9 U0 k, Z
  135.        (session "board" (buffer-string)))))1 E4 G+ t& ?, v, {. W) Y0 |
  136. + L' r$ e# y. U
  137. (defun board-init (size)5 x0 O3 g; P5 ^
  138.   (session "size" size)5 p5 F, D7 A$ I2 }8 v
  139.   (session "step" 0)
    4 K2 m- N: i) g2 ^% B' E
  140.   (erase-buffer)# r# V/ E$ `9 y  X
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))3 F$ Z0 x- R  D7 y# W+ g, D" X- I1 \
  142.   (dotimes (row size)& Q, w4 y( |4 o4 m; x; s
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    . W; s! Q" W4 F( Y
  144. # w* ?+ a* S* i
  145. (defun board-contains-p (y x)+ I8 O' [, E8 z5 ]; r8 e
  146.   (let ((size (session "size")))
    ! u' n, e: l3 [
  147.     (and (<= 1 y) (<= y size)
    ! f3 j7 K. q& W- D' D% F
  148.          (<= 1 x) (<= x size))))% V* F2 A- _0 u. w
  149. ) N5 y6 V4 N7 u
  150. (defun board-toggle (y x)
    $ r( W, _8 L) T/ t$ j. X
  151.   (when (board-contains-p y x)0 W8 F& ]1 G& E
  152.     (goto-line (1+ y))+ t& F6 ?! B# }. F8 `3 a
  153.     (beginning-of-line)3 P9 I. l  b& R2 w. T
  154.     (forward-char x)
    * r4 [7 x& C2 [' p$ e. h
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    - F1 k8 h5 u2 W7 [8 h
  156.                 *wechat-5x5-black-chess*
    ) ~* L) `8 K. ]
  157.               *wechat-5x5-white-chess*))
    9 x8 S6 {: a2 b$ Y
  158.     (delete-char 1)))
    $ H. d/ ?8 W# F3 e
  159. 6 b4 C! H0 A) q+ w
  160. (defun board-put (y x)
    - z; V! F; D9 E9 K! O3 y
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))3 E' x0 C) o7 Z, B0 x9 r1 T
  162.     (board-toggle (+ y (first dir))/ w. ?' d( E  b* j: R" |3 g
  163.                   (+ x (second dir)))))
    5 ]( X9 w+ H* g- H4 c5 `; M  ^/ j

  164. # q  H' m7 Y( I2 G" p: l
  165. (defun game-over-p ()
    " ]  x/ |) q" v. ?& [- V
  166.   (beginning-of-buffer)
    ! X) J' _- _3 H6 ?; ^# a
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))/ o$ m( F, [* Z0 `6 r8 S0 d# q

  168. 1 f5 m$ P8 h0 u3 p* }
  169. (defun board-show ()
    3 Y% {5 U! @/ I  w4 E
  170.   (with-board
    . K# s, n8 v" s
  171.    (concat (buffer-string)
    8 A8 c( ~1 Y$ a/ m
  172.            (if (game-over-p)
    6 o8 t  J. l: e8 r. Z
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    2 w& p) Y  T1 B2 @+ Z, q4 ^* k
  174.             (format "第%d步" (1+ (session "step")))))))) M( y+ Y; T, z7 Y2 ^' m2 |5 p

  175. # i0 w* w3 D& B9 H& {) e# t
  176. (defun board-position-parse (cmd)+ \1 T" e: ?7 s3 l. {0 r0 k
  177.   (if (= (length cmd) 2)
    2 n7 V+ i0 W# T- C( _/ k- r, B
  178.       (list (string-to-int (substring cmd 0 1))3 i+ k# Y3 G' I( A
  179.             (string-to-int (substring cmd 1 2)))
    , m( C9 G7 w( |9 }3 g8 T& D: c2 O# [
  180.     '(0 0)))
      a8 @; ?1 w" K# d) ^+ L- k
  181. / ?- ?# }- ^+ q" }7 t
  182. ;;; 游戏房间$ y/ x: W  i( O' }/ [) l
  183. (defun game-room-init (cmd)
    ; N, ^8 a9 a3 R8 a
  184.   (let* ((middle (string-to-int cmd))$ U7 Q, F* l% c+ _  |, a5 @; I
  185.          (size (1- (* 2 middle))))
    - s2 m7 |8 e' z/ F9 d! }
  186.     (with-board, n; `# r! p  x3 h
  187.      (board-init size)0 o, l9 u, o% Y# f
  188.      (board-put middle middle)))
    % k/ j% G, Y9 X' A
  189.   'game-room)& S. D! n. m, o$ i: W- w

  190. + n( z/ _1 P$ Z( X' D, T3 k. n' X
  191. (def-room game-room
    , h: p4 V& x3 T% l4 B
  192.   #'board-show2 o7 N: k/ b7 d8 g
  193.   (t (lambda (cmd)
      n3 S9 n% e) X+ b7 c- l$ C
  194.          (with-board
      m+ m& w* x2 h  K8 @
  195.           (if (game-over-p)
    0 }2 F: G4 n5 ^  U
  196.               'living-room8 L+ C4 _* v/ ?- E
  197.             (destructuring-bind (y x) (board-position-parse cmd)8 `2 v: b' E5 }) [1 i/ q
  198.               (when (board-contains-p y x)
    ( Q5 o" ^) m: N; {: d1 S
  199.                 (board-toggle y x)
    7 _, i: x" m2 ^* v  U+ y
  200.                 (session "step" (1+ (session "step")))), z2 }+ }2 c4 h4 u6 ~7 u
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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