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

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

  1. - s* O: E8 p8 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;">;; 定义新的游戏地图
    # X0 l; F. L. b; H4 |9 V0 E' D
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL' c& }+ Z. ?( E: f; ^" u
  4.   'tutorial-room-0)                     ; 默认的入口
    0 O" M7 ^3 `- ], X9 V7 V4 q* V

  5. " ]$ v& j/ E0 {1 M" R* M: `- Y
  6. ;;; 游戏大厅- j" J7 k9 P9 T* F. F. X2 C
  7. (def-room living-room
    8 _) _3 @, o8 @! ?
  8.   ;; 进入该房间后的提示语
    1 \/ `4 r, C8 s
  9.   "1. 教程9 z9 Z7 U* r  t& n+ L+ b
  10. 2. 入门(3x3)
    9 `% B/ L) V3 r' Y
  11. 3. 初级(5x5)
    + h" B" X8 O$ c, C8 h% x
  12. 4. 中级(7x7)) V  J8 i; v! o9 e
  13. 5. 高级(9x9)
    . [- Z% o8 v0 H3 i! V5 p  n
  14. 0. 关于作者: G/ s; }7 p% b. O* X
  15. 请选择1-5开始新游戏:"
    % v) f3 ~) A! A9 X# c( @* M; V
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    ) U/ E2 b* W+ C& e* _: _3 q: n
  17.   ("1" tutorial-room-0)/ g2 \* \" `; f' z) n
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    + l: L. F. U) Q, P
  19.                                         ; 相应的返回也可以为函数,动态返回房间名6 J, V" E3 i* `) \2 U$ c
  20.   (t living-room))                      ; 如果条件为t,为永真
    . j, N8 G  [1 y, h
  21. ; `; X  A: i. o& m$ y7 z
  22. ;;; 作者信息
    7 a' s, ]' P) M* n$ j+ I( B0 [
  23. (def-room about-room
    " D+ M6 m# |* _$ O
  24.   "作者:redraiment/ _, n. O  u+ [4 L( E2 M3 n
  25. 微博:http://weibo.com/redraiment
    5 z8 y9 t# Y% O# N5 g- b2 J
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    5 p. b8 i5 X7 b$ T" z8 A
  27. 请输入任意数字返回游戏大厅。"; p- D5 N, q. ]' z: [4 D2 ^( s. ?9 g
  28.   (t living-room))
    2 ^7 ~& y) `3 m0 u

  29. 8 w4 f8 H( h' K9 D
  30. ;;; 教程1 t1 [, g# J; W8 q  e
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    8 B  u4 ^) r. J5 r7 u" e/ X% s3 [
  32.   "The number of tutorial rooms")' Z8 [: N% u, m) t9 \6 G. X& N
  33. 3 c" N" p1 s6 F4 o+ N6 B
  34. ;;; 简化教程的定义4 T& h9 O. X7 E+ p0 M8 P, [
  35. (defun string-last-line (content)
    0 \' |- R( ~" S: T" _
  36.   "多行内容组成的字符串中的最后一行"* d3 w. M: _9 G. _- A3 R
  37.   (with-temp-buffer' {% A! R% u% I  m4 \2 O
  38.     (insert content)" B8 p: k: r0 z$ A7 B0 F- M8 v: G8 ?
  39.     (buffer-substring (line-beginning-position)
    8 V: E  y1 v5 X+ {0 v/ d
  40.                       (point-max))))* V  M+ x0 g! @. V

  41. # [5 G( f, s; z* m( P! W  E5 V
  42. (defun def-tutorial-room (prompt)
    8 T6 B( z3 P- Z' }) H1 b5 E
  43.   "根据提示语自动生成教程房间。/ x/ v; j; v% v. H& R6 E% a

  44. 1 ^$ d* B2 o) }; B: g
  45. 1. 提取最后一行作为问题;' G& Z# C" K. ?* t# R. V9 z8 w, i
  46. 2. 分析问题,获取期望用户输入的内容;
    $ F% n5 V/ X, o: A
  47. 3. 定义教程房间和重复提问房间。"3 P. M6 a  z/ a1 U, v  [( e
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    , S$ W- t4 C$ H: D( ]( ?1 z: v
  49.          (repeat-room (concat room-name "-repeat"))* L; z0 O7 `5 |0 d% ~% ?3 I
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    ( n. J, z7 V8 H! o' k% m
  51.          (question (string-last-line prompt))
    9 l1 R0 d1 {, N  w. ^
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    # V1 U  `; I( T' J5 I
  53.                      (match-string 1 question)))
    + U5 t" E/ K* A
  54.          (doors (if except
      w4 l3 W4 J0 e
  55.                     `((,except ,(intern next-room))! Q- c! B6 `- t
  56.                       ("q" living-room)) @0 U$ Q6 K3 Q( _2 q6 W  L
  57.                       ("Q" living-room)
    % S6 P. p, i* Q
  58.                       (t ,(intern repeat-room)))5 ?& o$ g( \" R- L8 d: [
  59.                   '((t living-room))))): m8 b# D5 B3 _" T3 Y5 t. n: O
  60.     (def-room-raw (intern room-name) prompt doors)* M4 D+ N# Q+ l3 H' U, C
  61.     (def-room-raw (intern repeat-room) question doors)))4 }  B8 k; z  m; i
  62. ' a1 [! s5 l. h4 D
  63. (defun def-tutorial (&rest prompts)$ a2 B  b7 G! M. G- E/ {. j) r
  64.   "批量生成教程房间。"4 q" B0 I( F  }
  65.   (dolist (prompt prompts)
    , S" f- U6 c; s$ Z
  66.     (def-tutorial-room prompt)))
    5 f( O. W& N/ l6 b, v  e: D

  67. / n+ U' u, w1 ^3 r6 P2 I
  68. (def-tutorial+ V7 m/ L& V& `! [
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。7 k% o5 |3 ~4 C: t. F0 `/ H) V
  70. 1. 教程
    ) b- ~: l! y) n4 a$ D8 |6 h
  71. 2. 入门(3x3)
    / F7 M9 h9 i4 m' X) S
  72. 3. 初级(5x5)8 Z% S9 C/ m9 c( t' N  T2 T
  73. 4. 中级(7x7)8 U1 D9 _/ K" l, T3 Y
  74. 5. 高级(9x9)
    ; O1 E+ c$ U( {5 m1 E3 o: r8 u
  75. 0. 关于作者" l# i( o& z, T5 H: ^
  76. 请选择1-5开始新游戏:0 [: k- v' Z& {) _
  77. 您现在正在游戏大厅里。" O* V2 z8 `% Y* Q' {) u
  78. 请输入“2”进入入门级房间"
    9 }4 K" Y' K* ~( v6 }
  79.   "  ①②③
    7 @7 j+ [! l  [8 l% X
  80. 1 ■ 
    & ?# B8 H* U( X/ [/ H
  81. 2■■■
    + O2 P5 A9 J$ H5 }& M$ v9 C* |8 X
  82. 3 ■ 
    ( R2 w& e% C5 d/ y
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    5 v% W0 y3 b0 M; ^
  84. 请输入“22”来关闭第2行第2列的窗户。"0 L4 D* _- j9 {6 X. \$ O
  85.   "  ①②③& J" @& U+ [1 m/ m
  86. 1   
    # t5 z- H/ l3 _  x% d
  87. 2   
    9 m9 L7 p! q- B7 T5 P4 R
  88. 3   - }$ ^6 T% q% M) K, ]  G
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    ; U. z4 O7 Z( d+ D( l% I
  90. 请输入“11”,试着开启左上角的窗户。"
    7 k/ Q) w8 f& J( v/ a" m1 H
  91.   "  ①②③
    ( e9 P+ f3 O1 T" P
  92. 1■■ 
    ; b  G- }2 K& F" E  f9 B
  93. 2■  : C1 [" U7 k1 J# m2 u- m0 j( @
  94. 3   / K& e  E8 ~! h: s
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    ( {. \9 V  S  z- c9 I
  96. 请输入“13”开启右上角的窗户。"
      i5 e& n. ^+ X, q4 y; ^3 x
  97.   "  ①②③* B" t1 J* o- J2 R  n6 k/ i9 _) k
  98. 1■ ■) U9 S: F. x" O! p) p# c1 v5 g) v8 [
  99. 2■ ■5 k' O) T/ ~0 b+ f- n
  100. 3   $ a) D2 A+ a! v( J+ e
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    5 X6 E& n6 A: I7 L8 V( h, v0 G. n
  102. 请输入“31”开启左下角的窗户。"
    7 G1 A+ k4 K, v8 k7 F# x% u/ T  k
  103.   "  ①②③
    9 `  N# d3 t9 X
  104. 1■ ■8 q! Q0 s% n; o( P; h$ o7 R7 I
  105. 2  ■: k4 Q1 b! y' n
  106. 3■■ 
    5 Y0 B, s2 z" w5 W. K
  107. 此时,总共有5扇窗户被开启了。, M  U" @$ |/ [1 J" D) G' L
  108. 请输入“33”开启右下角的窗户。"; n1 W9 V4 V( j1 ~
  109.   "  ①②③
    3 d9 ^: ?- m5 c% T7 C; [2 {1 _# J: z
  110. 1■ ■; l7 x, C# D' u' a
  111. 2   " I5 E* c! _3 [- [& ?& s) c; j" o
  112. 3■ ■
    - F. G+ e' d5 L/ M: V1 ~
  113. 现在,只有四个角落的窗户被打开。* A0 \( N% U4 W
  114. 请输入“22”完成最后一击!"1 u. x0 {  s1 l& S* o8 y* @& |
  115.   "  ①②③" l& Q( w7 S0 l# D0 S9 m
  116. 1■■■) ^/ G: c7 Z- q3 f: }
  117. 2■■■6 P8 o* J  n( ~6 O. P2 d
  118. 3■■■" s! {4 K  g* U  N
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    / i6 x! W7 J* _+ a$ ]5 o" y
  120. ' {/ m9 }+ O7 y! v' ^) m+ ^2 {  ?
  121. ;;; 棋盘
    7 G! K  F# F8 I; r* t% D
  122. (defconst *wechat-5x5-white-chess* 122883 G5 [2 @% N5 y: [4 K3 p4 \- _
  123.   " ")
    , {. Z/ k: o3 c- J1 ~* U

  124. 5 z# M/ v5 N8 _. Z& C. T6 s, v( F% h
  125. (defconst *wechat-5x5-black-chess* 9632  {# Y* Y7 W+ h  n
  126.   "■")
    % S: T# l% m+ a# r3 h( n

  127. 5 p1 D; k0 S$ H
  128. (defmacro with-board (&rest body)
    0 A6 W+ q" T. S* p. X1 R
  129.   `(with-temp-buffer
    ; o  ]. G$ ~" }' F, d. \
  130.      (unwind-protect" Q' g, W* f, E3 @; R/ L: Q# b
  131.          (progn
    1 h& G, X1 U7 o
  132.            (if (session "board")
    * J% Y& ^8 l/ U. w/ z
  133.                (insert (session "board"))), o2 i# e# k3 F& s) N) j
  134.            ,@body)
    * i/ j4 @$ z) w& O/ {+ j4 ?2 B
  135.        (session "board" (buffer-string)))))/ K6 h3 ~4 F0 W( O4 ]) y

  136. 5 V* Z# c( F# x: h( \7 N7 N
  137. (defun board-init (size)
    $ Q, T- X5 |) [  L) u
  138.   (session "size" size)
    7 F: o- G1 l7 ^. ^7 r
  139.   (session "step" 0)2 q/ X: N1 S" ~7 _4 u0 n
  140.   (erase-buffer)3 C5 q) k' m9 V* V! J9 j/ l1 {
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))# ^% ?6 Y- W7 P% Y( P4 k2 I
  142.   (dotimes (row size). y' h* E! d8 t& b* [( g# i# J
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))" f* t& x- {9 }& _& y) C. r" l
  144. 7 O, J- C$ Q; p. T  r( Y
  145. (defun board-contains-p (y x)
    ; W  R  m7 Q: O. j, i
  146.   (let ((size (session "size")))
    ) v& H' O: Z; P7 L7 n
  147.     (and (<= 1 y) (<= y size)0 N$ |% X! y4 b' C  M5 h, y8 U
  148.          (<= 1 x) (<= x size))))$ H) ]' K9 g1 }4 s
  149. ) [3 x! F; C5 ~, X/ ^  C
  150. (defun board-toggle (y x)6 f3 z" v, [6 k6 B8 h
  151.   (when (board-contains-p y x)
    2 C$ o' }1 `7 t5 d& B% l1 i0 [
  152.     (goto-line (1+ y))! o' G2 Q  R# u# X
  153.     (beginning-of-line)
    , E0 E3 v$ b7 W: o* z5 m9 n% ]
  154.     (forward-char x)
    3 C: |" C7 y" E% Y
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))4 U4 d3 s* n+ c* j( d" G3 ?
  156.                 *wechat-5x5-black-chess*1 k- i! F7 S) E
  157.               *wechat-5x5-white-chess*))
    ; u- r. ]( t5 a# [6 i
  158.     (delete-char 1)))0 g. O  H( a" l+ j. A8 E
  159. * u% a7 B; m% F; Z3 I
  160. (defun board-put (y x)
    # n( z0 F1 Z: b% R0 b4 `4 b! z
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
      k( D& _( [- z- R: _" X9 v# ^( t) Q
  162.     (board-toggle (+ y (first dir))
    + }0 |% S: `5 y: K1 t3 r
  163.                   (+ x (second dir)))))$ t6 Z3 }# X$ |, b& T+ Z

  164. / O/ `, y, `! s. o* v7 {" F  Z( E
  165. (defun game-over-p ()8 l) n! @3 o% D: c+ Z& q
  166.   (beginning-of-buffer)5 I, G: t7 Q' H8 v" U
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))' H# `$ H* ~0 }7 Q( [0 D
  168. 5 I, L0 M" f- P' W0 N9 U4 e
  169. (defun board-show ()6 v5 y" L( E- A
  170.   (with-board2 b; k9 A" H7 D6 K& ^
  171.    (concat (buffer-string)
    ) g; z' o/ P! Z& o4 S6 }. f- m5 L
  172.            (if (game-over-p)
    6 C- ~/ C9 h- J6 ]. {
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))6 j" r3 W3 ^$ J- _4 w9 K
  174.             (format "第%d步" (1+ (session "step")))))))  [8 r8 m: ~) b; R6 m3 r
  175. 3 D7 {: C0 E/ V
  176. (defun board-position-parse (cmd)
    + V+ P) `  ~  |# P
  177.   (if (= (length cmd) 2)
    * _2 X3 u, a8 @  Y% }# h: }
  178.       (list (string-to-int (substring cmd 0 1))
    + s" C2 z6 t" Y3 W
  179.             (string-to-int (substring cmd 1 2)))$ o. X/ L* t0 q' r
  180.     '(0 0)))( a& B7 g) o+ `0 G( \) b7 @
  181. % e) ~$ {' O9 p! }. A. O
  182. ;;; 游戏房间
    ! `" x0 s+ Z1 v9 [( A
  183. (defun game-room-init (cmd)
    7 a& E) e: k9 K
  184.   (let* ((middle (string-to-int cmd))
    ( z' W; T6 P0 a$ O! z; p& M
  185.          (size (1- (* 2 middle))))9 {' W( S! a) i6 Z
  186.     (with-board
    3 B4 n0 c- F  @; Q3 u" x, v; J: C# a
  187.      (board-init size). e4 f) P3 d; C* c
  188.      (board-put middle middle)))
    ) T2 [1 q& m! O* y9 K. _
  189.   'game-room)
    7 L4 u7 R4 S: i. R- _. C9 U, m0 t

  190.   y$ b2 ]. C/ d
  191. (def-room game-room. q4 d& D) ^7 _9 q$ P
  192.   #'board-show# @: z- ~3 C- x$ ^# g3 O2 e" T
  193.   (t (lambda (cmd)/ @. d& m) ~) @
  194.          (with-board
    9 p( f9 h) {( f* [' B+ O
  195.           (if (game-over-p)
    ' _7 A) s, E* h2 D9 n+ C9 N
  196.               'living-room
    $ {" w4 y2 O6 ^7 {* ?  w% }: q) z
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    " r( G- a6 D0 \, x
  198.               (when (board-contains-p y x)
    0 i* [5 l8 o" F& O
  199.                 (board-toggle y x)
    - L' u; o! X- M. Q4 |
  200.                 (session "step" (1+ (session "step"))))# `; M0 T, c$ j8 R4 H6 h; R! Y+ Y3 _
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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