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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。5 o2 v, y* R- E0 u2 Z" m& s
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. , u5 F- W2 _, w2 S( a! P+ @
  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;">;; 定义新的游戏地图
    9 u" w) ?8 x/ g$ c: z) f* S
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    : I* Y  s  o- H# P+ ^" l
  4.   'tutorial-room-0)                     ; 默认的入口
    ! N# ]/ r$ Z4 D9 ~
  5. / \( x( p+ O: V) f6 k
  6. ;;; 游戏大厅
    + f+ V! G# U9 c$ ^( ?( s4 J* v/ o1 j
  7. (def-room living-room
    7 G/ d. ]' @+ n0 l6 W* n
  8.   ;; 进入该房间后的提示语# @5 L- g; P! n) ?  Q$ U/ T
  9.   "1. 教程3 l/ I! q$ E4 m' E6 v! f
  10. 2. 入门(3x3)
    1 m4 a( e$ F& V* k) b" C  x
  11. 3. 初级(5x5)
    7 @" M. M( H! j3 d7 N
  12. 4. 中级(7x7)
    ( V6 t4 N) K; E7 R6 F1 ~
  13. 5. 高级(9x9)
    3 r# Y0 Z) r! N9 ]
  14. 0. 关于作者& y# k; f0 |! [# B  A: u2 q
  15. 请选择1-5开始新游戏:"; Q! S9 c# G3 [& `7 ~
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    * Q4 z" _" Q1 y
  17.   ("1" tutorial-room-0)2 o+ F% N; e) ?  E7 _7 q; @" M1 Y/ W1 T
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配' W; K# r4 i7 R, }* c6 L
  19.                                         ; 相应的返回也可以为函数,动态返回房间名. m# v9 n# `$ e2 D4 s- Z7 \. o# M0 b
  20.   (t living-room))                      ; 如果条件为t,为永真  z. E7 B- G/ J: \# J5 e
  21. % S# J+ c6 w: ^8 u% e
  22. ;;; 作者信息" j6 m2 R) j4 e  }; R
  23. (def-room about-room
    , E/ [; |$ y4 [4 T, f  w& x
  24.   "作者:redraiment/ {, M' _: e$ `( [/ O! r  H
  25. 微博:http://weibo.com/redraiment
    9 }( Z4 d7 ]; o: E3 e. E
  26. 有任何建议,欢迎在微博或微信上联系redraiment。; I/ J* q, z' f6 Z. |5 ~8 q
  27. 请输入任意数字返回游戏大厅。"+ `, C( x* u$ i4 y- a
  28.   (t living-room))
    0 j8 B9 {: b  ~; I9 `' ?

  29. 0 ^  D8 m) j) q- ?' ~
  30. ;;; 教程
    % j# a7 Y0 R7 l# S/ R8 t; m, B5 m5 I
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    ( r4 [# @6 t) _7 n4 g0 h
  32.   "The number of tutorial rooms")
    $ F- v1 h, s- B- J6 N' |# \

  33. 4 K" m. K# A: F6 \* M+ t$ |" p
  34. ;;; 简化教程的定义
    ) {' e& F" H" R$ U
  35. (defun string-last-line (content): q9 k5 ?$ P9 R( r! O# w% Z
  36.   "多行内容组成的字符串中的最后一行"+ q4 V: B" Y9 u# [9 j  M
  37.   (with-temp-buffer
    3 E4 y8 u2 b! }- K
  38.     (insert content)  E+ u, c- ]" T+ U5 Z- u
  39.     (buffer-substring (line-beginning-position)
    7 N  P3 R3 `9 |) c: b# {, a' @
  40.                       (point-max))))  c+ O* ]* r, m! x% J
  41. * s' O% x1 }6 e: |
  42. (defun def-tutorial-room (prompt), M3 g+ J. [8 v4 S
  43.   "根据提示语自动生成教程房间。1 l1 Q; T* z1 t+ X8 y8 f  x

  44. 0 w+ G. k  [! c% P+ z
  45. 1. 提取最后一行作为问题;: _9 G' V7 W$ ?' p7 h# {
  46. 2. 分析问题,获取期望用户输入的内容;
    " G7 ^) Z$ n, m' U  d2 B/ j0 T
  47. 3. 定义教程房间和重复提问房间。"
    # o. y3 z* a+ n8 `- r% F
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))6 a+ k# }1 G4 c3 s9 j+ K
  49.          (repeat-room (concat room-name "-repeat"))
    9 D* L+ |1 S; r
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))! ^! B! ~. Y" M0 T
  51.          (question (string-last-line prompt))
    + \5 d% s1 o6 ~  c, B1 W2 X
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)! m# I  j0 V6 g+ B# b
  53.                      (match-string 1 question)))7 D4 c& A7 {$ o& r6 o; _+ s
  54.          (doors (if except
    3 h. N+ o2 u5 |8 N3 D
  55.                     `((,except ,(intern next-room))
    ! u: w3 Z7 {* V7 \5 g! M
  56.                       ("q" living-room)
    & T( `6 r- u: G3 j' E
  57.                       ("Q" living-room)& n( f4 w& \1 P* \6 L6 k0 m
  58.                       (t ,(intern repeat-room)))
    + ]% x! i- ?5 b1 n
  59.                   '((t living-room)))))
    2 q- W* A+ w4 ~0 W! B, K0 N  L
  60.     (def-room-raw (intern room-name) prompt doors)
    % T+ {( P4 Z. M" c: d, c* \
  61.     (def-room-raw (intern repeat-room) question doors)))
    * D4 b3 z6 \+ p/ w

  62.   `. k9 a; _( c4 e
  63. (defun def-tutorial (&rest prompts)
    7 G$ }3 V1 W1 T( W1 J
  64.   "批量生成教程房间。"$ [& F; i, Y8 U3 a# `1 g" n
  65.   (dolist (prompt prompts)
    9 p6 U; o7 z% U  c  [  i0 p; o$ W
  66.     (def-tutorial-room prompt)))
    8 B4 n9 Q5 U. Q; c+ A, a

  67. # q# f) J2 e; J" j& H
  68. (def-tutorial
    ; e; G4 w5 H7 r9 @+ {( B
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。! c3 q6 g  V, k! a. M
  70. 1. 教程
    + L+ j6 b7 U. |
  71. 2. 入门(3x3): y* I6 f9 H; ~+ Q& }6 C
  72. 3. 初级(5x5)
    + y  m! e5 [$ k- X" y# P) J" }5 T* o
  73. 4. 中级(7x7)
    , ^# p& s4 P' w5 W! X# s" }- _
  74. 5. 高级(9x9); U: h' E$ A9 s0 i& C' B
  75. 0. 关于作者2 y# A4 t) A: Q: }
  76. 请选择1-5开始新游戏:
    9 {; |$ ?$ n5 p6 v: V
  77. 您现在正在游戏大厅里。
    , Y# j3 X9 `9 X  n& K0 O; y$ Z
  78. 请输入“2”进入入门级房间"
    ' s( |, L: N$ v% S% r( _& u1 Y
  79.   "  ①②③: X7 J* [- e: [; m
  80. 1 ■ 5 X1 E' Z% c% W! I8 c
  81. 2■■■
    0 a1 o. ~9 Y6 b( t
  82. 3 ■ 
    & ]9 x7 v7 z7 |* j" [+ K
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    : u5 B) k: B. W; E9 A
  84. 请输入“22”来关闭第2行第2列的窗户。"9 I& O; l$ k" P
  85.   "  ①②③3 W% {7 _6 U- k% I* L; d, H
  86. 1   
    8 x' D; n) L' a$ B: F$ }
  87. 2   
    6 C( h5 Q& F6 X: P6 o$ C6 W
  88. 3   
    3 G5 {4 M$ N& m, ^& p8 r
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。) b% U) @2 K4 r8 I6 U' q
  90. 请输入“11”,试着开启左上角的窗户。": m, d! l& Q' E0 @; A
  91.   "  ①②③
    / A( v  d7 F9 m8 \, f- B
  92. 1■■ 
    + m+ [, V/ g' b* D/ }) b- u
  93. 2■  
    ; X2 o* R. p+ D- t; J
  94. 3   9 b& [$ j) n0 ^& n6 I
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。' e" Z9 {6 [% s1 y- A' u
  96. 请输入“13”开启右上角的窗户。"
    4 j2 H) ?& ?! A1 @4 x, H
  97.   "  ①②③' `7 S2 R8 T, V2 b
  98. 1■ ■
    ( A% ]  H) Z! y2 Q& r
  99. 2■ ■
    " ~! N( p7 D" D/ _& ^
  100. 3   
    3 w4 w8 @$ ]7 o$ u) N
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    2 ^0 f! S6 P, D5 U
  102. 请输入“31”开启左下角的窗户。"
    " k% ?8 |/ v8 Y
  103.   "  ①②③9 U$ m8 l2 a, [+ [! V
  104. 1■ ■# i* I( ~& T1 Q( n1 n
  105. 2  ■
    # L+ B* z9 k3 {0 P3 B
  106. 3■■ 
    % G) W' F8 x1 E! p$ c$ l
  107. 此时,总共有5扇窗户被开启了。
    3 O5 u  G0 F  f4 a! ]3 O
  108. 请输入“33”开启右下角的窗户。", [- x" t6 `8 o0 n
  109.   "  ①②③
    0 l# f1 S, b+ m0 B/ I
  110. 1■ ■
    ) r+ `/ b, j3 m# l
  111. 2   ( h, B2 [" h0 c* g/ V9 W
  112. 3■ ■
    7 U; x* j8 _: d! _$ w3 s
  113. 现在,只有四个角落的窗户被打开。% t' {, g" c& h; c6 i& h8 g- C% E
  114. 请输入“22”完成最后一击!"
    3 Z# i8 h4 n# n8 N
  115.   "  ①②③% L" j( @; ~+ S
  116. 1■■■
    3 y8 ?9 T7 W& m4 ~& Q  J
  117. 2■■■# E( `1 \( H+ a* L7 ?
  118. 3■■■  y& @% u3 {$ a, S
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    " f! d- m" ?# L8 o+ A3 i3 n

  120. ( j2 w6 t* ?$ t. u+ f
  121. ;;; 棋盘$ G& _( [6 M5 |0 Q+ K5 q! I
  122. (defconst *wechat-5x5-white-chess* 12288' J- c- a9 ?  m  m
  123.   " ")& E: H  I9 r+ s3 H/ c
  124. ! F" Z  V; A# e* |* K- h
  125. (defconst *wechat-5x5-black-chess* 9632
    ( X+ O  ?' D( c: [/ k% c: X
  126.   "■")# G8 C: [; M3 O) [9 P3 d2 j) X$ k
  127. 6 b/ o, ~( ^8 q& K5 l& R
  128. (defmacro with-board (&rest body)
    3 `+ V1 O0 e1 _9 ?7 ?! o
  129.   `(with-temp-buffer3 s# p; `: c. J3 J5 z
  130.      (unwind-protect( [) d; N) e, R2 }
  131.          (progn
    * }" y) S- h: B' H' F* }
  132.            (if (session "board")
    6 b9 K+ u( D7 E4 N6 G* f
  133.                (insert (session "board")))
    # p' i; g2 S0 ^% t) e' J" g3 E% G
  134.            ,@body)2 @9 L; ^4 Q; W' Q5 m8 x3 d: K
  135.        (session "board" (buffer-string)))))
    7 A2 {* z7 Y4 F7 Y( R4 D

  136. * \  `, P) Q, ?
  137. (defun board-init (size)+ Z8 v! z1 s5 [* W7 e
  138.   (session "size" size)
    9 H; ]/ m( d4 }; v! C
  139.   (session "step" 0), p  z9 I; j; D/ C; k) H
  140.   (erase-buffer)
    3 r5 F* y) s& J" R# ^& V
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))( R# l3 w& e5 l# |
  142.   (dotimes (row size)
    ( f) I, G; `  Z+ A( t9 }& w& r; m
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))( a1 f) p  c, o5 G

  144. 1 A) U6 H4 v) y
  145. (defun board-contains-p (y x). B5 s9 V5 ~# k+ F& Y) \
  146.   (let ((size (session "size")))
    % r3 M" \+ Q/ ]/ S  I4 g
  147.     (and (<= 1 y) (<= y size)
    3 ?+ r. R9 w! k+ K+ p
  148.          (<= 1 x) (<= x size))))
    , N% q/ }; a  h
  149. 4 Q9 [* O* c: J) V" X: e* R& C
  150. (defun board-toggle (y x)! K& ?- M# L! f/ p( M" e( Y; Y# {
  151.   (when (board-contains-p y x)+ I6 p5 }4 h* y+ N+ m4 t( y8 Z- m
  152.     (goto-line (1+ y))  L  t) L3 C6 Z* Q+ n, i! L; H9 R
  153.     (beginning-of-line)
    ! X! z) _# k1 N# M  q
  154.     (forward-char x)9 W! v, R! O3 B2 U. z7 m
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))& X; f8 [, d2 B( o. m0 H1 S6 ^
  156.                 *wechat-5x5-black-chess*
    0 ?/ x. S: s6 O1 t. ~
  157.               *wechat-5x5-white-chess*))
    2 I* A4 g1 F6 S0 D2 @3 W; k
  158.     (delete-char 1))): ?/ b' S! A. h2 E3 v
  159. ( e, k. Q" v, W- ]8 A. r
  160. (defun board-put (y x)7 y. Q+ Q! F% c& p6 ?" u( W6 p
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))+ Z- G* ]/ e% u; o1 ]
  162.     (board-toggle (+ y (first dir))
    2 ^" g8 n7 f( y* W3 U: R3 f% x
  163.                   (+ x (second dir)))))# A/ S2 P0 k+ `6 A+ K

  164.   \6 w2 m- z' v* m
  165. (defun game-over-p ()  ?+ `2 ?2 r1 p" o2 E7 a/ z8 a
  166.   (beginning-of-buffer)+ o  g, N9 }) Q  E6 x% Z. F. |
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))& T0 T% z! N( d5 d$ b

  168. # R" N0 F+ r7 g  i
  169. (defun board-show ()
    3 X' Y1 i* G: w5 N1 P
  170.   (with-board
    6 U3 W3 H& m$ `6 g9 H9 ~, ^5 l
  171.    (concat (buffer-string)9 j) p8 g8 o% t6 A% J  D; v
  172.            (if (game-over-p)1 M+ i2 m; h- U: N3 A9 h9 ]
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))4 K$ F. z+ m9 H# ~/ c
  174.             (format "第%d步" (1+ (session "step")))))))
    ( c) C, v7 j/ W% u, y
  175. , s* }8 N2 }& M9 o# u# S: ]
  176. (defun board-position-parse (cmd)
    : P& ^8 ]- [  e* v: h
  177.   (if (= (length cmd) 2)& Y$ H4 g# ?# _" U& z# w
  178.       (list (string-to-int (substring cmd 0 1))" u3 X6 {6 Y; r- ~: T. N0 p8 b
  179.             (string-to-int (substring cmd 1 2))), @, Y' t( J8 {
  180.     '(0 0)))% Q% @' M: e* Q: t
  181. 5 Q! T4 F: K0 i: Q0 ?2 C
  182. ;;; 游戏房间
    * g! ^" k% ~8 O) Y: c
  183. (defun game-room-init (cmd)* R% p2 {, C' u/ l* z' v9 k
  184.   (let* ((middle (string-to-int cmd))* \  y, v* E2 @% J, c# L& ^
  185.          (size (1- (* 2 middle))))6 D! K* f9 E) x$ l
  186.     (with-board3 p0 G7 Z1 H, a, Q% F8 ?0 H
  187.      (board-init size). S5 v% c, H" q) L- c$ p
  188.      (board-put middle middle)))
    % f7 \# m, ?4 h# z1 N
  189.   'game-room)
    8 W* W! G- f! x; j$ m6 T+ U9 O
  190. ) t& x9 D0 _$ \. P4 B" n' v3 T
  191. (def-room game-room
    6 H; T1 D5 |9 f+ y/ f
  192.   #'board-show  w: W$ r$ f) {+ ]9 D9 A
  193.   (t (lambda (cmd)6 z( w3 }) _1 d4 j$ Y/ [6 o. A" V, m
  194.          (with-board
    4 n2 r& j2 ?9 [1 ]  z
  195.           (if (game-over-p)
    0 s0 \$ ^* W3 G: I' {/ ?3 s
  196.               'living-room
    : K1 F9 s8 Q- o) L8 T
  197.             (destructuring-bind (y x) (board-position-parse cmd)0 Y! x; L2 f0 U6 `
  198.               (when (board-contains-p y x)
    & V) y. y. U4 o7 [2 @
  199.                 (board-toggle y x)
    ' c$ K/ o7 o! e- H9 m, U: K! a
  200.                 (session "step" (1+ (session "step"))))
    & d, Q. N. k1 _2 h1 e1 q
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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