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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。+ M* i/ @0 a# J( j
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. / [! t" g$ S2 }  r5 w
  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;">;; 定义新的游戏地图3 g/ G5 W5 Y/ F
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    $ u6 E' a$ G  L" L) ?, R
  4.   'tutorial-room-0)                     ; 默认的入口
    0 I; A; ~' ]% g* a/ u# T5 C; [1 v

  5. 5 N. O* }. h/ x+ M* l
  6. ;;; 游戏大厅
    5 m( j/ {8 X: @8 y/ W( d* b
  7. (def-room living-room6 D. y& i3 j' N0 U% K1 K8 ^( b
  8.   ;; 进入该房间后的提示语/ t+ t( S6 x+ Z& a7 w2 h4 I
  9.   "1. 教程
    & r& }7 B. d2 W2 r
  10. 2. 入门(3x3)
    0 ~& \1 ^0 o* @7 ?; ?3 h! n0 x
  11. 3. 初级(5x5). }! j9 `% U) N0 x4 f! h
  12. 4. 中级(7x7), L9 Z% K# X  g: }
  13. 5. 高级(9x9), l' W9 A+ y, z: S" Y0 e6 f
  14. 0. 关于作者+ x5 v2 U8 U! y$ }) L9 v
  15. 请选择1-5开始新游戏:"( o% `$ v  ~* f! v; A* _$ V+ P
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    * _+ j$ }1 n% @9 f
  17.   ("1" tutorial-room-0)
    ! [1 N0 e; ~$ z; F
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配5 k6 n5 x6 B9 j+ p' N1 D9 p, o0 Z$ l
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    / T0 k/ O$ E0 g( M7 Z: Y
  20.   (t living-room))                      ; 如果条件为t,为永真, I5 `8 p& B- Y% B) p9 E
  21. " f' l+ f  R& h
  22. ;;; 作者信息; M& `" }% P1 a, I$ I, A6 j0 j
  23. (def-room about-room8 a  L( e4 I+ h& H$ G0 S- Z
  24.   "作者:redraiment
    - W5 r) y1 n% t2 r6 ?
  25. 微博:http://weibo.com/redraiment
    % s: M1 s7 c) J- |* r9 E' T4 E, Z
  26. 有任何建议,欢迎在微博或微信上联系redraiment。5 r3 W* h% A- ?# z; ^
  27. 请输入任意数字返回游戏大厅。"" I* M" N; b: g
  28.   (t living-room))8 E% K& I0 V$ F: w4 a
  29. ' P) [8 N& `: A2 o* p8 J
  30. ;;; 教程
    ( J7 B( q1 l+ Q
  31. (defvar *wechat-5x5-tutorial-rooms* 0. _+ P' t7 E! Z
  32.   "The number of tutorial rooms")7 J( B9 ^2 S5 Q4 D* }
  33. 0 Y- A; K0 s* _% ]3 o
  34. ;;; 简化教程的定义
    4 R9 Q4 P8 j) n3 I1 `8 C
  35. (defun string-last-line (content)2 X& g: P) e# t) h+ t  c
  36.   "多行内容组成的字符串中的最后一行"7 t& u9 h  l! h4 I
  37.   (with-temp-buffer" b& d. f7 S3 p8 q
  38.     (insert content)5 o0 {' q# [0 z. L" ?. ^* V) c
  39.     (buffer-substring (line-beginning-position)* [; `) n' L. @7 @
  40.                       (point-max))))
    0 N2 h. l  C* X/ v! Y6 M
  41. : M2 z+ X# ?, ~; j
  42. (defun def-tutorial-room (prompt)
    5 |/ ^0 }2 U3 H( l
  43.   "根据提示语自动生成教程房间。1 M8 U) C% V1 Z: v
  44. ( W. W% j; U# M- x" X5 L6 N2 e* _- A
  45. 1. 提取最后一行作为问题;
    % l0 U7 K' F" E' P2 m9 p
  46. 2. 分析问题,获取期望用户输入的内容;
    5 t, D4 ?. o0 f2 J* Q3 E6 v
  47. 3. 定义教程房间和重复提问房间。"* ~- p9 ~. L2 Z$ C( z# B
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))8 I+ H. O. b' U# U6 Y
  49.          (repeat-room (concat room-name "-repeat"))4 g: U; e+ d/ E0 M+ [- W2 P
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    # S: Y: o! @4 V* |
  51.          (question (string-last-line prompt))7 \7 y+ e7 W  E2 R! I
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)8 h! j5 t5 r- C
  53.                      (match-string 1 question)))
    # I4 T9 r& T5 Q  g& X1 M1 f
  54.          (doors (if except
    - T! G* ~, e3 F
  55.                     `((,except ,(intern next-room))
    6 g# d5 Q7 E1 z5 `
  56.                       ("q" living-room)
    , f$ C' ?2 ~5 s: N- S6 F2 [0 U1 l
  57.                       ("Q" living-room)
    0 k, t6 U% O2 n0 i7 i2 C
  58.                       (t ,(intern repeat-room))): f0 w6 P2 z: o8 K  p: e. C' q
  59.                   '((t living-room)))))
    / f7 y4 I  a1 w4 L
  60.     (def-room-raw (intern room-name) prompt doors)' U* e7 I. V$ X: b- `7 \
  61.     (def-room-raw (intern repeat-room) question doors)))1 B, R- G! z( _" C
  62. ; \4 b" K# q/ m0 e0 x- j6 ]
  63. (defun def-tutorial (&rest prompts)
    ; U/ E2 E/ Q; e0 u/ j  N
  64.   "批量生成教程房间。"- F4 C9 c; u  u9 b) H, `( R
  65.   (dolist (prompt prompts)
    ; N5 X& {" C$ B
  66.     (def-tutorial-room prompt)))
    ) c. M6 _6 B6 X: [
  67. ( o4 @* c! R* j
  68. (def-tutorial$ h3 M  }8 B1 L  s9 ]7 U& z- C( |) e* Q
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    3 w5 b' R/ ]8 M  ~+ x: _3 e' X
  70. 1. 教程
    ; B, E2 c0 Z4 [0 F4 \, L  O0 J
  71. 2. 入门(3x3)# z- M! q  _9 ~0 c, _; Y. R
  72. 3. 初级(5x5). U; J9 T! s- C9 v2 A% `8 G6 w
  73. 4. 中级(7x7)/ V8 a+ b0 Y$ ~' @9 j) ^( H" r+ W( A
  74. 5. 高级(9x9)* c% W4 f/ I) K& _- i1 o% A
  75. 0. 关于作者
    ( A& ?# S9 h+ ?- p- G6 A, n2 G
  76. 请选择1-5开始新游戏:
    , f6 L& r( D1 d# r7 d- I
  77. 您现在正在游戏大厅里。; d+ p! Y6 i/ o0 J
  78. 请输入“2”进入入门级房间"2 C2 d* Q, s0 ^
  79.   "  ①②③+ f7 \/ }" m# ^7 i: V4 H# G6 s
  80. 1 ■ 
    9 |! \. w7 m. z( B8 t$ f
  81. 2■■■
    + @+ e/ M2 w3 X) S$ g& k8 y) L) E
  82. 3 ■ 
    & ~; L( p0 |4 Z: g
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!7 a9 D+ S9 B% H, w3 c6 \  U  `
  84. 请输入“22”来关闭第2行第2列的窗户。". ?3 i  n# \1 A
  85.   "  ①②③
      C$ Y7 ]$ Y2 Q+ P8 I
  86. 1   " h. s1 ]- X- x
  87. 2   . s7 ?% l( i! D) v2 _
  88. 3   
    # Z  @, Q3 C1 E. l3 A6 u
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。& H% [9 A# O0 {8 M
  90. 请输入“11”,试着开启左上角的窗户。"! q; h: F6 d% }. t- W5 O2 X
  91.   "  ①②③
    6 d- ~% `* M2 T* G# ?5 T
  92. 1■■ 
    0 u3 S& d/ c3 h3 j  P$ d
  93. 2■  
    . g) o- J# D. D: l7 n8 Z' {' d' F
  94. 3   
    7 q5 Z) ^6 V9 w1 h2 H* m, s: c% i
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    , d* y% n2 K8 z0 c* ~! G
  96. 请输入“13”开启右上角的窗户。"
    ; ?3 \9 O! ]2 Z3 q) M
  97.   "  ①②③# \6 ?/ w" Q* R( @, J3 _
  98. 1■ ■8 t$ {$ i# d' o1 a
  99. 2■ ■& [+ i( `/ E  ^
  100. 3   
    9 E3 z6 R2 @! T: Q
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    4 e. j9 |8 R) O: X% _
  102. 请输入“31”开启左下角的窗户。"' D6 f: Y) [! T9 }
  103.   "  ①②③
    - e: `' Y& H6 V# t1 t5 h& Q
  104. 1■ ■
    5 A# V5 h( G# e9 ~6 P. D3 ]4 u
  105. 2  ■$ Z  w4 R+ z9 Y: @
  106. 3■■ 
    9 M/ Q' R$ `6 |) |7 i5 p
  107. 此时,总共有5扇窗户被开启了。
    1 R7 L1 E8 b7 O2 d0 C& `
  108. 请输入“33”开启右下角的窗户。"
    & V' i. Y% G2 P' u- z
  109.   "  ①②③& y6 A# I) S1 j
  110. 1■ ■
    / v/ P! b5 a) y1 o3 a; Z
  111. 2   
    3 \4 C! X: O6 T/ `) M  M/ V( O! _
  112. 3■ ■
      @' G7 @6 Q: t$ T* E1 A
  113. 现在,只有四个角落的窗户被打开。+ i. E8 o  M3 l
  114. 请输入“22”完成最后一击!"" [* N2 R" Q1 V# U/ t! s' m
  115.   "  ①②③$ f8 B; y2 t0 _' W" O
  116. 1■■■
    7 X2 ^. y/ X# Z5 _% ^' b; O
  117. 2■■■
    ! x# i5 S) J* [2 Z% E
  118. 3■■■* ~  G! h! i3 V: b8 @
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")8 W( ~5 y, |% |2 n% X# H
  120. " K6 l' ?( p" f9 t
  121. ;;; 棋盘1 H6 E' y# n) c4 ?( v6 H
  122. (defconst *wechat-5x5-white-chess* 12288
    ' z! ?! O7 Z4 e6 _8 N9 O, H
  123.   " ")
    ( h  S3 C: H; z" N: N8 ~0 h: z
  124. 4 M) Y- L+ b- n' G" E! c8 T: o
  125. (defconst *wechat-5x5-black-chess* 96324 }( F' x1 `, Z0 B& A# Q
  126.   "■"), I# t# u8 f: C6 X& s5 T; p) ^0 n

  127. ! Y5 p0 {' |3 e6 v
  128. (defmacro with-board (&rest body)3 i. E- W2 a: ?- `" a( s% U: Q
  129.   `(with-temp-buffer
    5 u" o7 {) D, B' @- e
  130.      (unwind-protect( B" Q4 w" p& i% H: e- q, m
  131.          (progn- u* }1 w$ }5 e. i! R/ J" j4 u
  132.            (if (session "board")$ _7 V4 Y6 n- ?  p: S  a
  133.                (insert (session "board")))
      c4 S  u0 ?3 A2 t
  134.            ,@body)
    , l6 z% C3 K0 k, U7 {5 `3 c
  135.        (session "board" (buffer-string)))))" `$ y) |2 v5 @1 Z
  136. " ]7 ^' M+ d9 z- l( B' F0 S- T
  137. (defun board-init (size)
    7 n0 \& G6 V8 D$ ~4 y
  138.   (session "size" size)
    # ^1 _( P9 i+ ?( J2 z5 X
  139.   (session "step" 0)/ B3 |6 d' f( J4 x4 A. j7 F
  140.   (erase-buffer)
    , X3 d# ^- J5 p$ K: Z
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    $ c1 m5 Q" H  q
  142.   (dotimes (row size)# ^# E1 u, ~: w* Q7 `1 `. s* n
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))6 k" ^* U$ ]8 P: }
  144. + q' n& M0 A" W/ e
  145. (defun board-contains-p (y x)
    % e- }! ?6 u, R  R2 \
  146.   (let ((size (session "size")))
    , k8 u6 P  S/ `8 }( U/ C
  147.     (and (<= 1 y) (<= y size)
    0 S- H) C1 N& P, J: {- S
  148.          (<= 1 x) (<= x size)))): D: I2 m2 O6 c& }
  149. 7 G3 H. H" C4 I2 R
  150. (defun board-toggle (y x)) N' e7 h+ K( p  O% X2 D
  151.   (when (board-contains-p y x)
    6 H- }* J$ g5 G9 g$ z: Z
  152.     (goto-line (1+ y))
    * `9 y- N) f3 Q( w
  153.     (beginning-of-line)7 \9 R% `! d3 R% }5 u3 N
  154.     (forward-char x)9 P. f, x$ M3 E- L! i" A" g7 p
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))0 c( @9 q- Y5 `; r8 {7 o2 ^
  156.                 *wechat-5x5-black-chess*! j4 @6 [' q8 j# \( }
  157.               *wechat-5x5-white-chess*))
    + Y5 v2 p* K8 U! O
  158.     (delete-char 1)))0 w9 ]5 s9 p6 v: [. V

  159. 7 Z8 v- n$ w: [* ]  I3 B6 u
  160. (defun board-put (y x)
    5 \& J6 U/ n) j" ?1 d- K
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    * D" [1 u& n+ ]& @
  162.     (board-toggle (+ y (first dir))( p5 K0 U! h5 i- c0 [
  163.                   (+ x (second dir))))), {; M  U& x3 W7 c  c7 G5 u% B* h( P
  164. ! Q7 _0 Y5 x6 x, l) i5 x. ~
  165. (defun game-over-p ()
    - z6 T, w" u4 k8 D! C0 ]
  166.   (beginning-of-buffer)+ V$ u! A) o" p2 Z* l% W
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t))): N; v1 A2 }7 \( P1 [6 u) c$ W, }) {
  168. & c6 U$ c" X* s! \/ Z  h
  169. (defun board-show ()
    4 d9 k3 ^' {" l8 Y$ ~- {+ ]+ h
  170.   (with-board
    1 C1 K  Q# F/ @) C- W
  171.    (concat (buffer-string)
    / {& n( X" b  {6 j
  172.            (if (game-over-p)
    % |6 I3 j2 y  n9 q) H; @$ s' V
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))( Z! A# ]( [( r" p
  174.             (format "第%d步" (1+ (session "step")))))))
    % X/ ]# u- O8 h' u' d4 h) x
  175. 4 F6 D; A, p0 B
  176. (defun board-position-parse (cmd)& E% R  y! p+ h* T- e# d
  177.   (if (= (length cmd) 2)' f- J, x' a/ b7 q) D9 M' n6 W( o
  178.       (list (string-to-int (substring cmd 0 1))
    , K: a  L& E  n
  179.             (string-to-int (substring cmd 1 2)))* g* b( q$ t( V0 \8 `0 o* c
  180.     '(0 0)))
    + u8 Q2 O( a8 B& o4 f: h0 O" |
  181. - S( c+ F! O2 }3 _9 d  ?: [+ }
  182. ;;; 游戏房间6 ~6 {( v( u8 W) v9 x% O# `
  183. (defun game-room-init (cmd)5 c0 K% t$ j4 K' A0 N1 o2 U
  184.   (let* ((middle (string-to-int cmd))2 S  y) u( D+ f/ R1 q1 t
  185.          (size (1- (* 2 middle))))
    / k: N* A( v* X; o5 n4 c) S
  186.     (with-board0 ?. Q% u; _# @- `2 D
  187.      (board-init size)+ d4 d( p; W: P
  188.      (board-put middle middle)))
    / {2 {! W$ n0 C6 \
  189.   'game-room)/ g0 U' B. Z$ u% Y4 b7 X

  190. / a  D- B, |) t. _
  191. (def-room game-room
    + F- ^; s0 V$ E: U+ h2 h- c
  192.   #'board-show
      F8 D6 O1 z, e# I, j) B
  193.   (t (lambda (cmd)
    2 w: n6 h+ }4 y6 ~0 L
  194.          (with-board
    . {% n/ c3 y- t: U5 P+ x! p
  195.           (if (game-over-p)
    , v9 H+ \. h; [! l3 a' o, [2 O
  196.               'living-room
    & T% f3 o  X3 F" L5 d
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    % }/ Q" V  o) [  K4 ~
  198.               (when (board-contains-p y x)
    + X8 \* U  ^+ \
  199.                 (board-toggle y x)' H2 m* u+ [" f: r8 O. a
  200.                 (session "step" (1+ (session "step"))))
    9 G: d1 I  Y  B4 q
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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