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

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

  1. - }# R4 ~; z/ B& ~! s. l
  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;">;; 定义新的游戏地图" V' t5 Y$ u2 R+ [" _
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    9 J  H+ O" S, `3 y/ ~: z
  4.   'tutorial-room-0)                     ; 默认的入口
    % L2 R5 O, O$ }' S

  5. $ a. M( ~' q5 g+ f3 ~" y
  6. ;;; 游戏大厅- V- Y) ?7 f7 d1 j
  7. (def-room living-room
    ( S8 f: l4 E9 v/ G4 I
  8.   ;; 进入该房间后的提示语
    5 \: u1 P( _' a& i( Z( K6 `- @
  9.   "1. 教程$ M9 D+ R0 c  C3 e  W3 v
  10. 2. 入门(3x3)
    $ |% J4 P1 A; X5 _7 d, X4 g
  11. 3. 初级(5x5)
    + s+ u5 L" s+ W2 ~0 S7 ~1 e
  12. 4. 中级(7x7)& J: X* \) ~3 X! T% H
  13. 5. 高级(9x9)% I4 O% ?# g; T% ?6 [! T
  14. 0. 关于作者
    % Y/ w% i" c& R* T% L( H% z$ @
  15. 请选择1-5开始新游戏:"0 h. |( _4 N) A
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名1 n, m  A% [. G7 g2 Y" d
  17.   ("1" tutorial-room-0)
      O0 Y$ K; g) ~
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配& w+ y) Q4 N: M5 K' b. `
  19.                                         ; 相应的返回也可以为函数,动态返回房间名2 P# g4 I1 a& y2 z" A
  20.   (t living-room))                      ; 如果条件为t,为永真
    8 Y7 E9 V. {: _2 i5 Z

  21. * @1 Y* M, \# t+ t7 \9 y  w6 K* B
  22. ;;; 作者信息
    + _/ V( y1 u; z6 G5 f
  23. (def-room about-room
    3 A' g4 e7 b4 W' a4 ~' x3 E  ~
  24.   "作者:redraiment: X8 e8 Q) Z" d7 g6 ?3 V
  25. 微博:http://weibo.com/redraiment
    * B9 T; ^; J* y$ L
  26. 有任何建议,欢迎在微博或微信上联系redraiment。) c1 {! {4 ~$ }# B; U1 _6 c2 F
  27. 请输入任意数字返回游戏大厅。") K  J7 u& B  W9 d7 q( N
  28.   (t living-room))
    2 l/ G, A' M2 t8 `
  29. 3 |; m% M' ^4 Y; W, J' u
  30. ;;; 教程% w9 H6 v7 ^  w! Y# m
  31. (defvar *wechat-5x5-tutorial-rooms* 03 K6 d9 S4 w0 ~5 U5 Q* G
  32.   "The number of tutorial rooms")
    + E7 p6 `- P% a% h
  33. ( W# H$ n8 }! p" E6 `' H' X% t9 o
  34. ;;; 简化教程的定义1 V4 Q: [( ^4 z% i$ {; @
  35. (defun string-last-line (content)& o; h) j% d* S4 d# S+ S8 T8 E( k
  36.   "多行内容组成的字符串中的最后一行"" N: j; h" Y  @
  37.   (with-temp-buffer# b+ ?! o- H: m( u9 p
  38.     (insert content)
    4 d( @- R- g" M8 ?" Y, D
  39.     (buffer-substring (line-beginning-position)
    ( q" {5 \. H4 z3 `+ F6 T
  40.                       (point-max))))
    % k. {2 z0 E$ H% r5 [

  41. # g% ^/ Q* ]7 L8 V1 T( u/ a
  42. (defun def-tutorial-room (prompt)1 D0 T+ P% `5 @% _0 A
  43.   "根据提示语自动生成教程房间。6 ^* i/ f9 K- }$ u

  44. 0 A; x+ s7 x2 A: l- q
  45. 1. 提取最后一行作为问题;
    8 g3 V4 u8 s/ L: `  o% k
  46. 2. 分析问题,获取期望用户输入的内容;3 W+ `  o6 F" o9 w( ]$ D6 w1 S
  47. 3. 定义教程房间和重复提问房间。"
    ; b; t+ V: e4 _1 s7 F9 p0 R, Y% U
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    # \+ [/ f5 ^# g/ @
  49.          (repeat-room (concat room-name "-repeat"))3 Y4 I; n/ n8 J, E  E
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))% W( w  M# l6 K0 `" n
  51.          (question (string-last-line prompt))
    / F+ [9 `* P! I7 T5 B" f0 q
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    : A" Z8 Y, h4 D1 }. y3 a
  53.                      (match-string 1 question)))1 o+ D0 k; k0 m4 D. h
  54.          (doors (if except
    # T6 }8 M+ ~3 t- W- [
  55.                     `((,except ,(intern next-room))6 m( C" ^) X' n5 m4 P
  56.                       ("q" living-room)) F! ~  ^" I# v  B" m9 n- j- s# ^) g
  57.                       ("Q" living-room)* v& u: F; p0 B
  58.                       (t ,(intern repeat-room)))
    0 e1 N2 f1 h" V4 C
  59.                   '((t living-room)))))7 Z' D- T4 i7 |( ~  F/ p% K
  60.     (def-room-raw (intern room-name) prompt doors)* K2 a" Z8 U" F
  61.     (def-room-raw (intern repeat-room) question doors)))
    2 F! s  `: ]' y9 `( O0 ]1 g
  62. 0 t5 q' r* K! u5 M8 @
  63. (defun def-tutorial (&rest prompts)0 T; Q7 R- a7 W
  64.   "批量生成教程房间。"2 @: j3 {4 l" R& m: V, K+ |. O
  65.   (dolist (prompt prompts)
    / M4 ?" j/ A5 P( i4 E9 h
  66.     (def-tutorial-room prompt)))
    2 d. O8 Z0 L1 J1 V2 H" Q1 C
  67. 3 W9 M0 |% Z/ B, a6 z
  68. (def-tutorial) y7 b9 m8 {  \% {5 Z/ ~
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    & Q0 W0 @, O. k. r9 Y9 S5 {/ o- O
  70. 1. 教程
      E5 T5 ^4 V2 Q& Y) B
  71. 2. 入门(3x3)' U4 B& M6 ?0 o: i
  72. 3. 初级(5x5)
    5 f- j& V5 T( k* J) D& r8 |4 L4 A
  73. 4. 中级(7x7)
    ) m+ f) t7 g+ J7 N
  74. 5. 高级(9x9)
    8 N5 z! }3 I; [, R
  75. 0. 关于作者
    8 s  K3 A' i3 f, v6 V% J
  76. 请选择1-5开始新游戏:
    7 b9 o/ C8 Y. ?0 G( T6 K
  77. 您现在正在游戏大厅里。
    ! v" @" q% I. a  Q9 Z9 q
  78. 请输入“2”进入入门级房间", {, \/ P7 F7 W. A! w
  79.   "  ①②③
    9 h) b6 c& a! V3 j$ O0 b
  80. 1 ■ " `3 U- y; H* a" `* `  I4 j* A) y
  81. 2■■■0 [7 }9 R* \9 S- T( h
  82. 3 ■ 
    + ~0 f" ~& X. k& `
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    8 i4 ~! F# r3 O9 b
  84. 请输入“22”来关闭第2行第2列的窗户。"+ M0 `8 Q" X0 s; d8 A4 {1 u
  85.   "  ①②③
    , M8 a7 w! s: u- Y
  86. 1   5 Y0 @' s# B' y* s5 `! j; {5 W4 i- }
  87. 2   
    0 s1 S. l0 P' j9 _  E3 r
  88. 3   
    + l7 u, N. H8 p2 i7 P" i3 P  f# z8 E1 [
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    , n$ ]' b0 F" ?
  90. 请输入“11”,试着开启左上角的窗户。"
    ' c7 z  N' g1 I" Y. E
  91.   "  ①②③+ [# G8 }* {8 f/ b. Z
  92. 1■■ ' j1 Q% E1 W  R- T: C% S
  93. 2■  
    2 U7 ~6 c; V3 p2 _+ o( B
  94. 3   
    # Q2 h8 ]* Y3 u. u: r
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。6 }( y+ T% t  e- \3 {8 A% B) n4 j" R
  96. 请输入“13”开启右上角的窗户。"
    " g4 q4 D0 [3 N9 l
  97.   "  ①②③
    6 d# w' u9 S4 T/ D  P
  98. 1■ ■
    3 p) q" z/ E! S9 y% E
  99. 2■ ■
    " m3 m. r* O  b& j7 w8 |& D
  100. 3   
    ; _& ^2 {8 n3 u! ?5 H
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    % ^: h# w& m1 S+ I2 S# e, `
  102. 请输入“31”开启左下角的窗户。"8 j6 r/ p% g2 c! B
  103.   "  ①②③3 W' `* J. v: [1 A" M
  104. 1■ ■
    & J  M' N5 d) t3 f
  105. 2  ■
    ( r! A6 |) e* {1 O
  106. 3■■ 
    8 v+ ?3 J5 ^4 Y. d4 n9 Y
  107. 此时,总共有5扇窗户被开启了。( ]1 }# ?2 b* w! L/ Y) F( ?+ j
  108. 请输入“33”开启右下角的窗户。") o" o+ ~$ Y6 A% p( O
  109.   "  ①②③
    4 T; u+ K( E8 f( |+ {  \) i& U: f
  110. 1■ ■% U; i# d! P9 N" E( v% L2 I% {
  111. 2   
    , t2 A0 Z- s: d! n4 W; Z, u& ?
  112. 3■ ■0 o0 }6 B; ^8 F+ |  k; V- s7 }
  113. 现在,只有四个角落的窗户被打开。
    + Y& @6 S7 l8 o1 ]. m2 i/ @8 W0 Z
  114. 请输入“22”完成最后一击!"
    8 C3 H; G% C% M5 {
  115.   "  ①②③2 V4 h9 S9 `5 I  Y  V
  116. 1■■■, F) m! W/ u( a' @9 D) B
  117. 2■■■% ^3 Z9 U+ _. U6 Y
  118. 3■■■: T8 g. V: ?4 W8 X$ A
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    6 _# @0 N& T0 a  K% c7 l

  120. ; O# o2 Y8 y# ~8 W: G2 O6 d, O: D; n
  121. ;;; 棋盘
      g* X2 Z% ^4 ?* l9 L$ m+ r
  122. (defconst *wechat-5x5-white-chess* 12288
    ! S3 I- T' w4 x: A5 h
  123.   " ")
    # I; j* r4 k: I" w- T5 B* t

  124. 4 t0 h8 U+ K# ^; @8 Y% y1 ^' O1 X
  125. (defconst *wechat-5x5-black-chess* 9632
    " Y7 a6 I4 ~3 O; `( P
  126.   "■")+ n5 `' d" {% Y  B3 `$ B- o
  127. + X" n+ X3 d3 E. N- N
  128. (defmacro with-board (&rest body)
    4 f) v1 s; E# i! B6 f# i, @
  129.   `(with-temp-buffer
    1 w! K8 |; }# Y* K% @
  130.      (unwind-protect
    1 ?( x# Y1 ]8 w- R
  131.          (progn
    4 M/ o% w+ ^" F! t
  132.            (if (session "board")
    6 e4 z/ S! O: v' y/ j$ [% k
  133.                (insert (session "board")))+ J4 o1 v5 Q" {! Y! ?5 O
  134.            ,@body)
    * @- Z  X& v3 f  J! c$ e
  135.        (session "board" (buffer-string)))))
    " m$ J3 ~8 I- z! \2 P5 [# `
  136. 4 G; G4 x" h6 p9 }+ m
  137. (defun board-init (size)& m, c" H0 w5 I6 @$ c0 c
  138.   (session "size" size)4 c2 |3 j* ?! K& v. }% L( Z
  139.   (session "step" 0)
    # O- i* T8 v7 [5 U2 f
  140.   (erase-buffer): M  |; D% h: B+ V# C/ k9 @
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))0 Q* P* U# U/ e* |! v
  142.   (dotimes (row size)
    / E6 S. _" ~7 o
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    % E, g4 i0 C2 w! v& ~" g

  144. ! W. k& t0 W( B" T4 N
  145. (defun board-contains-p (y x)  ]7 I$ z7 F; k! E, D
  146.   (let ((size (session "size")))4 ~4 P, d7 G- L! {% V1 K
  147.     (and (<= 1 y) (<= y size)/ _- i, o( |# Z, ~
  148.          (<= 1 x) (<= x size))))
    9 N8 `( _7 j, f2 S
  149. 4 {6 o6 s" ~( F) Y5 Q
  150. (defun board-toggle (y x)  s7 j" A) |. E, \
  151.   (when (board-contains-p y x)
    3 w0 s' |' f1 T4 M* }
  152.     (goto-line (1+ y))0 p6 q- E6 e  T
  153.     (beginning-of-line)
    0 f9 J, d8 E5 Q5 @, v. F
  154.     (forward-char x)
    & x& i6 i* o% i4 a& x6 J+ d
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))1 h6 \' a8 \1 h
  156.                 *wechat-5x5-black-chess*
    $ ]! I9 k. }  f7 g/ }$ Q* m, D- @5 F) h
  157.               *wechat-5x5-white-chess*))4 e! x1 _# T- D* M2 h5 g  K
  158.     (delete-char 1)))
    & p3 m0 T! ~# C: |
  159. 4 Z9 p5 i6 E# C8 |! u4 [9 Z
  160. (defun board-put (y x)
    ; J( b4 w% t, }" ^, S( ]
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))$ U* m) {0 G3 h
  162.     (board-toggle (+ y (first dir))4 n# e$ O' g* r& y- M$ Z2 W% L
  163.                   (+ x (second dir)))))
    ' @0 @5 Z) r: N/ t2 K4 t
  164. 9 j( a  {+ b& q: @! b- B& I
  165. (defun game-over-p ()
    ) O( V8 b' I. K1 D/ a; b% q
  166.   (beginning-of-buffer)9 Q% D: E1 c  Y- E. Z, A
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    / d* \8 Q, A0 @; k

  168. * n. @2 k' J: V; n* G  L  a
  169. (defun board-show (): G. m0 x5 V; P9 ?: p2 J( F
  170.   (with-board% A8 g7 _! a5 s& l$ }
  171.    (concat (buffer-string)) B. u* s! v& J: E( G
  172.            (if (game-over-p)
    # W7 V2 H. p1 g; m% W5 @' }  {+ ]
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))5 Q" Q" w- F; Y2 t/ v% o
  174.             (format "第%d步" (1+ (session "step")))))))
    6 s/ V3 |/ s) R: q3 g8 ]7 Z

  175. / N' W3 L) l+ {0 R. G/ \# z
  176. (defun board-position-parse (cmd)
    1 Z0 P$ J* ~! v1 o
  177.   (if (= (length cmd) 2)
    : [) {3 l- h/ c& A$ I# m# d
  178.       (list (string-to-int (substring cmd 0 1))
    . G5 r$ U$ E& G# U  w; T" C
  179.             (string-to-int (substring cmd 1 2)))
    " x. U* f) h  D! [, h
  180.     '(0 0)))8 D7 ]  ]- g3 g

  181. ; J% w' f- l" e) B2 [/ V# T$ }
  182. ;;; 游戏房间8 t$ E4 ^( g, \: B
  183. (defun game-room-init (cmd)* b) J7 s4 w- C% O9 [
  184.   (let* ((middle (string-to-int cmd))
    - y4 c3 x: ~+ W6 e3 }2 |
  185.          (size (1- (* 2 middle))))
    / B, m8 C3 @' x9 z5 F
  186.     (with-board
    - O; p2 C/ E$ L$ X) k  M: h
  187.      (board-init size)
    - M$ t" {' W3 _  y$ I, }7 {( o4 j
  188.      (board-put middle middle)))3 j4 o: ^$ E5 c2 `- O9 M, R1 l
  189.   'game-room)$ t' N  |9 A' s) x

  190. 5 V0 s& h7 ?0 A3 O- a) s
  191. (def-room game-room6 o, I' t+ \" E  U& {5 v1 E' `! w
  192.   #'board-show5 D6 w8 v0 O* {) }
  193.   (t (lambda (cmd)
    0 t0 C  A: W, e& c3 ^/ l( A7 r0 Z
  194.          (with-board
    7 s$ q" F/ X3 G( R1 G: Q' j- ]( n- t
  195.           (if (game-over-p)* u# ?. A. H+ n3 s. L( ]- d+ e
  196.               'living-room  V) T" E  J0 b3 D% [
  197.             (destructuring-bind (y x) (board-position-parse cmd)" `1 @' z/ \2 r. K; x3 P
  198.               (when (board-contains-p y x)
    % a  @# a8 p6 e' ]% i
  199.                 (board-toggle y x)
    $ f" X' o0 K: D/ N7 c) l' e
  200.                 (session "step" (1+ (session "step"))))9 [+ k  \  r: W
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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