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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。! T5 a7 l. K: A: j" ]4 ]! r
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. ) p$ m- O1 ?- f9 `2 g& 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;">;; 定义新的游戏地图
      @/ O7 ^1 }; K- A+ o+ ?; e# S" p
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL7 n0 m* J+ o( b! y/ K6 {
  4.   'tutorial-room-0)                     ; 默认的入口5 t6 f# T. p/ P; v, t) g
  5. , l* Z) [' J- `( l  n
  6. ;;; 游戏大厅
    , |* t" y1 Y3 e- Y& i2 k
  7. (def-room living-room
    4 k( i1 C. e1 m) T5 n% Y7 w  ?
  8.   ;; 进入该房间后的提示语
    ' k( h# _- Y, L4 Q5 v$ D
  9.   "1. 教程
    3 {/ i; G2 \- @- t9 N
  10. 2. 入门(3x3). E* C5 C( L" f7 c. k/ b2 L/ D" F
  11. 3. 初级(5x5). H+ W9 u6 i$ g6 P! V5 Y7 M/ q) T
  12. 4. 中级(7x7)0 H9 y# ~+ ?9 J1 M
  13. 5. 高级(9x9)5 h" w' q' I: H2 z
  14. 0. 关于作者
      D6 V9 n: c- j
  15. 请选择1-5开始新游戏:"; B" A8 b+ F* I% S* ]
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    ) k% ~3 d% k! _7 c6 _- x
  17.   ("1" tutorial-room-0)
    8 ^: ?5 L$ p" A# U" X4 R
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配' Y" l+ y- f/ M: w0 W8 s2 p
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    # @4 Q' W/ }: G1 ]' F  U
  20.   (t living-room))                      ; 如果条件为t,为永真
    ' J$ V2 ^6 S/ O8 @+ B
  21. " x; Q+ u  Q5 i7 L; s1 b
  22. ;;; 作者信息
      j4 e# i/ m0 R3 g, Q
  23. (def-room about-room
    3 ~9 Z) |0 ~" p" C
  24.   "作者:redraiment, j3 f' R+ M1 M8 L
  25. 微博:http://weibo.com/redraiment) {; G& q; h& h
  26. 有任何建议,欢迎在微博或微信上联系redraiment。: ?3 H% W& V' y2 Y5 v7 T/ `
  27. 请输入任意数字返回游戏大厅。"
    $ D5 g. a! l5 w" Q2 l
  28.   (t living-room))0 S; m+ ?8 z* Z. A

  29. 6 X7 r8 l( K: E4 n1 n" ]
  30. ;;; 教程! Y4 i# A$ t4 ]6 b0 w
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    + |7 g( f/ \' H' k  h$ @4 x9 ~
  32.   "The number of tutorial rooms")
    $ o! I9 u0 k* h6 P

  33. 5 S+ j1 k1 Q5 a& l0 C5 }" Q7 i
  34. ;;; 简化教程的定义
    ( t0 }% y8 J: G" B  x# x
  35. (defun string-last-line (content)
    8 z7 C( ~# B7 T) P3 d7 Q
  36.   "多行内容组成的字符串中的最后一行"2 M* b: Q9 y! |3 }
  37.   (with-temp-buffer
    # g9 j3 u' s% m( U: V" v8 [
  38.     (insert content)
      x5 u$ Q, [% }4 @  |4 G
  39.     (buffer-substring (line-beginning-position)2 |2 _) {) {8 L
  40.                       (point-max))))
    % D" x' v: [9 [

  41. 1 S* O/ |8 L7 R
  42. (defun def-tutorial-room (prompt)  q; w/ `4 J7 a9 E# [
  43.   "根据提示语自动生成教程房间。9 F. }! n1 F% t, M& h  x' {( O
  44. ( N, U; e  Z! ]' J
  45. 1. 提取最后一行作为问题;
    $ @1 g2 N$ M, U' r
  46. 2. 分析问题,获取期望用户输入的内容;! U1 @( N' y  @! c9 \" x* ]
  47. 3. 定义教程房间和重复提问房间。"
    " k$ `$ E2 U: E% {
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))* k; a2 i1 E6 u2 e
  49.          (repeat-room (concat room-name "-repeat"))  m1 {( d) t  O# O" h! J( C3 r6 F1 Q
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    $ e# X  B( {* |- U( D+ U1 a: k
  51.          (question (string-last-line prompt))0 e" w: n: `& B4 i6 B" q
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)- p, f9 `5 W4 e  a4 [/ _" B6 z$ M
  53.                      (match-string 1 question)))
    6 J; E6 m2 {& {& Y& P' |% P
  54.          (doors (if except5 ?: r/ M1 I5 ?7 d8 T. d5 y* q
  55.                     `((,except ,(intern next-room))& j# D/ y7 v. z" B! g; |9 O) Y( U5 [
  56.                       ("q" living-room)/ @3 O' c9 Z8 O- N* j
  57.                       ("Q" living-room)
    # `" p/ Q* `4 f  S4 d& [
  58.                       (t ,(intern repeat-room)))0 I1 k! |- ?) q3 {- f: z
  59.                   '((t living-room)))))( A& f, b4 e6 l0 _- @9 Q. I
  60.     (def-room-raw (intern room-name) prompt doors)
    - |& v! m8 k: H9 T! d2 `0 k  f
  61.     (def-room-raw (intern repeat-room) question doors)))+ X' M9 l6 i# Z) f4 Z  L! U

  62. 9 Q  C* R; B: ^' T  L3 G# l' j
  63. (defun def-tutorial (&rest prompts)+ E2 o( ~0 |# ~/ v* w
  64.   "批量生成教程房间。"
    : B- ~7 f8 m1 Q) B9 V: C
  65.   (dolist (prompt prompts)# _3 g/ i* A! l3 W+ p7 l4 q6 \
  66.     (def-tutorial-room prompt)))2 w5 `% `' N# Y/ j' T
  67. " ]- H7 C$ D3 b( P+ f; W0 `" }
  68. (def-tutorial" C- C; @7 ~; N2 N8 W
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    * {5 V1 z5 Y. B( I9 n2 N& S+ a
  70. 1. 教程3 k* h9 T) Z5 K" k8 T8 A
  71. 2. 入门(3x3)3 K/ R4 M+ f" @, W! }
  72. 3. 初级(5x5)
    3 F+ M; f  I1 K1 n# K& i  [
  73. 4. 中级(7x7)" B/ r4 j- u9 @6 e6 p0 C! l# F
  74. 5. 高级(9x9)$ y  i* l; @6 Y/ Z7 b* M
  75. 0. 关于作者7 x/ ~6 {: r3 P! A2 V  T
  76. 请选择1-5开始新游戏:8 E# Y6 J. z! m  P% x
  77. 您现在正在游戏大厅里。3 x0 [( J9 u4 u2 c0 N
  78. 请输入“2”进入入门级房间"8 b+ H+ l& z4 X0 D/ q$ T* [
  79.   "  ①②③
    7 d6 D. }0 |! G8 y8 ^+ Z6 a
  80. 1 ■ 9 H( D$ x  \9 ~. A; H4 c3 S) ^
  81. 2■■■' {  \6 Y% G/ J! W
  82. 3 ■ 
    4 z; `; P9 q7 [( ~
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    $ U3 f& Q3 C$ d" s
  84. 请输入“22”来关闭第2行第2列的窗户。"
    ! D! I/ U* I3 ^2 L
  85.   "  ①②③
    3 N' D) ?, a- _' l- ^6 B
  86. 1   
    " j. T# |5 _4 U9 s2 l
  87. 2   0 E' @" [3 Y8 D; P+ f9 b6 B
  88. 3   
    1 X* N& T  s4 B+ O; u
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。2 w  }! W8 b% }, I
  90. 请输入“11”,试着开启左上角的窗户。"
    7 r& W. X: H6 a/ ?: K, q
  91.   "  ①②③
    + i+ C. t5 O4 [* \
  92. 1■■ 
    8 W* g) }4 W& Y
  93. 2■  
    1 C  H* r* ^. }  o+ A3 a1 r
  94. 3   
    # @/ \/ Q8 Z. ?8 m) _9 z
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。- E$ X' Y1 c4 N7 I# c
  96. 请输入“13”开启右上角的窗户。"7 b* N2 u: I# x8 j1 @, k2 O1 c# ~
  97.   "  ①②③9 G7 M+ i' s. b; s- I
  98. 1■ ■
    7 ?5 m8 O7 u3 ^: {+ \  x
  99. 2■ ■
    2 o6 A/ x, y* U* }
  100. 3   
    2 A0 h3 P+ d! f' F
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    ; ^# G, N" k: L5 Z$ R4 s5 I
  102. 请输入“31”开启左下角的窗户。"
    1 o$ E! b# V! O+ R# L' t
  103.   "  ①②③
    ' |7 s, O5 z' ^% d) W1 C
  104. 1■ ■- R5 [/ i* q9 I- M$ }( v" j
  105. 2  ■" F- R  r6 k" {/ z4 o. n' B
  106. 3■■ # |3 s$ D* Z3 \( e( N
  107. 此时,总共有5扇窗户被开启了。
    6 H+ O: ]' J( u! V" D
  108. 请输入“33”开启右下角的窗户。"
    " Q$ x, d% D: m9 b# G
  109.   "  ①②③
    2 D* b) A; V- C5 L# l
  110. 1■ ■
    3 q  D9 D* r' s- N
  111. 2   & k0 _2 a3 d# Z2 L# p  d4 ^$ f
  112. 3■ ■
    . V0 p5 k$ J  U, d7 B/ F2 n" c
  113. 现在,只有四个角落的窗户被打开。/ e  f  \& p) t* M) k6 G& d: C
  114. 请输入“22”完成最后一击!"
    / ^: _5 C( }" U/ [# W
  115.   "  ①②③) N5 c7 f5 K5 S4 G6 C  H
  116. 1■■■0 q, k- h: q0 u- \  ]9 [) W
  117. 2■■■5 q- Y+ U# D8 I
  118. 3■■■
    ! R7 m: E% }) M1 s% k0 g' S
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    8 G: \: C6 Y+ E$ i5 d
  120. 3 _! h1 q4 E8 o  D) W/ H9 e
  121. ;;; 棋盘
    & {/ h" m7 t% s- e
  122. (defconst *wechat-5x5-white-chess* 12288
    ! [7 {1 p& h; u. E) s. R9 a
  123.   " ")+ J7 o6 ^1 X5 L& Z

  124. 8 t2 {( j' H4 f. i! F
  125. (defconst *wechat-5x5-black-chess* 9632  O0 [% E5 v$ z
  126.   "■")
    8 K' P! P% z4 C0 E3 \. _& }6 ~. ~
  127. $ }  |4 B( g; G; O% Y& p
  128. (defmacro with-board (&rest body)3 {' q, Z: z5 |8 V. o9 h& L8 g3 C/ j
  129.   `(with-temp-buffer
    2 s5 Q, W3 V% x7 h+ C' s9 ]. n
  130.      (unwind-protect% {, A/ {5 c/ B, O& ?7 N8 v* r8 |
  131.          (progn
    2 H/ u) |4 ~4 u
  132.            (if (session "board")& O9 z' e' A( ~
  133.                (insert (session "board")))' t- g5 n5 m! W4 z
  134.            ,@body)  ~# v8 n8 x4 ^! g7 Z+ W
  135.        (session "board" (buffer-string)))))
    * N% F  D# `8 U4 E/ b

  136. $ `3 G6 x9 \) @0 o
  137. (defun board-init (size)
    9 E) o4 c: {7 w  d: }7 U
  138.   (session "size" size)
    5 O% E2 \7 T4 P: X
  139.   (session "step" 0)9 |1 u0 {4 ]7 ]6 _. R
  140.   (erase-buffer); D, i( S! t7 v9 n; |1 Q
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    ; X/ ~) ]( D/ r$ i
  142.   (dotimes (row size)" I) c, E  \7 I  F9 @( c
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))/ u& O0 ~/ X8 U5 w" {% c

  144.   o- b: z- }- S0 p5 o. C- t% |
  145. (defun board-contains-p (y x)2 |7 a( ^: C' @, K: @9 s( \% F% B
  146.   (let ((size (session "size")))
    # `/ e* o8 w6 c& T0 l5 N6 L
  147.     (and (<= 1 y) (<= y size)8 Y1 B* `0 v% E: s
  148.          (<= 1 x) (<= x size))))
    . U  l1 M, i& W- t
  149. ' J# n! P: q! N3 l
  150. (defun board-toggle (y x), B1 w( T0 T0 b: A
  151.   (when (board-contains-p y x)  Q9 k2 e( f" k* m. |6 a8 Z
  152.     (goto-line (1+ y)). C* v% p- ^8 R4 n  N
  153.     (beginning-of-line)" p! [/ ?2 x- i  b8 Z9 h
  154.     (forward-char x)
    ' b; C) ^* v6 I. Y: {
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))" `+ M. q% J) [" X* B
  156.                 *wechat-5x5-black-chess*& ^  R! J' m4 o' w, y
  157.               *wechat-5x5-white-chess*))
    , |( P1 J9 ]+ k! {3 ~7 D/ {
  158.     (delete-char 1)))( S5 Q( f( `! W9 V$ {/ K% V/ P2 Q
  159. , j8 X7 S' ?! k
  160. (defun board-put (y x)
    1 j  c8 ?* H1 Z2 B: T! G$ x
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    8 P9 k. U1 X! R- g
  162.     (board-toggle (+ y (first dir))0 z* ]$ a  H- o* H/ g
  163.                   (+ x (second dir)))))
    " }* H" [! `! H0 _$ k0 B

  164. : q9 B' t" n, c9 H6 m2 k' C$ j3 |) s! E, }
  165. (defun game-over-p ()7 ]( U5 N/ z. U. y& o; E
  166.   (beginning-of-buffer)0 a9 O9 T2 T% y0 z, z
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))" ^* \8 o8 C1 R

  168. # A( ^+ g7 p* {
  169. (defun board-show (); [3 _4 r" P" _8 R
  170.   (with-board# B( i0 q# J% g& W, B; u* v
  171.    (concat (buffer-string)
    * I7 l, b% a8 O. w8 ?
  172.            (if (game-over-p): A" A& E8 B/ H, w) f0 K
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))+ P* B; k) g7 {- H9 m
  174.             (format "第%d步" (1+ (session "step"))))))), c3 v6 R5 x; [5 D7 j1 X, ?# k4 V

  175. : Q0 T% N5 B' E6 x! B
  176. (defun board-position-parse (cmd)
    4 b" i, o' G3 x) d- H$ \! u! k& i
  177.   (if (= (length cmd) 2)  q# e5 N9 a4 d; l  Y
  178.       (list (string-to-int (substring cmd 0 1))
    . [1 H4 a- C. K: y0 a+ W
  179.             (string-to-int (substring cmd 1 2)))
    $ W8 W$ W% E. k; n" i  r8 I
  180.     '(0 0)))( Y/ u) E: c& V

  181. & l4 e3 d8 g, N1 t0 F! R: D/ J
  182. ;;; 游戏房间
    ) w1 B) b( W2 d) T) x  q
  183. (defun game-room-init (cmd)
    3 j3 t! i! Y3 x0 L& G/ R
  184.   (let* ((middle (string-to-int cmd))
    1 C3 R+ Z) q) f  r) P* v) E% Q
  185.          (size (1- (* 2 middle))))0 [, ~5 [5 t0 S/ \1 k+ K" r
  186.     (with-board7 K+ i6 `/ ]/ @3 @9 T0 Z% A" B
  187.      (board-init size)( a6 @( T* s+ q
  188.      (board-put middle middle)))
    / `8 O8 \; q% R; n+ ~
  189.   'game-room)3 T/ C1 m( _/ i- n- Y# a

  190. ' a  Y0 K$ H/ L& c. r& F3 w" t
  191. (def-room game-room8 E% A/ R. k  K
  192.   #'board-show
    4 m3 v" a: U& |
  193.   (t (lambda (cmd)+ @, j& W  l, t6 f/ U% p. U1 v
  194.          (with-board4 y- U( Y( I9 {, H& Y! T) X
  195.           (if (game-over-p)
    # G- {# \- F" U
  196.               'living-room; j: I- t, {5 H! i5 O
  197.             (destructuring-bind (y x) (board-position-parse cmd)' K; _$ Q7 @& }  R6 @4 j. u0 h
  198.               (when (board-contains-p y x)
    7 T% L8 M$ F2 Y! y0 S  g
  199.                 (board-toggle y x): @* r9 k7 p! s, U3 C2 M
  200.                 (session "step" (1+ (session "step"))))1 {& J2 W/ N, i2 T9 u
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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