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

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

  1. " d" m( ~( T- a, H5 ]4 w5 ~6 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;">;; 定义新的游戏地图/ W( @# E6 @' y& F2 R# f* x& w
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL! C* A/ [, o- h
  4.   'tutorial-room-0)                     ; 默认的入口6 P9 A' X9 N- g5 T

  5. 2 ]+ b/ l/ O* k4 c
  6. ;;; 游戏大厅
    2 }% @+ I2 }; f( h
  7. (def-room living-room& z$ Q# p) L6 P% x
  8.   ;; 进入该房间后的提示语7 x2 Y" [$ b! A9 `
  9.   "1. 教程
    " l) B# Y3 B& Q5 u( ?
  10. 2. 入门(3x3)
    & S( U3 I- A2 q4 [' [
  11. 3. 初级(5x5)/ D4 F+ |% Q6 H' j# e/ ]; {
  12. 4. 中级(7x7)2 b0 f& \; B* |/ q0 s' g
  13. 5. 高级(9x9)
    3 J7 R, t8 N* _; U! c
  14. 0. 关于作者5 H4 X( r& n  K+ I( b3 |+ b
  15. 请选择1-5开始新游戏:"% f* S6 G- R+ }/ L# W6 G. `$ }
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    * M. q! y! k# h* L! j+ y2 }
  17.   ("1" tutorial-room-0)
    7 a" y- K4 n6 v" n# a+ \3 B7 |
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配$ V0 b2 w0 l# `; R
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
      ~7 f. P3 A1 J0 Y3 [6 _; `
  20.   (t living-room))                      ; 如果条件为t,为永真8 a8 y: L: p# U3 y
  21. 1 E5 d0 x! B% u  Y6 y" D; P" Q
  22. ;;; 作者信息
    5 ^& {3 E$ i/ E2 H
  23. (def-room about-room! L, ~$ b4 w- n! B
  24.   "作者:redraiment
    % ^. k* }3 [1 Q! _- v
  25. 微博:http://weibo.com/redraiment9 U  S6 m" ^+ F! t1 ]
  26. 有任何建议,欢迎在微博或微信上联系redraiment。( L+ S5 Q2 g) i9 R4 S
  27. 请输入任意数字返回游戏大厅。"
    4 M. i' S3 f& [* }5 S
  28.   (t living-room)), t5 {* r) X# g4 U8 Q
  29. ; @7 R0 J/ q( |0 y) {
  30. ;;; 教程
    + h8 R4 J# [9 S& |7 w; `
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    6 ?; e. v! a5 W- z
  32.   "The number of tutorial rooms")0 \4 X+ W- u1 ^" I  c8 H! W. v. G
  33. 8 _( Y$ @- f! j- o  C# g' ?$ h: _# }, }+ c
  34. ;;; 简化教程的定义  {. X- T, O( @
  35. (defun string-last-line (content)) t* O* s5 W! ~( K+ \8 d6 d6 G
  36.   "多行内容组成的字符串中的最后一行"; `& W5 A5 O( J1 Q5 L
  37.   (with-temp-buffer
    $ e' {/ i6 W6 G) J$ r2 M
  38.     (insert content)- ~% {9 q4 v7 S
  39.     (buffer-substring (line-beginning-position)
    ! m8 y0 S" {: R; U* T+ g* `
  40.                       (point-max))))( |7 Y; K1 u* S
  41. : E4 s! P# o& Y8 N
  42. (defun def-tutorial-room (prompt)
    ! ~+ t' S- R+ O4 z( X2 }( Z& T
  43.   "根据提示语自动生成教程房间。' z) U# v5 ~  _' m

  44. * j, j, r7 S; \" ]! B
  45. 1. 提取最后一行作为问题;
    $ u1 H' _% E$ Z, b
  46. 2. 分析问题,获取期望用户输入的内容;+ ^; I" J2 {$ ~, Q$ q4 u# E
  47. 3. 定义教程房间和重复提问房间。", I; ?5 h% O9 K: i3 _; h3 C
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    $ S6 a) A8 C( x& |  T
  49.          (repeat-room (concat room-name "-repeat"))
    " Y- }7 {8 ?) G6 u  {: u
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
      \: L% T" f9 n; C# C$ p- m" H
  51.          (question (string-last-line prompt))
    6 X  w4 m0 I- x+ m( p
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    7 d+ I. Y; R- {0 P
  53.                      (match-string 1 question)))
    + J9 p# j/ _' f6 s: V8 e
  54.          (doors (if except
    1 h6 f' ?' |2 W% l
  55.                     `((,except ,(intern next-room))
    " O9 Z. u1 W3 @) S+ O
  56.                       ("q" living-room)6 |: ~* Z0 L6 N0 h
  57.                       ("Q" living-room)
    7 s" f7 r# S; R+ t% y' b
  58.                       (t ,(intern repeat-room)))# _9 T- q3 s8 [6 T) P
  59.                   '((t living-room)))))
    9 Q- f% W& j( v) }' ]9 ]" l
  60.     (def-room-raw (intern room-name) prompt doors)
    5 q! u1 R0 n8 ~8 l2 ~' o' _/ D, w3 r
  61.     (def-room-raw (intern repeat-room) question doors)))* a1 J9 O  g; a# b' ^( o! N" Q
  62. 8 G% C5 @+ q0 W" a0 U5 u* x
  63. (defun def-tutorial (&rest prompts)& K4 U" \3 i  x% D
  64.   "批量生成教程房间。"1 ~% _+ \2 G7 M! l+ L  f+ G6 V1 P
  65.   (dolist (prompt prompts)
    - I# S; q# [# S1 p; R- o: w
  66.     (def-tutorial-room prompt)))
    - V5 q' B+ z0 h+ @
  67. % x$ S3 y3 |% D7 u5 ^
  68. (def-tutorial
    5 b/ g  }: ]7 v* E
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    2 Z5 {% L" M- x, K+ K& E
  70. 1. 教程
    $ ]* e4 W4 E0 E7 B7 G/ e
  71. 2. 入门(3x3)% T4 Y4 c5 m3 U2 Z; W& q4 z" q
  72. 3. 初级(5x5)0 C  z; a/ `  L; ?7 _, h1 P
  73. 4. 中级(7x7)
    0 W8 k' k  a4 i) k+ V4 I
  74. 5. 高级(9x9)
      W/ @% Y0 @6 a) Q
  75. 0. 关于作者
    " r3 c1 O% z$ S3 M+ T4 s7 n0 L
  76. 请选择1-5开始新游戏:, j. ?1 r9 l+ m& K7 J/ B
  77. 您现在正在游戏大厅里。
    $ w7 P5 N3 s8 E8 p' @. U
  78. 请输入“2”进入入门级房间"' E1 {2 H! J( K. q# M& }' A/ i
  79.   "  ①②③
    / Q3 }, z8 |, K5 c% k- ?
  80. 1 ■   b( I: L5 K/ x. D
  81. 2■■■
    & n  m# g! _3 K3 {
  82. 3 ■ - b' f8 h3 A$ p+ u6 E, Y
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    ' ^8 ~- w8 T9 u% c, u# G3 W0 V9 y
  84. 请输入“22”来关闭第2行第2列的窗户。"
    / u2 F# `# P, G; u5 G; T/ X
  85.   "  ①②③
    $ I5 c! I; z4 z2 V
  86. 1   5 F: K. }( F+ ]- T3 n9 c
  87. 2   ' D) s* E( E6 B, O' y
  88. 3   8 e  i' {" s" g6 g% f/ c
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。! X3 Z$ E( Y" Z( p+ q
  90. 请输入“11”,试着开启左上角的窗户。"
    * S" y) r7 d, m% ~6 A$ m# q, t
  91.   "  ①②③0 R8 K$ G2 E8 h1 y7 V5 D# E- g' C2 T' j
  92. 1■■ 
    2 C& J; {: y2 L, s* h/ H
  93. 2■  
    4 ^. L' B. M1 p9 ^2 c8 A* M; q
  94. 3   * E0 Q; g. i7 V
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。5 ^% }1 S: ~/ d2 b0 b
  96. 请输入“13”开启右上角的窗户。"
    6 c% A' L! l/ e4 K. ~3 C& j9 P
  97.   "  ①②③
    3 N2 c1 I; {- d( @+ T, \
  98. 1■ ■! O! Y; g5 h3 H
  99. 2■ ■$ ~3 U( q; G6 g, w# ]: v! _9 O
  100. 3   8 m; W/ O, [: z* c9 E
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    " y5 |: d# w' A! z) D+ S
  102. 请输入“31”开启左下角的窗户。"( h3 _. h; F5 m4 {+ l; z( w4 l
  103.   "  ①②③$ [: }' f& [) e7 P6 w" t
  104. 1■ ■
    . Z0 @( A, h" e$ ?% a
  105. 2  ■1 ~# [0 h" {% Z, i8 E. J
  106. 3■■ 0 Y$ g: t/ g9 a& G/ K( ]) `3 A
  107. 此时,总共有5扇窗户被开启了。
    ) O; V% M! j/ z9 }' }; S+ h$ J
  108. 请输入“33”开启右下角的窗户。"& ?$ p) |2 z+ a8 x7 C8 I0 V
  109.   "  ①②③5 X' i5 v& h2 h& S  w7 A
  110. 1■ ■; d" d6 t) ]4 T+ V* G
  111. 2   
    % b: `  y/ j/ J, g7 Q0 s
  112. 3■ ■2 p' T+ F4 v" i% @# x. C# i9 @
  113. 现在,只有四个角落的窗户被打开。
    4 `% a1 m# {# e: t; O
  114. 请输入“22”完成最后一击!"9 E+ c4 C2 h$ [+ z: P+ t( K
  115.   "  ①②③% y1 k6 y5 A# J5 d5 c
  116. 1■■■
    / Z4 B6 |5 `, u7 y" z
  117. 2■■■
    2 f" b: j- e' T
  118. 3■■■4 X0 H( O( x4 j  n5 P3 F
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")# j  T& {% W4 |* a
  120. 0 x& z* `" ?+ a! I' r+ o1 ]- E
  121. ;;; 棋盘- j% `; j3 B4 S4 q3 w6 b
  122. (defconst *wechat-5x5-white-chess* 12288
    1 w0 m( m* ^5 c7 x
  123.   " ")$ a6 [9 _) w1 p9 Z3 l1 u% n

  124. 2 Y8 }7 b' w4 L* k! r9 X/ s& t0 r
  125. (defconst *wechat-5x5-black-chess* 96328 O6 c7 a: z) W! W. z
  126.   "■")
    * K' }; Y# Y9 R. b1 ~( ]

  127. * g0 w% ^$ H1 h, i* J4 M% K3 a' B9 c
  128. (defmacro with-board (&rest body)
    ( }9 c0 o* _2 a4 ]) K
  129.   `(with-temp-buffer( L/ [. d( L2 J
  130.      (unwind-protect/ J- Q* V# _* D: @5 U- ?$ Q8 p5 {% p
  131.          (progn, V. h3 X1 `- `$ a4 s
  132.            (if (session "board")
    . H) ?  r8 Q' |, g7 R% o1 H7 r
  133.                (insert (session "board")))# O, r8 J* Y! W% A3 a+ ?! r/ f
  134.            ,@body)
    & T  \; ~  Z8 f, Q8 P
  135.        (session "board" (buffer-string)))))
    - X7 I: A* W, ^' R' B1 O3 k8 u: P

  136. 9 G( c0 a" u- x1 {
  137. (defun board-init (size)
    ) _0 Z* P: v% L: L
  138.   (session "size" size)
    / X. S! @8 g% k# i6 n( a
  139.   (session "step" 0)
    + B( C+ L7 [+ q% ~6 Z5 J
  140.   (erase-buffer)
    ' x: J: o7 W- P9 }  g
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    , ?( z4 s5 ]( a8 _+ Y
  142.   (dotimes (row size)
    0 m8 v. [! u5 U* l' V6 `
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*))))): N6 b2 s9 C6 ?- g9 j
  144. + v2 m# b: i( [  ]/ K4 t. I
  145. (defun board-contains-p (y x)  c# ^  S( d6 N$ d' V
  146.   (let ((size (session "size")))
    + X+ Q7 V  }0 ^3 H, Y4 r; H
  147.     (and (<= 1 y) (<= y size)( h5 J3 }5 e( d# A0 t
  148.          (<= 1 x) (<= x size))))
    7 H  @, I0 f$ N
  149. $ U3 t& D7 S. S0 n7 D9 Z* o
  150. (defun board-toggle (y x). b; \/ M. ]" p) y; Q' i. A' X4 Y) e6 X
  151.   (when (board-contains-p y x)
    " t7 S- `$ n1 w# B, U% Q$ h
  152.     (goto-line (1+ y))' J$ e9 O7 }, v) L
  153.     (beginning-of-line)
    3 q0 i0 i) H! b3 w! |9 S
  154.     (forward-char x)
    ) v8 Z* t. d, I) r5 I' w0 b/ i0 Y
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
      v6 L# l' \" k. q, F4 @1 i
  156.                 *wechat-5x5-black-chess*
      }' N- X  ?$ N9 [# h/ ?
  157.               *wechat-5x5-white-chess*))  A* g4 d6 L( l3 j
  158.     (delete-char 1)))
    7 R4 k3 P* h6 Q& S2 j; E, ]
  159. 5 V1 T$ l& J0 r4 u! B! o6 A
  160. (defun board-put (y x)
    $ w4 d6 t$ f' C7 N1 i9 l: t% N
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))! K& b: |2 e" f( P: ~. w
  162.     (board-toggle (+ y (first dir))# ~" a1 n. w" Y3 V2 e/ g' e# g
  163.                   (+ x (second dir)))))
    ! Y" d: H+ ]* r  z$ V. }* k
  164. % f6 [- I  [6 R+ v/ m* _4 J
  165. (defun game-over-p ()
    . V! T- n2 e  T" ]9 x  N$ D, ^" N
  166.   (beginning-of-buffer)% w" i/ F% `1 \  ^, p1 G
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t))): E5 X( L9 s: t3 |2 d1 \* S

  168. - ]3 D3 {5 N7 q0 a  z6 d
  169. (defun board-show ()6 [0 b$ d8 u( |) {- p) u
  170.   (with-board! R$ ~5 L; e' ?. z! y7 X
  171.    (concat (buffer-string)) \- {: i$ d% K! j/ a
  172.            (if (game-over-p)& A/ D' G* S) L3 o7 V/ _
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))0 `; k3 K- D8 n: u
  174.             (format "第%d步" (1+ (session "step"))))))): a) C" `" x: q  t9 z9 d* `7 K9 Z

  175. $ ^$ K# m( i7 l
  176. (defun board-position-parse (cmd)
    8 J8 Y$ b9 ^. A2 v; }; @& @" k. R) S
  177.   (if (= (length cmd) 2)
    9 g9 a% U4 J: y+ Z5 n
  178.       (list (string-to-int (substring cmd 0 1))
    4 \: c/ k( d. g0 t
  179.             (string-to-int (substring cmd 1 2)))4 J: G7 O4 G7 o. L; \6 E. b. n: x
  180.     '(0 0)))
    % x" [) K, {* G) n0 Q# ^4 W
  181. 4 r) H, O4 U: h' O  s6 p* i
  182. ;;; 游戏房间
    & j7 H9 Z3 I9 |& ~# J7 \$ b
  183. (defun game-room-init (cmd)
    " e$ A$ g! X3 ]+ @8 T$ O/ b# @) J
  184.   (let* ((middle (string-to-int cmd))/ |! V' ]' T7 D. |9 w0 g5 b9 \- h
  185.          (size (1- (* 2 middle))))# r9 g* F+ E. e$ P/ L
  186.     (with-board$ E) D5 E. B4 s! H  f
  187.      (board-init size)% _! ]" W4 O8 R; {- f2 K
  188.      (board-put middle middle)))$ S5 ]/ S* b% r& {
  189.   'game-room)( Q# l0 S# l, b

  190. * [/ _* g# o* Y0 s$ a
  191. (def-room game-room5 c* t$ R+ F* N
  192.   #'board-show( Y; f. c4 D  i- r9 z0 U: X! ^
  193.   (t (lambda (cmd)/ w4 u% u- T5 O2 p& I
  194.          (with-board
    ; [3 y: x. S4 |$ S) x
  195.           (if (game-over-p)5 L4 Z# o" Q) p$ o. ~/ o
  196.               'living-room; M) g$ I* |# w4 e) X+ Q
  197.             (destructuring-bind (y x) (board-position-parse cmd)  S  h8 l- ]! S3 _1 T# i
  198.               (when (board-contains-p y x)
    0 ]! }2 t0 X+ z8 A; W' K
  199.                 (board-toggle y x)' o* w- q! W" m! r# ^. J
  200.                 (session "step" (1+ (session "step"))))7 X" ~- }0 K' m( t8 G* ?5 @
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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