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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
' s, b: t( D% t4 e借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. , Y+ |6 H5 Q1 W. L4 D3 N
  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;">;; 定义新的游戏地图" f  w, p0 r1 Z5 @! W
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    " h9 R. i+ U* R& u
  4.   'tutorial-room-0)                     ; 默认的入口
    9 _8 T9 F& X7 w! J
  5. 8 B. E  u& c+ E& O9 O
  6. ;;; 游戏大厅
    % K7 e. K4 p' i
  7. (def-room living-room
    : t; L4 O( @' {; t  i* A- A
  8.   ;; 进入该房间后的提示语
    . j, U0 ]' h5 ]1 i* @
  9.   "1. 教程
    ) e" Q6 u/ f+ z( H7 z& l+ u/ g
  10. 2. 入门(3x3)0 t9 n1 S2 }& a# n3 _
  11. 3. 初级(5x5)
    - g! ^; O8 |* O0 M8 R. x6 |- M
  12. 4. 中级(7x7)' u' f$ W" ?- h
  13. 5. 高级(9x9)* O1 h6 ]( I' |# E; N& J9 ]8 S
  14. 0. 关于作者7 y7 Q3 D8 L) s
  15. 请选择1-5开始新游戏:"
    6 @  E( B9 b$ J1 h! r7 i
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    3 w( @7 \6 {& A# A8 i) _! z+ H
  17.   ("1" tutorial-room-0)% q" W' I; e4 t9 [) ^: Y
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配: |( \- b8 R' g; o. |" F
  19.                                         ; 相应的返回也可以为函数,动态返回房间名2 n* I8 N3 C# v, y) z
  20.   (t living-room))                      ; 如果条件为t,为永真
    5 Q( e+ z$ {* |5 z! [
  21. 4 H3 z$ F' e" `/ p2 P4 m
  22. ;;; 作者信息% Q3 C- W7 _( H. }% F0 f
  23. (def-room about-room0 B0 I5 _; S& z4 Q6 s' x0 [. d
  24.   "作者:redraiment
    / Y" r( k3 V8 |& y, a% ~  c  J
  25. 微博:http://weibo.com/redraiment3 e2 f* h& Q" `( g" i
  26. 有任何建议,欢迎在微博或微信上联系redraiment。2 K! S, X4 t8 T$ k0 u) k
  27. 请输入任意数字返回游戏大厅。"
    : P9 p( }6 d( U( H
  28.   (t living-room))6 L, R5 S% D4 v. i# Q# p* X
  29. 9 `0 B/ l# P, R- e* e+ s: D- k
  30. ;;; 教程+ J" I% @# h4 \+ b) O0 j* r# d' s% i
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    - Q0 h3 o0 O( b
  32.   "The number of tutorial rooms")1 r" K/ Q! \0 @

  33. 9 l- {, o2 O/ Q8 b3 V7 I2 j% o
  34. ;;; 简化教程的定义9 g" o  x# O; b# d! `4 m' J7 i
  35. (defun string-last-line (content)
    2 {$ R$ I7 ~$ J; \  d) @1 `
  36.   "多行内容组成的字符串中的最后一行") a/ f' }! b. c6 R
  37.   (with-temp-buffer' ?1 V7 `& C! [7 F( S- T
  38.     (insert content)
    , q. f6 d+ E% f4 k. b/ q
  39.     (buffer-substring (line-beginning-position)  z: w/ D8 @# s# \
  40.                       (point-max))))
    + N/ d: Q+ V2 X2 `

  41. 4 R2 q5 f6 d  P9 Q/ Y& K
  42. (defun def-tutorial-room (prompt)! G; ?, S6 o  H; Z+ d# [9 b
  43.   "根据提示语自动生成教程房间。
    9 @# V$ K, E. t5 o
  44. ; K' K& b, m% m& P( v- V$ {
  45. 1. 提取最后一行作为问题;
    4 n+ y$ S! `1 a! G1 v. d7 Y
  46. 2. 分析问题,获取期望用户输入的内容;
    $ Z/ j3 ]( v& j9 W4 ?& @
  47. 3. 定义教程房间和重复提问房间。"% j/ t5 H' v3 Z- Q+ ~
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    7 N$ x5 _2 I: w2 d
  49.          (repeat-room (concat room-name "-repeat"))" z" w# c! _; n, }, I) F
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))2 w' F5 J" d0 e4 w9 A2 O) H
  51.          (question (string-last-line prompt))% D+ ?5 R- g4 d7 P2 ?
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    ! |! I& |% w3 o& B
  53.                      (match-string 1 question)))
    $ S7 E2 P: m& C5 D$ ^- H
  54.          (doors (if except. G! F' j. G, j; M( U5 ?$ I
  55.                     `((,except ,(intern next-room))6 o! l- [! t4 I  }7 A. {
  56.                       ("q" living-room), p1 r+ D& B) n: e: U3 l
  57.                       ("Q" living-room)3 i/ b$ P, u6 y( _) v9 `& u1 T- |
  58.                       (t ,(intern repeat-room)))
    , n' |2 @4 w+ J  F$ }6 n9 b  f
  59.                   '((t living-room)))))7 D9 b" Y0 }! @) B( J5 o
  60.     (def-room-raw (intern room-name) prompt doors); O; L* l0 D& h8 m/ W
  61.     (def-room-raw (intern repeat-room) question doors)))
    & F* \: c& B# c/ `8 Y& i- v
  62. % A6 S& H; S( T5 p$ V; X
  63. (defun def-tutorial (&rest prompts)
      u  h& D8 Y* }4 `: S  o3 z
  64.   "批量生成教程房间。"
    & \/ |# {3 e8 e& K7 i3 }
  65.   (dolist (prompt prompts)
    - X( K, K0 O3 b9 }* W
  66.     (def-tutorial-room prompt)))
    ! V7 D2 B, o& j, D" K

  67. / {* w  p& {8 i( V/ H
  68. (def-tutorial
    $ e/ w1 K& G9 N6 @
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。' B# w8 H$ I2 k( O* `
  70. 1. 教程6 l# a1 D1 |% w
  71. 2. 入门(3x3)
    ' W# D' x; N* C' z! E
  72. 3. 初级(5x5)) j* |" {4 [( ]# r0 f4 [' w; c9 r
  73. 4. 中级(7x7)) d# l# h- [/ I/ ?
  74. 5. 高级(9x9)5 u7 x$ w! S2 d5 ^5 g3 {, i4 w8 i
  75. 0. 关于作者
    : [7 _, U3 r, }' u6 C3 S
  76. 请选择1-5开始新游戏:
    1 {1 i3 }& w. M$ C3 R
  77. 您现在正在游戏大厅里。, o" W$ @& C, I+ ^+ g. S$ E; ?, o
  78. 请输入“2”进入入门级房间"6 e4 Y$ n2 _7 T6 {3 g
  79.   "  ①②③
    3 b) p0 l3 r  D8 l1 n5 _+ a
  80. 1 ■ 
    ) Z/ u; E0 w, o* \
  81. 2■■■$ o* G! U) W+ e( k; Y) p/ c6 A( R. W
  82. 3 ■ 
    4 I  Q( ~3 @' \/ [  e- e) s
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    0 V) S$ n7 o* G  [. T8 }6 z- S4 {
  84. 请输入“22”来关闭第2行第2列的窗户。"9 D$ b6 e) @# x0 B
  85.   "  ①②③
    9 s: ~& h  x! b8 d* m# ]+ I: F1 |
  86. 1   
    1 f0 j  W+ N7 d& o( n
  87. 2   ' K  ?; h* [1 u% Z
  88. 3     B: W, J. m) U8 P3 d7 |2 {+ h
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。! R8 X2 i/ }9 L* s! W
  90. 请输入“11”,试着开启左上角的窗户。"
    * i) B/ }6 Z; K; J7 L( ~1 P9 _/ D
  91.   "  ①②③* O% V# w& J8 v% E
  92. 1■■ * e' ?1 l' T1 j" P+ U3 Y
  93. 2■  
    6 f# h1 _) s0 r
  94. 3   
    - T) O* R# _1 _+ U: |, z6 [
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。, L9 N, d( _/ ]+ O8 L
  96. 请输入“13”开启右上角的窗户。"
    9 ~6 {" x0 R' G, S1 ]7 ~
  97.   "  ①②③
    % D, |/ `, ?/ L3 E% m
  98. 1■ ■
    - v9 V% N9 I- H0 r
  99. 2■ ■, }4 o& S! B2 @& e& j* W
  100. 3   $ j& B* z+ z7 p, p' p& p
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。1 @* f( t$ h+ g+ z9 F
  102. 请输入“31”开启左下角的窗户。"( F3 ?/ L$ m) E4 y4 O/ Y
  103.   "  ①②③- y7 X( U' H0 A- L7 Y
  104. 1■ ■
    : @: f; G2 e* ~8 L8 J
  105. 2  ■4 I0 Q: l+ p8 t( C/ p$ |& p. |
  106. 3■■ / h1 r! D" `2 F; L0 L  h, W
  107. 此时,总共有5扇窗户被开启了。
    ! k! J% U1 i) V. ?: d
  108. 请输入“33”开启右下角的窗户。"! I% b2 v  x1 Y% h
  109.   "  ①②③# p. Y, N8 c4 M: v
  110. 1■ ■0 u7 T# `9 P4 c* S( I
  111. 2   
    1 K0 O! h8 K; {6 i* w7 h9 H
  112. 3■ ■7 |7 ?/ L9 l7 {
  113. 现在,只有四个角落的窗户被打开。
    " @% s' h  p* S& r# W% T+ b2 [
  114. 请输入“22”完成最后一击!"
    ! u/ O' ?& ?7 s! |. K9 T! c
  115.   "  ①②③
    3 _7 F" F* e- Y& p
  116. 1■■■
    . e# a# l$ X! m7 d+ c
  117. 2■■■
    1 T2 J+ h+ K1 b! q! s: {$ ~4 D. e
  118. 3■■■
    0 {, A! b5 v' G$ u4 s
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")5 G0 z8 [+ ~: A2 q. q; h. k9 a

  120. , W3 U; ]# {! |1 p) o  }  _6 K
  121. ;;; 棋盘
    5 I8 Z3 h  F; t+ h" G, U
  122. (defconst *wechat-5x5-white-chess* 12288
    - d; J1 }. Y8 w, @  z6 K. j+ K/ E
  123.   " ")% |2 }. \' a0 Q& q- P5 U% q

  124. / h- z3 ~% I. I  V9 ^* D
  125. (defconst *wechat-5x5-black-chess* 96322 O8 u0 @/ g8 T. n  v+ o
  126.   "■")0 x' I, q- D5 c5 I# g0 N: `6 g0 M1 c
  127. 5 }  ^4 n7 B; W& c: e5 c9 d0 j
  128. (defmacro with-board (&rest body)
    ' {% B& q7 u* b
  129.   `(with-temp-buffer
    - g1 A- b9 Z2 q* Q& C# Q# X
  130.      (unwind-protect
    - A( A: E% v3 }8 f& _
  131.          (progn
    ! f; u, q* a' x
  132.            (if (session "board")* d1 K5 p* Y$ W8 H: m3 h7 f
  133.                (insert (session "board")))7 s+ d! y8 W' y
  134.            ,@body)
    ) c8 G/ V% `  v- ^9 r: q7 s+ F
  135.        (session "board" (buffer-string)))))
    $ H* x8 @7 ]4 y( C7 S7 D1 I
  136. 5 M$ ?4 O$ [; D4 H4 @
  137. (defun board-init (size). Q( v. J* F3 T+ z" Q
  138.   (session "size" size)- Y. G' ?2 B% j; j: R
  139.   (session "step" 0)
    ) m! X+ Y( D6 |) A0 ?& U8 {
  140.   (erase-buffer)
      `# G5 Z" I# i9 Y% e
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨")): y/ I' S7 v. U) |
  142.   (dotimes (row size)
    4 m9 b  Q( _' \
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    5 F, Z' j7 F. W, T1 A
  144. 0 I, v0 y/ B5 O* n* m7 O
  145. (defun board-contains-p (y x)
    ( f9 _* E/ l; b, s
  146.   (let ((size (session "size")))
    ! q" S- S+ H. v6 ?+ D9 t- h
  147.     (and (<= 1 y) (<= y size)) o, x# [: q* [! z! W
  148.          (<= 1 x) (<= x size))))
    / u; p1 b4 h& {! z7 e( d; @

  149. . v6 m7 z- \! F  i/ p
  150. (defun board-toggle (y x)
    / p0 Y) H* O4 e  i
  151.   (when (board-contains-p y x)
    * \6 N2 l4 ]# L. [/ y! z
  152.     (goto-line (1+ y))
    , [3 q- U( V9 D# I0 B& [$ m
  153.     (beginning-of-line)' K! e7 {8 f7 e" c, u
  154.     (forward-char x)0 ]& Z5 Q0 b( _% w8 f
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))) p# [' o/ j# k! w8 [
  156.                 *wechat-5x5-black-chess*
    ' O0 n5 E' V( a0 p/ X/ U- Y% q
  157.               *wechat-5x5-white-chess*)); E9 N$ N6 b# p% b- y8 u$ q! g+ }
  158.     (delete-char 1)))4 p2 N, ?, ]+ U3 k; g" D
  159. 0 [- Y* x* V' _* J4 K
  160. (defun board-put (y x), ]9 G/ r6 Q8 a
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))1 w9 C1 Z- m* {1 a" v
  162.     (board-toggle (+ y (first dir))
    ) @/ Q1 @$ O6 k1 v$ d
  163.                   (+ x (second dir)))))1 o: A, D6 a+ e. g* E, l) _1 u5 L

  164. : V: n+ O1 G, ^3 N6 M; M
  165. (defun game-over-p ()
    / D( ~" N( ]; c+ @
  166.   (beginning-of-buffer)& g1 Y8 @$ {* T/ n8 D! H
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    ) O) H/ q1 e5 m, ^( q2 i5 g

  168. ) z/ {. F# x1 ^, L
  169. (defun board-show ()0 J" F9 m, z8 v7 A0 G
  170.   (with-board5 `) _/ l' e. c9 o8 X
  171.    (concat (buffer-string)* h' r/ y+ u. z
  172.            (if (game-over-p)
    % z4 ^- ^; }8 U/ p, R& O
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))* O3 a2 n0 c) y6 }
  174.             (format "第%d步" (1+ (session "step")))))))
    ! Z  \0 g5 \0 f5 l" B- w! N/ C
  175. " s8 l: H4 a1 S
  176. (defun board-position-parse (cmd)  v. m# @# M) x# {5 ^0 |  P$ O
  177.   (if (= (length cmd) 2), h, w; E% [5 R$ A9 [9 f
  178.       (list (string-to-int (substring cmd 0 1))
    ! F* m1 I1 u) r$ u3 C) G: M
  179.             (string-to-int (substring cmd 1 2)))" B5 \$ E7 ~. U' T5 A
  180.     '(0 0)))
    " D: x5 h7 {8 a8 c& l! t
  181. 5 z+ [8 N  P! ~, A
  182. ;;; 游戏房间' u; G' N9 o  g/ A" c4 |
  183. (defun game-room-init (cmd)
    7 ?1 x4 T. W9 \$ q
  184.   (let* ((middle (string-to-int cmd))
    8 O' Q# r) W8 m' e6 b
  185.          (size (1- (* 2 middle))))( Z6 @- T4 m4 w/ Z0 ~- [/ H
  186.     (with-board) S6 P  g( \. k2 O
  187.      (board-init size)
    * g$ X4 {; ^- j. B
  188.      (board-put middle middle)))
      z/ C3 g! A+ N" E# C8 U1 Z
  189.   'game-room)8 T# I# z0 @% |( Q, O- m

  190. - U$ R/ k& u6 H1 N5 e1 _: `6 S6 v
  191. (def-room game-room# v" l) {+ p! r( b9 O& D/ K9 @0 _8 e
  192.   #'board-show
    - c& [- Z$ m+ b8 h) R
  193.   (t (lambda (cmd)
    9 }  I2 B) {  ], ?/ m6 s6 h
  194.          (with-board
    6 Z( V$ {0 r+ ]6 y" \
  195.           (if (game-over-p)$ [+ L( Z9 v( @& \+ F
  196.               'living-room6 F% x/ }/ i. m7 l4 M" m* H- P; e9 V
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    $ i- e# O; p/ H" I6 @/ W; b
  198.               (when (board-contains-p y x)
    # X8 f0 ^) r2 d+ i5 S5 A
  199.                 (board-toggle y x)9 a: G* C8 ^' M, Q
  200.                 (session "step" (1+ (session "step"))))- v$ r/ D5 ^8 B+ R7 y
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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