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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
6 J- N/ C% E# M6 x借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. 7 s+ V( i6 k3 K1 q, v
  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;">;; 定义新的游戏地图( i# I. E& j7 b0 V: z
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL+ E, M% X9 ]4 \( ^8 I; R5 J
  4.   'tutorial-room-0)                     ; 默认的入口9 J5 S9 L0 p4 Z

  5. & D% b6 ~/ W% }6 c! H
  6. ;;; 游戏大厅
    % [. S8 m% |+ }$ Z- u$ I
  7. (def-room living-room
    9 k( b- W& w3 d9 z( u% ~7 \
  8.   ;; 进入该房间后的提示语  Y9 b; b- V- E% }5 D: l
  9.   "1. 教程  u5 M! u7 s# ^0 A9 X
  10. 2. 入门(3x3)3 C. x2 H2 y- I6 @& S! q
  11. 3. 初级(5x5)- b3 x2 ~- Q, u7 E. j: Y# [# v
  12. 4. 中级(7x7)
    % h9 X% c9 d; G- I) M- Y
  13. 5. 高级(9x9)
    8 n* v# b9 b% a9 j' I3 j
  14. 0. 关于作者1 |/ \. ~* m# ^
  15. 请选择1-5开始新游戏:"2 F; a+ O( T1 \8 Z
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名7 i7 n4 g; J# S! ^
  17.   ("1" tutorial-room-0)
    8 O7 }& q/ B# ^" ~  R
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    ) K( J. G7 Q; q$ ^8 N. c
  19.                                         ; 相应的返回也可以为函数,动态返回房间名3 H' C# K7 t/ m4 e# Q
  20.   (t living-room))                      ; 如果条件为t,为永真
    6 `' x: R9 Y, R; e2 r
  21. : s3 P6 C4 s* g# r
  22. ;;; 作者信息9 M/ w- Y- d  l4 X7 o7 P
  23. (def-room about-room* \8 C4 k. n) O2 ^- R2 }- s
  24.   "作者:redraiment5 j' a8 ?% C' d  ^
  25. 微博:http://weibo.com/redraiment- }4 z) x; q0 h5 Y0 @
  26. 有任何建议,欢迎在微博或微信上联系redraiment。9 h4 I& W" v! Y7 z
  27. 请输入任意数字返回游戏大厅。"7 Y2 J* ?8 |* u, O& H: M6 C
  28.   (t living-room)): J  @3 ?0 M2 {8 e& [- B. W, \. @

  29. ; `7 s9 ~$ S  t1 t5 d4 V* S7 Q
  30. ;;; 教程
    " p" x. U! `7 `. \6 k' z, @, ]
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    # N( j$ @+ t. v1 F
  32.   "The number of tutorial rooms")
    ' H7 B" t# R% [! t4 K; ^

  33. 0 N9 V7 b( S  S  p$ S, T  F
  34. ;;; 简化教程的定义
    + e0 n" |  h5 a1 n# T* l+ B2 j
  35. (defun string-last-line (content)
    - c& d- E; v" a8 W
  36.   "多行内容组成的字符串中的最后一行"
    + L5 N3 B0 D; _
  37.   (with-temp-buffer. ?/ G5 A) X$ m+ A4 x
  38.     (insert content)
    . ]2 u- `; i* k; U6 R0 K$ {
  39.     (buffer-substring (line-beginning-position)5 O8 N5 x5 a$ O  L
  40.                       (point-max))))( g6 a  Y5 P# ]

  41. ( ]7 E8 h0 e2 M5 s3 y
  42. (defun def-tutorial-room (prompt)# |, O% U5 m5 F- I5 `7 m
  43.   "根据提示语自动生成教程房间。; f" E* A- l" s
  44. 4 b5 F4 r! c& T! g6 x; w- a. d
  45. 1. 提取最后一行作为问题;7 |4 R! i. \3 ~, U
  46. 2. 分析问题,获取期望用户输入的内容;) y) c1 v( G8 D  }6 K& N3 A" S
  47. 3. 定义教程房间和重复提问房间。"( U4 [; q2 _. r  L; Z
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))6 ~9 H, n/ {6 [2 q& G+ X3 P1 X0 _
  49.          (repeat-room (concat room-name "-repeat"))
    ( _4 o2 k' r- v9 S5 ^9 n# m' [
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))% Q9 d$ E' D5 P0 Y  u1 C
  51.          (question (string-last-line prompt))5 y7 M% E% l( V8 o( V# m" m  B7 _
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    . ?3 n/ S, ?# X; o
  53.                      (match-string 1 question)))
    ( W8 a) @: ^' a
  54.          (doors (if except
    4 ^' P2 Q( O+ X7 R1 e
  55.                     `((,except ,(intern next-room))
      @, h( f: n" [' S
  56.                       ("q" living-room)% z9 V) }  X) t. \/ w/ C
  57.                       ("Q" living-room)
    ( p& x2 R* d* h# K  \+ r; h
  58.                       (t ,(intern repeat-room)))" f# d$ ~) A+ `5 S9 ^$ ]
  59.                   '((t living-room)))))
    ) Q8 D% D, R( J& M
  60.     (def-room-raw (intern room-name) prompt doors)* g) Q$ o. J4 h) L7 P8 ?" N
  61.     (def-room-raw (intern repeat-room) question doors)))
    7 L  S7 U3 I) R) M. Y7 `

  62. ' i0 h; I/ {5 y1 d  z8 y
  63. (defun def-tutorial (&rest prompts)
    3 H, f' z4 y/ ?* K* F
  64.   "批量生成教程房间。"; i: R' z+ H9 ]
  65.   (dolist (prompt prompts)
    ! v) t+ `4 E) ~8 m
  66.     (def-tutorial-room prompt)))
    & f) M4 M$ d! q4 v6 O, }

  67. 0 ]& L9 l, P2 v, a5 ?2 h
  68. (def-tutorial
    1 O* N! d% Z9 ?4 [
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。9 K* P7 T3 l+ r% y8 z2 B
  70. 1. 教程, B. O1 Z, Q+ h4 g9 M
  71. 2. 入门(3x3)
    $ _; o8 F7 O( [- B1 Z3 H3 W
  72. 3. 初级(5x5); c0 l$ I# @  _) _6 I* \  Z
  73. 4. 中级(7x7), [. I4 _( h4 j% p3 P! L
  74. 5. 高级(9x9)
    # W/ A) x8 p3 d$ r1 c9 x9 H
  75. 0. 关于作者
    / `4 y: ^2 M3 R. W
  76. 请选择1-5开始新游戏:
    " C% [$ H. C/ _2 I" j" H
  77. 您现在正在游戏大厅里。
    ! B) A% n" z5 e7 _) w# j
  78. 请输入“2”进入入门级房间"9 R2 E: y' J, l- C' A
  79.   "  ①②③, d' `* ~" C/ P  [5 N' m+ f
  80. 1 ■ ( d9 \. p: W5 P# _0 s. i& U
  81. 2■■■) ]1 U( t  A* P/ p0 u- r* g
  82. 3 ■ 
    5 b$ e! b  G0 N
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    : X3 x& a7 [. Q+ u, q4 @; M
  84. 请输入“22”来关闭第2行第2列的窗户。"3 D% H; R' W/ z% C8 y
  85.   "  ①②③8 m9 n. O- k# O9 M- S5 D( y
  86. 1   
    3 @; @( U* `" q, [# d7 q
  87. 2   * j2 j" e! n7 K1 X' y
  88. 3   
    ' l3 w5 d+ u% l4 u0 d( k0 G4 R
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    4 P# R- N; m8 z6 I, |
  90. 请输入“11”,试着开启左上角的窗户。"6 E  K9 A  f) v: E1 R1 l, v7 a
  91.   "  ①②③# h# J, A" j( U: O( c% x
  92. 1■■ ; l8 ^+ L$ b/ I  m* D4 C$ s, o
  93. 2■  - u/ Q; q( J9 j, U4 c% t! u" h9 w( V
  94. 3   
    # _; D- t' I+ H' R
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。+ a( h3 _  k1 y1 v
  96. 请输入“13”开启右上角的窗户。"! V, J  D+ s( V2 T" |& |- c
  97.   "  ①②③- I6 ]2 D$ f, @, T  v% k' T, P
  98. 1■ ■
    1 E4 M  [" r- X7 y8 M" r
  99. 2■ ■! a) j3 O- g( R: O+ [3 N/ L
  100. 3   
    6 w2 w/ m4 S: H3 p, Y3 }
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。9 U2 B1 W* B+ M$ |! w3 v
  102. 请输入“31”开启左下角的窗户。"
    5 v* P2 q+ T3 `2 K9 ~2 t! M
  103.   "  ①②③
    ) x9 P$ f- o. [8 Q3 ?2 H  x
  104. 1■ ■# w1 u- G: Z) V8 w
  105. 2  ■+ x/ k2 }( g7 _2 b
  106. 3■■ / P" b6 n5 w6 J3 A- N
  107. 此时,总共有5扇窗户被开启了。* ], }0 d+ i5 |
  108. 请输入“33”开启右下角的窗户。"5 Q4 J/ b% h- b  J" W' Z
  109.   "  ①②③
    6 }( ^& e5 ?4 D( l! J
  110. 1■ ■
    ( Z* @/ z0 T3 i+ I1 O) `
  111. 2   
    $ c& o" P: m: P& p4 C
  112. 3■ ■8 ]9 W1 h8 K/ A
  113. 现在,只有四个角落的窗户被打开。' F# S  w* K& e  t5 R- x; K% ^6 M
  114. 请输入“22”完成最后一击!"
    / B" B% [+ a1 r* X5 Q
  115.   "  ①②③
    9 G- i5 i7 z9 W5 Z* Q
  116. 1■■■+ u: ?  W. U* N3 N
  117. 2■■■$ i0 K& U: y  P# l& }$ D
  118. 3■■■
    8 |0 a, w, W  v0 N/ X6 b
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!"). B2 |( b6 c1 g& {
  120. 8 V& i/ R' \9 t! N8 Y) R: E( N7 ~
  121. ;;; 棋盘
    3 k  s( O, e( ]7 v; Z
  122. (defconst *wechat-5x5-white-chess* 12288
    6 o7 ^( Q  z$ o( E/ H
  123.   " ")
    7 _) v2 D! O' o# x" `# [
  124. + B4 z( ~+ ]" t& D% {% f8 A; h; w& n
  125. (defconst *wechat-5x5-black-chess* 9632
    ( N- I9 I( L& O! u$ g
  126.   "■")5 v& j; ~2 i. M& o3 M( w9 R- X. l
  127. 7 @- q( |* v! M& e
  128. (defmacro with-board (&rest body)
    1 G, j% @* v% e! N4 K% z/ g; ^
  129.   `(with-temp-buffer9 P, A) E! X# R0 w3 j
  130.      (unwind-protect
    8 M5 A1 P& N) l9 t4 z# x* N
  131.          (progn
    1 I1 b* j3 u3 j  e
  132.            (if (session "board")
    & @3 s- s; t' h7 o* |' S
  133.                (insert (session "board")))1 o- `; b* g5 [! g6 b2 D
  134.            ,@body)2 u" }  q' [/ c2 I- D
  135.        (session "board" (buffer-string)))))7 p4 b% Z1 [1 n2 t8 F
  136. * L# L; Z6 J! \2 n
  137. (defun board-init (size)# `; t1 M3 {! u. f( g* d. c; U7 {
  138.   (session "size" size)
    2 {; I0 `" P% `/ _" Z
  139.   (session "step" 0)0 C6 _7 K  D  k$ z' i
  140.   (erase-buffer)
    2 v1 ^4 J3 K" X$ x, T( i2 {
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨")): w9 u0 T6 f* Y! l" T6 y+ A, n, W2 Q
  142.   (dotimes (row size)
    7 R( N& F* b! X* F" U
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))$ S: b; J. T! ]. f

  144. % o$ s/ i1 G. F/ j, g7 }/ R2 v
  145. (defun board-contains-p (y x)
    8 Y1 Q5 u  v* h
  146.   (let ((size (session "size")))9 h! Y; v$ a( P, u* R
  147.     (and (<= 1 y) (<= y size)
    % @, I+ j+ t: {5 m- I9 y  T& j
  148.          (<= 1 x) (<= x size))))
    8 o7 C' p1 j' L) I; C) s( }
  149. * g/ n  C1 I! N. I" {" D: e0 x
  150. (defun board-toggle (y x)9 j( K1 t& f- K7 s. L
  151.   (when (board-contains-p y x)
    ! E8 r, B) y$ D' F# X
  152.     (goto-line (1+ y))
    + a. w/ B1 D$ U- n
  153.     (beginning-of-line)
    * u% s5 a9 X7 A
  154.     (forward-char x)  Q" R; s" I9 h! M
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    * F5 r9 P" p1 J; B  v
  156.                 *wechat-5x5-black-chess*+ T; e. y  f3 m) {  I% N' q
  157.               *wechat-5x5-white-chess*))
    " H, _& h  @  U2 D# R4 X/ w
  158.     (delete-char 1)))$ w! M' D! P1 L* y' Y- N9 p7 X. }
  159. ! {) |8 e3 h+ Z0 S
  160. (defun board-put (y x)4 ^: ^! B5 J2 ^7 O1 A$ P
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    1 A9 `/ S0 B3 @8 |2 `
  162.     (board-toggle (+ y (first dir))8 h: m* y" @7 `
  163.                   (+ x (second dir)))))- G( x! d3 O" Z9 O/ W. d8 q0 s& v+ j
  164. 5 I  c0 X# z7 r# X
  165. (defun game-over-p ()
    * {/ _/ Z: ^1 U9 D  n
  166.   (beginning-of-buffer)8 s& C) c3 s6 |0 x2 U9 b3 s6 T
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))6 _4 |) T9 L* P

  168. / R% M& C/ Y$ P+ R& Q, f
  169. (defun board-show ()
    . o3 e3 K* D& }$ [( k1 c5 f
  170.   (with-board0 X! [% u' N3 Y" m; A7 D& F
  171.    (concat (buffer-string)
    ! h2 v) F5 z) S- o0 ~
  172.            (if (game-over-p)- u  R+ y% M  C# h& P
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    3 v9 T* _4 [+ M2 I; }
  174.             (format "第%d步" (1+ (session "step")))))))$ p3 [. p1 c* k6 T3 g( t# z

  175. - e4 I% a+ B% |3 C8 \
  176. (defun board-position-parse (cmd)' g. Q% v% X; K
  177.   (if (= (length cmd) 2)
    ( s1 S7 }) V2 R6 ^5 I4 J
  178.       (list (string-to-int (substring cmd 0 1))$ Z0 R$ F' E2 n/ s9 ?2 k
  179.             (string-to-int (substring cmd 1 2)))
    ) S8 y0 ]2 J; E' P# B+ j
  180.     '(0 0)))
    9 @( [# S: d$ l3 o: D" t

  181. / L3 J* K8 e8 m; c0 x/ @! f9 G
  182. ;;; 游戏房间
      w  q$ W+ q- L5 Q6 u0 M/ G9 k' [; B
  183. (defun game-room-init (cmd)9 a6 i4 V4 w( j  y
  184.   (let* ((middle (string-to-int cmd))8 M' E3 ]; Y7 f1 R$ w% E
  185.          (size (1- (* 2 middle))))
      a8 k- r/ {# [- W% t9 p# S
  186.     (with-board( c3 O7 z/ y& t4 ?1 z% N+ F
  187.      (board-init size)
    7 l; V: n/ |3 p1 O, q
  188.      (board-put middle middle)))/ [$ K8 i* v. {. H) p
  189.   'game-room)6 q0 K* `, v+ d5 w6 q0 q" A% ^! L

  190. ! |4 Q3 s. D' V" }1 E0 Y
  191. (def-room game-room
    ! A0 `0 _$ U5 ~" n9 R% F2 S. v
  192.   #'board-show
    % E) R+ G7 ?2 q% J& q9 o
  193.   (t (lambda (cmd)
    $ ]: M' F, z8 n5 m7 _2 J  X; M/ I
  194.          (with-board, ?2 D, V  R9 \6 F7 u$ x. K
  195.           (if (game-over-p)
    / W' ^% C( D4 i5 v
  196.               'living-room2 Z% r4 Z, f" C* a
  197.             (destructuring-bind (y x) (board-position-parse cmd)0 A1 g+ O1 [! F4 n; E1 E
  198.               (when (board-contains-p y x)3 B8 l/ k8 n; D! ?/ g+ }# t; `
  199.                 (board-toggle y x)( X9 g7 ], Z' V) C, T! j. P) d
  200.                 (session "step" (1+ (session "step"))))2 g0 {4 ^3 a- a: K" L6 ^; |, k. y
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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