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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。' J7 J6 ^5 I: I. H: L9 N: W
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. " ~, S) u# P, b& j  X( I! 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;">;; 定义新的游戏地图
    6 j& }% |! s; e* }0 ^# L
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    ) G) P* Z) e0 Y! G2 O: c# l
  4.   'tutorial-room-0)                     ; 默认的入口$ i2 U1 V. w9 r* h* @2 M
  5. 3 s9 L0 z3 n3 `6 t( Z2 y. J9 r; u7 `
  6. ;;; 游戏大厅
    ) B& Z' {1 p; O6 u$ f( N& c- [
  7. (def-room living-room) M) c6 n$ N! u9 N' D" f6 O7 @7 T
  8.   ;; 进入该房间后的提示语, Q3 t% z9 L) J* p4 X2 I, c
  9.   "1. 教程
    5 Y) h. C6 y9 I
  10. 2. 入门(3x3)+ b2 j4 _( F$ c1 K! }
  11. 3. 初级(5x5)
    2 G0 Y& H- g* V- p6 x
  12. 4. 中级(7x7)7 D/ p5 a1 }1 B  v
  13. 5. 高级(9x9)
    + |6 ~/ p' I5 j, X
  14. 0. 关于作者
    ; D9 c) ?9 D1 ~1 s* V
  15. 请选择1-5开始新游戏:". X8 V8 ^+ D% @) z, A1 p' q% `
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名% T9 c; m& w. \# w$ Z# T
  17.   ("1" tutorial-room-0)
    4 |) |  L4 A6 Y; S8 G  E! K
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    4 |7 G4 n% {1 H- {
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    , O, B; e! Q( [% l$ q: N
  20.   (t living-room))                      ; 如果条件为t,为永真1 m# Y$ }3 `$ t5 W
  21. ( Q- B* p# ?+ s' A' c' k7 A
  22. ;;; 作者信息
    9 C: |' m, Z5 K  R9 n
  23. (def-room about-room& g5 v9 W, E7 l- ~
  24.   "作者:redraiment+ x' ^" w6 M1 a2 G; }4 @
  25. 微博:http://weibo.com/redraiment. Z' }3 o7 V' ]. r, \' t
  26. 有任何建议,欢迎在微博或微信上联系redraiment。; z3 D8 r. \/ ~+ ]9 r# j
  27. 请输入任意数字返回游戏大厅。"
    - g8 J( F3 ^, c, g$ B
  28.   (t living-room))
    ( u- [7 f5 g' {" ~

  29. ( q- ^, d* H% o# H
  30. ;;; 教程
    7 X2 H* e% r: W& e
  31. (defvar *wechat-5x5-tutorial-rooms* 08 N5 T8 I+ ]! ~* J! _
  32.   "The number of tutorial rooms")9 ]1 k- J$ B& j5 O3 r& r$ v; d/ I

  33. 1 u2 n  P0 k' U" |+ l5 O. w; \0 ^3 R
  34. ;;; 简化教程的定义4 I; F! I" B( k. Z6 b  s3 p
  35. (defun string-last-line (content)
    ) B* k  e  Z. v. O- ~
  36.   "多行内容组成的字符串中的最后一行"
    2 a! y. ]) a) j. o
  37.   (with-temp-buffer
    % _& L: m6 [1 u" N, D
  38.     (insert content); M8 ~' k2 Z# U  h
  39.     (buffer-substring (line-beginning-position)
    ( K5 I% L- j" Z, ^8 {
  40.                       (point-max))))4 n; N& Z( \) n, O, g
  41.   Q7 X$ ?( D$ }( m! R( Q8 r1 y
  42. (defun def-tutorial-room (prompt)
    ; a% g4 w- B! s$ i* j$ ]7 ]( E3 y
  43.   "根据提示语自动生成教程房间。; H( G7 h; O( |3 B; w; w

  44. % r, A' y) e" ^/ a* Q' `9 o
  45. 1. 提取最后一行作为问题;
    - b" s6 m$ h% T4 h! t) c
  46. 2. 分析问题,获取期望用户输入的内容;
    0 a: t4 ^- `2 P7 c( N
  47. 3. 定义教程房间和重复提问房间。"  j" y+ h; m. N  Q
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    # j9 m" t( `/ d. o& M3 L  r5 ]
  49.          (repeat-room (concat room-name "-repeat"))& ]. b9 Z. i: i
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    , i7 ]  r' b! L( Z* N
  51.          (question (string-last-line prompt))
    ; I4 l) e0 I9 @) G: U2 z" s2 t: W
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)2 d+ W$ V% s, Z. P% L3 p
  53.                      (match-string 1 question)))# R+ _4 }! q3 W9 d1 e5 i1 }! b
  54.          (doors (if except
    ! m5 x6 J4 \! e6 m# R  j
  55.                     `((,except ,(intern next-room))1 A& t  c5 B/ o. R$ j. i
  56.                       ("q" living-room)0 I/ m0 T6 O7 X- o
  57.                       ("Q" living-room)
    $ g9 s, _! [  t% h! Z" p% r; x
  58.                       (t ,(intern repeat-room))); H- w* j7 g) d2 C: \
  59.                   '((t living-room)))))
    7 H: a+ W# G( w! N" X* e2 y
  60.     (def-room-raw (intern room-name) prompt doors)+ U: ?  B  O: ^. [0 Q
  61.     (def-room-raw (intern repeat-room) question doors)))
    ) S/ R$ k7 t# N; d4 b
  62. 5 x$ Y; k. q. m) n1 V$ ~7 r0 `
  63. (defun def-tutorial (&rest prompts)0 m6 i# t% `! d0 a
  64.   "批量生成教程房间。"* m; h: H2 G- O/ i0 C
  65.   (dolist (prompt prompts)
    ! Z2 a" [, e) f; z
  66.     (def-tutorial-room prompt)))
    9 M2 C: b4 y5 ?, X. ?
  67. ; E* C+ [9 N: w
  68. (def-tutorial6 r* l& N* c9 i  [0 t  o
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    ( u% Y2 M, d! J& c4 L
  70. 1. 教程
    ' g; v% L2 c6 `
  71. 2. 入门(3x3)4 C) V" @1 S! \( T$ V0 M
  72. 3. 初级(5x5)2 g8 G7 S) F! z* {$ U' E( j( I, r) H
  73. 4. 中级(7x7); j* d+ {! {) F9 H. {4 K! p
  74. 5. 高级(9x9)7 r% J0 P! F1 Q8 E
  75. 0. 关于作者
    " {6 j" ], k: M% o. N- a4 b
  76. 请选择1-5开始新游戏:$ y- F* r8 D3 n$ x+ ~! S
  77. 您现在正在游戏大厅里。
    # p7 B3 K3 [5 v# p
  78. 请输入“2”进入入门级房间"- b8 x2 G9 C  w" O& l9 ~* V
  79.   "  ①②③
    0 I8 V0 H/ m/ m+ O" A7 }
  80. 1 ■ 7 K+ y3 O! K: H8 D
  81. 2■■■( N$ M$ y- k. N5 e& f! `( x( F) \
  82. 3 ■ 
    5 I! ^0 p! U2 d3 F) m; i
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    ; g0 e. S- o8 f. {
  84. 请输入“22”来关闭第2行第2列的窗户。"
    ' C% E  r: l/ q0 C
  85.   "  ①②③
    8 W- V! X2 n$ z) Z
  86. 1   ( L% U$ S) I( F5 k5 r' }
  87. 2   ; e+ w' R5 V; c4 i+ X
  88. 3   
    1 G' o0 s6 \0 |( x( p5 I1 B
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    2 V1 [2 c8 }. p, D( d" h
  90. 请输入“11”,试着开启左上角的窗户。"; L# {7 y9 ]7 Z# f# P4 ~& a- Q
  91.   "  ①②③. {2 Q! k7 a" u. J$ s, Y
  92. 1■■ 4 K! L7 k  }6 i! J" i+ X
  93. 2■  
    6 l. I+ w# A' I- ^3 q& S
  94. 3   
    2 ~1 l! H, Z7 W6 J
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。* A6 e) ~2 x$ h1 Z3 b: m. {
  96. 请输入“13”开启右上角的窗户。"0 H, A- h# _  x2 r! Z- b) \/ G( A
  97.   "  ①②③: k- S+ a1 d5 `1 D# K
  98. 1■ ■1 v2 a! @0 n, Y4 |. \
  99. 2■ ■  V0 H  c. Q) |5 p* U: ^
  100. 3   
    * X3 A& Q$ S4 U1 ~. P4 g% |
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。( f$ f( k! O+ Z
  102. 请输入“31”开启左下角的窗户。"
    & b' r# q2 U8 u
  103.   "  ①②③1 G& m  {; |' i0 z" L" I
  104. 1■ ■2 [- I' d9 k- ]$ n
  105. 2  ■
    # V! g0 Z: |1 a2 d
  106. 3■■ 5 Z3 U, T; c3 I! I# o0 s
  107. 此时,总共有5扇窗户被开启了。
    + j; z4 f9 P: A) E
  108. 请输入“33”开启右下角的窗户。"% @5 D- A! I0 G. ^8 H3 ^
  109.   "  ①②③
    - z$ O8 }5 y( z! t
  110. 1■ ■
    & m. e& p+ e* q2 x4 z
  111. 2   
      I( j" I' q* _* {" L
  112. 3■ ■2 }4 e. ~2 i- q& j* r
  113. 现在,只有四个角落的窗户被打开。) B! J, s- i+ t  o6 X4 `7 t2 v
  114. 请输入“22”完成最后一击!", r8 N5 ]# S. q- X$ V! e
  115.   "  ①②③
    & c  a, m- K; v  I: D) f
  116. 1■■■& M6 G  [: R: w% w1 P2 u! A
  117. 2■■■( W/ E0 F7 A) X; ?7 f6 y& |6 x) [
  118. 3■■■' q% R5 r- A7 }6 e; F6 v# X: c
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    + z& \, U* w% w7 N2 w
  120. , t( b5 D2 |- m1 H- u
  121. ;;; 棋盘
    : z4 ~9 m  K# p+ L
  122. (defconst *wechat-5x5-white-chess* 12288
    9 c1 Q. E. l+ q2 p* T' c4 R5 c
  123.   " ")$ Z/ m5 t0 g0 F8 Q& y( z3 N9 R

  124. . g- q/ Q6 |# L0 J  k
  125. (defconst *wechat-5x5-black-chess* 9632" b1 P9 u  S: s  B
  126.   "■")
    2 P3 Y( t) M/ q3 d: q5 l$ ~' b

  127. ! t" N/ \3 ~4 R
  128. (defmacro with-board (&rest body)
    4 Y# C: ^' n2 u& Z9 o; I1 e7 `# V
  129.   `(with-temp-buffer" \. T- D& e3 F+ z3 X+ z. k. j
  130.      (unwind-protect4 p# W+ ^7 M5 m- N# Y4 ^
  131.          (progn
    , Q- k& U4 I$ Z
  132.            (if (session "board")$ R/ `( C) r# K% a& Q, J6 u! b
  133.                (insert (session "board")))0 p5 U% U% T, c3 @& T
  134.            ,@body)
    0 c+ d5 i. T1 L
  135.        (session "board" (buffer-string)))))0 J4 D- I/ m/ _& V6 ^5 J
  136. % d9 s" g0 x3 P+ V
  137. (defun board-init (size)5 E; k# `7 d. O/ P3 |7 W* C
  138.   (session "size" size)
    . w3 Y4 L# }' d+ N! n' {
  139.   (session "step" 0)
    . P  S% }* V. O
  140.   (erase-buffer)  Z" K" I7 Y) D  V- R7 c6 j. M
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    2 `  H4 l  u) g  U2 f) e- L
  142.   (dotimes (row size)
    & }+ a% Y3 p5 N) @7 o
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    * Z# T" Y+ G* \8 w
  144. * a" g3 H' l, Y2 w3 u  L4 c
  145. (defun board-contains-p (y x)
    5 y" g0 N. p! b6 J& r
  146.   (let ((size (session "size"))); \" l, q+ c! R
  147.     (and (<= 1 y) (<= y size), O' V! @# c( V& x
  148.          (<= 1 x) (<= x size))))6 Z1 _+ s! C: X# a$ F

  149. ' }. Q$ w6 {' x2 S2 d
  150. (defun board-toggle (y x)
    * n6 P  M- g  C5 [
  151.   (when (board-contains-p y x)! i% n- G$ e6 n! O) B  v* |
  152.     (goto-line (1+ y))
    ! M+ {4 z; Z3 ]( b0 R# P
  153.     (beginning-of-line)& B; L8 {2 w5 G
  154.     (forward-char x)' G$ g& s+ Y) {. u3 e( A1 X
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    9 u8 }# \1 G1 u1 x, \5 s
  156.                 *wechat-5x5-black-chess*
    7 R$ k5 q* `* J3 S
  157.               *wechat-5x5-white-chess*))$ f6 h) I6 E/ Y$ k) k% M' E
  158.     (delete-char 1)))
    % A1 N( I% j/ b+ g/ e
  159. % Z! P; G! }5 G- Y
  160. (defun board-put (y x)
    ; T  _/ j  o* Z6 e) u9 {3 l
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    . E# j- M$ n6 m$ `0 r+ ]9 @
  162.     (board-toggle (+ y (first dir))7 v$ a  v  I: }) S4 y7 P
  163.                   (+ x (second dir)))))- F, f" r4 O, N  N; h8 u6 a7 O2 }* y& P- `' n

  164. / l0 z- }0 N2 z3 O$ Q  {* n9 h
  165. (defun game-over-p ()% O* z2 Q4 n& M
  166.   (beginning-of-buffer)
    ; H# u  P* W' F$ ?2 X: L( _
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))- Y) E! u1 [7 a2 e9 C- }! s* e0 d
  168. 2 l# s# E8 B& H0 R0 M0 o# l
  169. (defun board-show ()
    6 O( z0 j5 J* }- V
  170.   (with-board
    " \9 e3 J# u/ \+ a: z% s
  171.    (concat (buffer-string)
    0 a! a( R* G, D+ v
  172.            (if (game-over-p)7 v6 l- r! G  m' m
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    4 O) a5 a3 X1 G* z5 u
  174.             (format "第%d步" (1+ (session "step")))))))' t4 ?( c, c8 Y; t7 c
  175. 7 s& C. X7 D0 D* |6 l9 }
  176. (defun board-position-parse (cmd)
    & ?& U/ l3 ?% G7 u9 m2 E
  177.   (if (= (length cmd) 2)
    ' c0 @/ V3 I$ r' e
  178.       (list (string-to-int (substring cmd 0 1))1 E. m! F- U, U& |
  179.             (string-to-int (substring cmd 1 2)))
    ) s! r) S4 N% @1 \6 I
  180.     '(0 0)))
    5 e  F/ R7 o; K/ [3 f

  181. 5 m) \6 j- a% s* ~
  182. ;;; 游戏房间
    & |1 M$ F/ M9 Y6 h" _3 U7 m7 G
  183. (defun game-room-init (cmd)$ v% b# \8 Y' X/ i3 i  q: w
  184.   (let* ((middle (string-to-int cmd))
    8 C0 l, _" V7 e7 ]- M. M# w& R( _6 M
  185.          (size (1- (* 2 middle))))( y, Q) `' F7 Q( a$ U
  186.     (with-board$ L& W* a5 T- q! t: W/ r
  187.      (board-init size)
    ) o$ z4 g( W  Y; G" o
  188.      (board-put middle middle)))7 E  i" l0 l  E9 C
  189.   'game-room)# N+ C0 y" n$ q2 w

  190. # Q. b- `% D! q* K
  191. (def-room game-room4 V/ k- m, U  a1 R8 |
  192.   #'board-show% ?2 |! Q3 i# p4 W+ ?& }
  193.   (t (lambda (cmd)
    / Y6 ]0 D- Z2 _' k- Z4 P5 D
  194.          (with-board
    ( K$ `* \2 F3 K- f, y
  195.           (if (game-over-p)
    $ s" w' Z& }5 b/ C4 |7 V, @
  196.               'living-room
    9 m- Y! e) E$ j
  197.             (destructuring-bind (y x) (board-position-parse cmd)5 F/ s: \, z( @
  198.               (when (board-contains-p y x)& @: h, R* e# ]( X* e* `% L; q
  199.                 (board-toggle y x)7 J& B0 f  O# q; Q3 k) `
  200.                 (session "step" (1+ (session "step"))))* l' P( P$ m; N  C& _$ m
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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