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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。: @, e8 o; ?( s5 ~3 q9 h9 N
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. ' ?; I7 U3 A9 Y% y" 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;">;; 定义新的游戏地图6 `0 Z" O) {2 q( ^3 b  ?" n
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL8 q# S/ q/ D) _4 [
  4.   'tutorial-room-0)                     ; 默认的入口
    7 |# C5 _1 q! A3 ^+ x/ F
  5. ; I. d6 Y5 V! @1 X0 ^- ~
  6. ;;; 游戏大厅$ Y+ y9 J  }" p& N5 Y
  7. (def-room living-room% U, Q* Y" W: A  A" ~
  8.   ;; 进入该房间后的提示语
    % [$ D! b* M1 D1 n
  9.   "1. 教程; \: D; X4 u& t6 |6 R8 j
  10. 2. 入门(3x3)
    ) X0 Z" H& Z- s7 M
  11. 3. 初级(5x5), {" [* ]; Z) x* _9 g
  12. 4. 中级(7x7)5 h+ z' Y7 @! E  S3 i
  13. 5. 高级(9x9)
    : s4 X: w- {+ ~. }' k! t3 ?
  14. 0. 关于作者" i% O) Y' }. {' B+ ]+ v! {6 z
  15. 请选择1-5开始新游戏:"! X$ S1 b2 n7 I+ T' R, V
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    ! Z& v; L- t4 d+ d1 X2 X* [+ E
  17.   ("1" tutorial-room-0)4 x& L# }* C9 s" h' @; F" L5 I7 j; e
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配. a2 K- q* {$ \+ S; P2 E/ f& m& k0 d
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    ) S1 x0 ~* K' n* `9 q) L
  20.   (t living-room))                      ; 如果条件为t,为永真
    , E+ Y# \* l8 k& I$ P) X0 r- q
  21. ( T) g+ z! y' Z+ R' y. H9 M% ]
  22. ;;; 作者信息7 C( O- n/ q8 E  V# W; z) m* g& x
  23. (def-room about-room5 z4 L" v4 o) B
  24.   "作者:redraiment
    * a6 D" {4 d! ?
  25. 微博:http://weibo.com/redraiment0 v' ^( T' V) M0 x5 b0 l2 e) L: `& c
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    8 p/ B3 w, X- ~" M  F% m
  27. 请输入任意数字返回游戏大厅。"
    0 s3 j8 z  y( C9 Y& p, x9 J
  28.   (t living-room))
    * o3 a# I7 c" R2 {/ e/ B, i

  29. * D8 O8 J+ O0 O$ H% U/ [2 b
  30. ;;; 教程
    4 I, ~3 ?% P7 M2 o7 y
  31. (defvar *wechat-5x5-tutorial-rooms* 0! J* ]/ F, r: _. s7 K9 d  n' p
  32.   "The number of tutorial rooms")
    + W' o* x+ e& G9 }
  33. % {" ^5 [* }( o
  34. ;;; 简化教程的定义
    ; b. B. i( ~; \! A6 i: Y
  35. (defun string-last-line (content): Q  Z* S! G: j/ K
  36.   "多行内容组成的字符串中的最后一行"' d- x1 o* d' u4 V- f8 `2 y
  37.   (with-temp-buffer
    0 U3 b# E' ]8 J; q8 N5 P* @
  38.     (insert content)
    # j+ f1 o0 [/ }. i& l2 m
  39.     (buffer-substring (line-beginning-position)2 t$ [! D- ]4 {" z" A2 E. b
  40.                       (point-max))))" G; ~# r+ T. R3 \( e9 N$ L

  41. 0 g2 {, S  J9 u7 C0 a; L
  42. (defun def-tutorial-room (prompt)4 C0 Z; z# o& F
  43.   "根据提示语自动生成教程房间。0 M$ E! a3 Q) r/ {6 }7 m' m3 z
  44. 1 r' r& k+ y% k/ h  L2 U
  45. 1. 提取最后一行作为问题;
    1 q' ]& r6 o& t( D
  46. 2. 分析问题,获取期望用户输入的内容;$ _- f5 V. k' @6 ^3 X
  47. 3. 定义教程房间和重复提问房间。"
    8 f! l$ M" i0 X. Z. }
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    . }0 g5 n9 ~1 G  K8 p" g7 X$ K
  49.          (repeat-room (concat room-name "-repeat"))
    ) o, }8 c" J, j1 @/ E
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))6 A6 \5 z. B8 m* W2 X; v
  51.          (question (string-last-line prompt))) C2 p- \- ]2 p8 R- S+ O# c6 |
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    * \4 F2 U/ _1 S) z* d0 y1 d
  53.                      (match-string 1 question)))4 D# t5 M! ^9 d# B7 w& R8 v. u
  54.          (doors (if except# d1 P- D4 ~/ `& T0 h
  55.                     `((,except ,(intern next-room))
    + V. v6 M3 c4 V# i( t( N7 {; [
  56.                       ("q" living-room)
    ' x: m; R; H/ n+ `+ y5 R$ l
  57.                       ("Q" living-room)2 V( j" A, f6 z7 n+ a9 ^4 h
  58.                       (t ,(intern repeat-room)))
      D7 f+ d# |6 F7 G/ }
  59.                   '((t living-room)))))
    , t$ [; k; K+ }% X5 y+ ?
  60.     (def-room-raw (intern room-name) prompt doors)3 i3 j9 t$ _2 r3 M
  61.     (def-room-raw (intern repeat-room) question doors)))) r# V9 N" D3 a, m5 j

  62. " N7 m* }; A7 J7 |4 [1 j
  63. (defun def-tutorial (&rest prompts)+ K& P" x% @3 ^2 Z
  64.   "批量生成教程房间。"
    2 s% Q/ S, g) F& r) ^9 {3 k% H  A
  65.   (dolist (prompt prompts)- d; d% V0 @- I/ w. ^
  66.     (def-tutorial-room prompt)))5 m% U1 u  R! ^, F

  67. . E# W  d' T+ h: G
  68. (def-tutorial
    5 b6 K% n1 G/ F5 y( x7 e
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。/ O/ Y( d7 C3 z: k4 c1 b3 q
  70. 1. 教程$ I; ~- N/ L3 p0 K$ a. Y
  71. 2. 入门(3x3)
    2 c" l+ q8 V, s9 I8 D
  72. 3. 初级(5x5)7 E  ^7 c4 A, \5 {/ G9 p6 x
  73. 4. 中级(7x7)" Q3 ]  j2 N8 M, m5 ^2 C
  74. 5. 高级(9x9)% Q" m6 s2 u1 E5 ]/ Q  W
  75. 0. 关于作者
    * b3 C  L; E. d$ Y. |
  76. 请选择1-5开始新游戏:
    0 B0 ?8 g* z/ L4 ?
  77. 您现在正在游戏大厅里。) X8 E7 O( ]! e+ u7 {3 k4 A, Q* |
  78. 请输入“2”进入入门级房间"7 U  ~4 j5 I& F6 u7 V8 ?9 G) d
  79.   "  ①②③
    $ R* E: Q; u2 ]9 h8 m6 y
  80. 1 ■ 
    1 C# l  t* ]4 m4 R
  81. 2■■■
    5 j, Q% T9 q+ Z2 l) u
  82. 3 ■ . Q4 ?+ V" _1 z% P& Q
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    . n6 U1 t1 B2 T- j# c
  84. 请输入“22”来关闭第2行第2列的窗户。"$ P" _0 F& g. Z' r2 h
  85.   "  ①②③# Y8 I; ^9 F) m1 W& x
  86. 1   
    * N0 X0 c9 ^6 L: J$ P
  87. 2   7 q; m' W$ ^+ l* ?
  88. 3   
    % {. Y2 M. D" V4 p, c* n0 n9 `
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。4 ?) E  L) r$ X5 ^% |+ z
  90. 请输入“11”,试着开启左上角的窗户。"
    - Y9 l, Q- m4 Q0 t
  91.   "  ①②③
    ; \4 H: F0 w; k* r7 E: [
  92. 1■■ % n) G- w$ O/ V( i3 D' j
  93. 2■  8 [4 i, Z% ]+ O4 ~' `+ G4 i8 Z
  94. 3   
      ~# G% I4 n1 k3 s+ T& j. J
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    ) @9 _' f0 ?8 O
  96. 请输入“13”开启右上角的窗户。"# M. \0 q4 Y" m7 A
  97.   "  ①②③
    ( w& G+ @7 Y9 |. u( k/ k4 X9 F  P# W
  98. 1■ ■
    . z8 ~! m6 u3 ]* Y) Z
  99. 2■ ■- ~) r$ h+ d3 q8 e' d" Z
  100. 3   4 i" e4 `+ n8 Z6 u
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。8 r1 L9 p6 D0 V* G+ R$ c7 N
  102. 请输入“31”开启左下角的窗户。"
    8 D8 }/ U1 I" Z) b) j/ U
  103.   "  ①②③& A$ B6 w2 f" O- j* z% p4 S
  104. 1■ ■7 X. l! I* ^+ u9 e" Q6 Z( j
  105. 2  ■1 Z6 E0 H/ B4 T0 o$ Z' e
  106. 3■■ ( p- Y$ V2 Q' b1 x
  107. 此时,总共有5扇窗户被开启了。
    5 @) r4 ^% F, u  t3 w
  108. 请输入“33”开启右下角的窗户。"" X; f" h2 J' [5 [6 ]& I( |9 v
  109.   "  ①②③9 Q% o5 W& \" |- h# \+ s
  110. 1■ ■+ Z) A! W% ]5 b
  111. 2   
    0 m. Z6 s% ~! a* }
  112. 3■ ■
    ! N5 f" m) i! a2 G5 m
  113. 现在,只有四个角落的窗户被打开。
    / U9 M" k# g( l) u. Y, U( l
  114. 请输入“22”完成最后一击!"& D. A3 M$ }6 f! T0 c
  115.   "  ①②③
    & R3 [8 X; \9 Q" R
  116. 1■■■& E( i8 ~- h. K) s3 W2 f  e
  117. 2■■■
    : [' z. ^' }+ H+ }' R; t
  118. 3■■■9 S- |; b% y: |
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")( U- z& A0 v# U0 y6 G- Z0 ^" T5 z

  120. " n8 {7 f: M3 |4 j# p7 q
  121. ;;; 棋盘4 W' q! D, D  r5 j& Y) R3 v9 F
  122. (defconst *wechat-5x5-white-chess* 12288
    3 k* t" A0 U& i" F
  123.   " ")
    9 c& q( r) i: t5 J! G& K7 V* A

  124.   `1 a" w! C& T, W6 k! L
  125. (defconst *wechat-5x5-black-chess* 9632
    0 C8 I. k- V5 ?$ E, ~6 w" \
  126.   "■")9 \: `/ S1 o2 \

  127. / ]8 n7 l9 Y- K/ P; Z+ L) s+ ]% Y5 B
  128. (defmacro with-board (&rest body)' C, _" h5 A7 v
  129.   `(with-temp-buffer
    0 N5 _: ~- n/ f+ Y- m
  130.      (unwind-protect
    ! g5 f5 S' Z3 ~
  131.          (progn  k2 u7 D! ^; s* C9 G6 p) \
  132.            (if (session "board")7 t. \' p: h/ s1 h4 q7 v1 w6 r
  133.                (insert (session "board")))
      X& J) B/ o4 p' w
  134.            ,@body)/ X8 n* K0 |7 X+ j" H9 y; ]# B
  135.        (session "board" (buffer-string)))))- B- ]' j; C/ ~9 a' K6 r8 P
  136. - D) g! v+ g6 p5 O7 K9 w
  137. (defun board-init (size)
    $ ^6 M/ T9 c% A" M$ I
  138.   (session "size" size)) {6 }+ U1 t! |* X* o7 s
  139.   (session "step" 0)& W+ [- t# [2 G
  140.   (erase-buffer)' o2 _9 n2 H3 ^) x7 F: C$ X0 r
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    : [8 L, u- W& Y! J2 Y" l
  142.   (dotimes (row size)
    - R' M+ _( h, l6 T
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    ! A) |0 E0 g5 `1 u! X, }' o  D8 v

  144. 3 H( ^" f0 W/ ^1 F9 x- B% P
  145. (defun board-contains-p (y x)2 U4 k1 D) Z, J. a$ W9 ^6 }; ^- [8 V9 r" V
  146.   (let ((size (session "size")))
    + r# D% }  G, s/ K
  147.     (and (<= 1 y) (<= y size)+ c9 f: r* m. C# _# B' y3 R/ A
  148.          (<= 1 x) (<= x size))))
    6 W' H' R6 _- e" l! o
  149. $ h" _' b8 _1 w4 k- }2 k8 ^* p
  150. (defun board-toggle (y x)( p2 p" h: o) {
  151.   (when (board-contains-p y x)
    8 n- K. m5 D5 e, I, r- J
  152.     (goto-line (1+ y))
    6 r2 e! N; O( G* r! |3 x4 e7 {
  153.     (beginning-of-line)0 y$ D5 R- v/ U7 P' |) M, U
  154.     (forward-char x)7 M% A8 I! c5 |3 T; l
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))* q" p0 ~8 r8 j- ?$ T5 W- q3 ]
  156.                 *wechat-5x5-black-chess*. L  i7 i, J: P: L" P
  157.               *wechat-5x5-white-chess*)); ^# t0 W4 j( k2 I" q( z+ s
  158.     (delete-char 1))). n8 @) i; i/ B2 i. O
  159. 8 E5 l+ m! O8 H3 y; P& m; t
  160. (defun board-put (y x)/ n0 v& n' w% |3 v6 ?8 t
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    " W1 x% c4 K8 _, H! S
  162.     (board-toggle (+ y (first dir))
    ! g$ [; H+ j* A- u
  163.                   (+ x (second dir)))))# ~5 ]4 C, ~3 z* U
  164. ) o' R+ }  x5 S/ L  @
  165. (defun game-over-p ()
    ' d4 x1 e' |7 J0 o- @! F% b
  166.   (beginning-of-buffer); [5 T3 y" D! m- F
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    & M; J) w& R( B$ f. s
  168. 5 q* T4 H; H4 U; I* ?
  169. (defun board-show ()
    % [0 R- i6 x& \$ ]  _6 c4 ?$ I
  170.   (with-board
    8 z; e9 z( |/ O7 ]: C- X
  171.    (concat (buffer-string)
    ' i+ @7 E+ r: ~; J
  172.            (if (game-over-p)5 B/ K. {/ l. B0 _
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
      z) W, s& h% Z# ]' \# n4 [: }
  174.             (format "第%d步" (1+ (session "step")))))))$ R4 ^% B9 {, B: _% a

  175. 9 H" k5 a; `8 g# ~  q2 w# b
  176. (defun board-position-parse (cmd)
    . @% k. X* J+ f2 L" V' N: U7 l
  177.   (if (= (length cmd) 2). n6 |* c3 Q! z& K: M6 }* y$ Y
  178.       (list (string-to-int (substring cmd 0 1))9 x; F- [2 E- n! p2 t
  179.             (string-to-int (substring cmd 1 2)))" v9 Z) w2 [& l7 k" G7 t
  180.     '(0 0)))
    . f6 k) K! U$ c5 G4 o- q

  181. 0 C' r0 h& U5 R
  182. ;;; 游戏房间5 L/ @+ m9 L. g9 k( \4 g
  183. (defun game-room-init (cmd)
    ; k1 e& {" U7 c; L4 h
  184.   (let* ((middle (string-to-int cmd))
    : X$ O  O1 e# h
  185.          (size (1- (* 2 middle))))2 O5 f0 U* t$ n+ T+ |* Q% w
  186.     (with-board, M# q0 M3 b' j3 D, s, X
  187.      (board-init size)1 G: O& N/ w' k
  188.      (board-put middle middle)))
    & G) n4 x7 b0 W$ x2 n+ j* R' a( i7 U
  189.   'game-room)
    - F! s# r% B+ m/ N$ e0 Z: v# I
  190. + X3 N! F; |1 ]
  191. (def-room game-room
    ! f: r+ m9 O! |7 ^
  192.   #'board-show
    / o* D6 C1 Q+ |0 u
  193.   (t (lambda (cmd): T" n& G! i( x0 \7 A
  194.          (with-board, b7 L5 F4 [5 z' S+ S5 \
  195.           (if (game-over-p)  n9 t4 ]6 T3 b' f2 _+ t9 P
  196.               'living-room
    3 B8 K8 Y& p3 ~0 j# I/ D
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    % \  ]8 N3 ^+ U  N6 @% q. f
  198.               (when (board-contains-p y x)& ]) ~3 ]' A, d- z# X0 c) m) Q
  199.                 (board-toggle y x)
    $ ~. v$ ]% g: e- h8 t
  200.                 (session "step" (1+ (session "step"))))
    ) H1 a" O& [7 W5 m5 @
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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