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

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

  1. + M/ D7 U' h! F" {# G1 e9 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;">;; 定义新的游戏地图& [( r( {$ [  w5 Z& K
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    & p7 L/ n, ?, F9 ^2 h* ?
  4.   'tutorial-room-0)                     ; 默认的入口
    1 s7 A8 g3 l* H5 F) @
  5. . B; `: h; f' V: f8 l. o
  6. ;;; 游戏大厅1 J8 z# C+ |, H+ p5 F3 S
  7. (def-room living-room2 X3 N; @+ I& o  x1 V6 D' z
  8.   ;; 进入该房间后的提示语% q) l: }2 O4 |
  9.   "1. 教程
    ( E2 p$ M5 `+ }, |
  10. 2. 入门(3x3)% `3 p# k) ]' A4 {
  11. 3. 初级(5x5)
    - F. U" K) q3 R+ o3 v/ e. M* Y
  12. 4. 中级(7x7)
    1 D: o! o- ]/ p) y! F1 L$ H
  13. 5. 高级(9x9)1 V' o2 C" j* @2 n( S, U
  14. 0. 关于作者
    # {5 i/ M4 Y+ {  E7 x6 H
  15. 请选择1-5开始新游戏:"5 J5 k0 G5 G7 a
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名3 }$ P& Y. ]* T
  17.   ("1" tutorial-room-0)' P) z2 I7 u& C, n2 O/ Y7 c
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    % W, S8 T! ^( c( Y) m1 b" Y$ C# A
  19.                                         ; 相应的返回也可以为函数,动态返回房间名" Q3 }& |' K: J$ D
  20.   (t living-room))                      ; 如果条件为t,为永真- a, v* ]; r$ ]+ y/ T' d$ H0 O+ J0 f

  21. 4 L  d% X0 ?1 m8 x: H
  22. ;;; 作者信息
      D4 `0 w7 `, H! ?& `8 T5 v) T2 F1 J
  23. (def-room about-room
    1 N, K7 K! R! b
  24.   "作者:redraiment
    8 D; @- N3 r7 C5 n  a
  25. 微博:http://weibo.com/redraiment# }! ?4 |. x8 V1 C" L
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    1 M: z, g) d5 j. C- X7 `- E
  27. 请输入任意数字返回游戏大厅。"+ {; [/ g7 M' q. Y
  28.   (t living-room))
    ) X: h# f: s# t* a6 d9 G
  29. ' i) U/ K9 S9 u. C% y; H
  30. ;;; 教程
    ( y. U% `; t9 @6 c
  31. (defvar *wechat-5x5-tutorial-rooms* 0' r" i: Y2 ?2 N1 d2 ]3 ^
  32.   "The number of tutorial rooms")1 Z6 t; e' f2 k+ M8 y& B
  33. ' o8 |  g8 A( b5 o/ u6 C
  34. ;;; 简化教程的定义9 p/ g/ C' Q4 ]0 T+ e+ i
  35. (defun string-last-line (content)& ~( I: ]/ P; P$ ]  y
  36.   "多行内容组成的字符串中的最后一行"2 r6 c4 `4 }; A# K# w; @1 v
  37.   (with-temp-buffer# s" Q+ o/ \) ?$ ]9 K+ n
  38.     (insert content)4 O2 u' t, U" x. w, Y- l+ X
  39.     (buffer-substring (line-beginning-position)
    - p8 ~. e/ {" w0 Z( _
  40.                       (point-max))))
    2 |% _$ h! N0 o; J5 n2 v; m; W

  41. 8 @8 _/ ]; q% @7 u
  42. (defun def-tutorial-room (prompt)8 Q2 O$ Y: E- e4 `1 S( k
  43.   "根据提示语自动生成教程房间。
    4 S, n1 F( ~2 P; g5 a

  44. , O$ E: e) V* y3 P
  45. 1. 提取最后一行作为问题;
    $ y3 M( i& l7 v! U+ A
  46. 2. 分析问题,获取期望用户输入的内容;
    ) U4 J1 J& [( E
  47. 3. 定义教程房间和重复提问房间。"/ q8 D6 d7 b# z6 k
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    . r& S. o  Y& `( j$ G+ v- ]
  49.          (repeat-room (concat room-name "-repeat"))
    # L$ W3 f; O) [+ W5 u: I
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    . V4 n; z. U0 a  m! K/ u& E+ C6 c4 J
  51.          (question (string-last-line prompt))" J- j5 v4 j6 D2 y7 N4 c; D
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)) S, g* U9 x/ I3 k, a
  53.                      (match-string 1 question)))% l/ Q" a+ [8 A
  54.          (doors (if except
    - q' H# C4 e# x' J6 J' d
  55.                     `((,except ,(intern next-room))
    / K5 @( O2 U, P; b1 n5 H5 W( b
  56.                       ("q" living-room): \2 Z$ l: x$ D  I; c; {- i
  57.                       ("Q" living-room)
    . ~- I- e; @) f* a: z9 O
  58.                       (t ,(intern repeat-room)))! c8 u- V: x9 M7 b' t) r+ r$ e. P# Z
  59.                   '((t living-room)))))
    6 R. N8 G& \  F! X+ d
  60.     (def-room-raw (intern room-name) prompt doors)/ s4 w8 B* w3 G" d8 a: `
  61.     (def-room-raw (intern repeat-room) question doors)))' }6 W1 V! C+ q0 A# x  }9 f$ B
  62. 6 k$ }& x, |8 V: u$ @' B
  63. (defun def-tutorial (&rest prompts)/ ~; w$ s4 l" g  F! `
  64.   "批量生成教程房间。"
    / t  z! b/ C9 q( x; p& m$ {4 W
  65.   (dolist (prompt prompts): e$ O/ O7 B9 r' K5 Q. V7 i2 s( `
  66.     (def-tutorial-room prompt)))
    & M: g! ]8 a9 _9 i% Q4 Y
  67. $ n' b, P- _# J& r' U4 c3 _& v
  68. (def-tutorial
    3 G- z. ~, z' _, M$ Y' s0 A7 h
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。1 @! b! P  f" r
  70. 1. 教程
    " S: Q: b1 q! s( j2 }" C4 \
  71. 2. 入门(3x3)
    + c6 p" G6 f. B# l. g# V
  72. 3. 初级(5x5)( m0 X* f% R/ p8 N" L, y
  73. 4. 中级(7x7)( d0 Z" |# B5 O+ E  h* f: u; f/ i
  74. 5. 高级(9x9)
    + ]0 U. n2 h8 O' v- Z3 N4 L3 h1 q
  75. 0. 关于作者) M; k! U  e) i/ R' ?
  76. 请选择1-5开始新游戏:4 v2 p9 Z. `& p: u
  77. 您现在正在游戏大厅里。+ j4 }7 v3 @9 _7 I
  78. 请输入“2”进入入门级房间"
      V' Q- U4 P2 p& Y
  79.   "  ①②③
    ! z6 t8 g) q" P- d
  80. 1 ■ 
    * Y) @* r: V# p" U9 e  c
  81. 2■■■3 ], R/ @. N( e7 |2 w2 C: y
  82. 3 ■ / g9 N9 X) J  v' m/ X$ _
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    * N. ]$ ]3 ]9 b3 @) b  a
  84. 请输入“22”来关闭第2行第2列的窗户。"- Y) \  E  Z* \# Q5 S
  85.   "  ①②③7 g: ]& ?; V! e: Y" c7 g
  86. 1   . `- [2 Q* @" F. X+ U
  87. 2   # ~  ~* |' X# k8 ]! ~
  88. 3   ( \7 ^  s. l, G; a7 G6 B
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    $ h3 m" A5 o1 z* D3 R
  90. 请输入“11”,试着开启左上角的窗户。"
    1 Q5 K/ l, B3 f
  91.   "  ①②③
    ' B; f5 ~, n6 G& [  P- x
  92. 1■■ ' d3 H' n1 i: d! h: o# @* r
  93. 2■  
    : F7 [5 y% [1 ]" w8 l
  94. 3   , }1 C5 A+ k; t+ C# L
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。" m0 T8 T4 \3 \8 v
  96. 请输入“13”开启右上角的窗户。"6 H" M7 Z7 |& }: M# P
  97.   "  ①②③
    . ^2 k( {1 X; N3 _3 e8 d7 d" ~
  98. 1■ ■
    " a9 L. j* C4 P. A, J+ R
  99. 2■ ■
    + Z3 c  p+ @/ W2 L0 n( m
  100. 3   
    5 y2 U7 G; n4 Y4 q" s9 A% y9 \8 P2 L
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    : N4 X: q$ z) u0 D  L7 A. d$ X
  102. 请输入“31”开启左下角的窗户。"
    2 c" _  }8 S" C9 d3 P; B2 w
  103.   "  ①②③& B  Z$ B* M1 q7 ]# B" R
  104. 1■ ■4 ]7 G1 P" R; x, V& F
  105. 2  ■
    , v) M( Z0 `: m! P
  106. 3■■ 
    2 a. ]3 n  O7 P# V3 n
  107. 此时,总共有5扇窗户被开启了。- ]! R2 X* r. s2 H
  108. 请输入“33”开启右下角的窗户。"
      y$ R; i; b0 W) u
  109.   "  ①②③
    8 @/ \" d0 c# f8 j$ g7 a4 L
  110. 1■ ■$ i  N; g$ {$ c5 b' ~1 S
  111. 2   
    % I& w* K1 o  A: f- ]
  112. 3■ ■
    ! W2 _! M2 M- e" m, A
  113. 现在,只有四个角落的窗户被打开。
    7 b, B5 N: M& |% q- T& g6 g
  114. 请输入“22”完成最后一击!"
    , s# L- Q; I5 j6 a' {
  115.   "  ①②③
    / a* g, N& D& G  I
  116. 1■■■2 O* O" s- A$ k( |+ {
  117. 2■■■
      [# t. F/ C, Y& N
  118. 3■■■
    + r4 Z: w  n, j- ~/ J) ^3 A) {& c0 B
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    5 U/ i& G- q6 U1 z% |0 e- ~

  120. - U; z/ H6 w" M' s
  121. ;;; 棋盘
    ) I. H8 i  ~  b8 C& X
  122. (defconst *wechat-5x5-white-chess* 122883 L4 P9 B) a$ F, p" v
  123.   " ")" S1 H$ Q# J/ m0 D) z8 u0 u$ a

  124. ( b4 x( T" ^+ g- f7 ]
  125. (defconst *wechat-5x5-black-chess* 9632! t8 g' U$ k- e
  126.   "■")5 T) U/ Y: g3 c: Q4 e
  127. / G& M  M& n3 A( r
  128. (defmacro with-board (&rest body)" X# A* N" t3 E" ^. E9 q
  129.   `(with-temp-buffer
    0 P; V/ T' z, k- }7 Y
  130.      (unwind-protect
    " j3 Y) v" N4 b  f3 p
  131.          (progn
    ' ^7 b2 N/ S6 u/ l) c
  132.            (if (session "board")
    2 B, S2 r$ b* j3 L* l: o& V4 [
  133.                (insert (session "board")))" G9 d  K" o, N0 c  E* ^
  134.            ,@body)
    3 x1 C. A" w3 A9 M
  135.        (session "board" (buffer-string))))), \# _; d9 m! M' p+ D1 {! L

  136. ! W2 ?, c- n0 q  a3 }
  137. (defun board-init (size)
    : `7 g6 a! V0 N; C! J+ h6 n4 L4 g) x
  138.   (session "size" size)/ m9 r6 r3 ~3 y5 \" n
  139.   (session "step" 0)
    6 W! Z1 z4 i, _# c8 z
  140.   (erase-buffer)
    8 i" p2 r+ G2 G" C0 y4 U
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))4 |' T. p4 \6 d3 ~0 r$ X6 y" n1 g
  142.   (dotimes (row size)
    ; V5 Z& O9 O; \0 O7 h1 {- R
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))9 Z# n; v; ~6 N: ~3 ]4 d' L: [
  144. " p$ _$ U9 F6 x) l
  145. (defun board-contains-p (y x), t% u. z, F/ i8 F" y( z6 w# r& R
  146.   (let ((size (session "size")))- Q) q! l/ G) x! n. n/ v- ?
  147.     (and (<= 1 y) (<= y size)
    5 W2 r% @! ]. W: c6 h
  148.          (<= 1 x) (<= x size))))3 M; n! }3 r% f0 Q/ C2 m( M/ _2 }

  149. . E: e5 W6 ?) y3 p8 U
  150. (defun board-toggle (y x); E; g! M, T  _5 S$ p1 C: ]
  151.   (when (board-contains-p y x)
    ) D9 G, [' z! l  i( e4 C
  152.     (goto-line (1+ y))8 w2 a5 \+ w: P) a+ k, [
  153.     (beginning-of-line)
    1 {) J6 |2 h8 y8 Z
  154.     (forward-char x)" ~8 |8 {' w* F6 S# u
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))& B; x- ?. N/ t- R" U7 i; v
  156.                 *wechat-5x5-black-chess*
    3 \. Q# v/ S( j3 R  B# M
  157.               *wechat-5x5-white-chess*))
    7 U% _, b% a! }# e7 |
  158.     (delete-char 1)))3 Z. h- A! Q5 A, P# q% V' U2 d

  159. 9 v' G! f( y# M2 z0 h3 v
  160. (defun board-put (y x)4 e# W& ?( c4 A& H/ u! S. S
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0))): D9 _8 ?7 c7 \, r, N
  162.     (board-toggle (+ y (first dir))
    2 {2 ?4 V- R3 }  d4 u
  163.                   (+ x (second dir)))))
    9 h9 y# C& Q( S1 s4 G8 ?; {

  164. % b4 U5 W* ~; R% s
  165. (defun game-over-p ()* c. m0 {' n& Y7 k5 f* \8 {) i
  166.   (beginning-of-buffer)* N& R+ Y& g+ P. U, r
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))) V: ^4 R; g3 Q! T& X3 f# X

  168. : [0 H7 ~$ D0 w1 u+ B2 B2 N
  169. (defun board-show ()
    4 T0 L' ^- [3 r9 e$ W. J( O, n( W
  170.   (with-board
    . G% {( U8 J* D! Q& p6 l5 P
  171.    (concat (buffer-string)
    4 z4 x0 S: o0 s1 @
  172.            (if (game-over-p)
    6 [+ @: I4 d- b; F
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    ' ]& n2 A+ b* Z# I, J# z9 v( p
  174.             (format "第%d步" (1+ (session "step")))))))5 F; h& J: {& S& P$ i; Q

  175. 4 n' y+ E; o7 I. V- d8 l
  176. (defun board-position-parse (cmd)9 L4 Y5 z0 Y  b; ?) f, I2 i
  177.   (if (= (length cmd) 2)! [# E3 N" q$ n. t: a
  178.       (list (string-to-int (substring cmd 0 1))
    - ~% A& T" a0 r+ Q6 S2 i
  179.             (string-to-int (substring cmd 1 2)))
    + N: X( i( R, ?8 ]" j
  180.     '(0 0)))
    2 o* i5 {5 u9 p9 Y9 y6 S3 q
  181. 0 I$ e) {; d! `+ p) v
  182. ;;; 游戏房间" C! J7 e0 q. R( t4 Q# i7 h
  183. (defun game-room-init (cmd)
    2 z3 o/ R+ i4 r4 D
  184.   (let* ((middle (string-to-int cmd))
    ; w% `( z; N0 x, @
  185.          (size (1- (* 2 middle))))
    7 T) L  ]% Y0 Y; d' z
  186.     (with-board- P* t1 ?# r! H0 {  O% b  p7 c+ D
  187.      (board-init size)
    9 R9 a( y9 _- k8 t7 ]
  188.      (board-put middle middle)))
    % s& b/ x: v/ w; U9 M1 U. {* K+ ^
  189.   'game-room)
    8 m9 \. |8 v3 E) ~$ ~8 v

  190. 4 ]: y, a- X7 R
  191. (def-room game-room) C8 c( H3 L: `+ K* X1 _
  192.   #'board-show! _. o' C9 p3 [0 L! ^
  193.   (t (lambda (cmd)8 \7 J0 Y4 o6 z) C3 t
  194.          (with-board
    ' u/ _8 P* L. k8 _2 b- }" X8 ?
  195.           (if (game-over-p)
    # J: V7 B6 k% A- p2 F  l4 j+ M
  196.               'living-room
    & {7 X4 d; L. Y! O& ]+ c
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    % O" @/ [) a1 y2 x" }5 a+ B
  198.               (when (board-contains-p y x)
    / T2 F8 b) o) S9 b8 Q1 a
  199.                 (board-toggle y x)/ U/ N6 J4 y2 e7 ^7 r
  200.                 (session "step" (1+ (session "step"))))
    . h* W3 c- x7 |$ w# K
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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