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

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

  1. , X  t2 ?7 ], U* L& B
  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;">;; 定义新的游戏地图) w. \. F- F7 n0 a
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL8 p- I; q! q4 i# ~7 t' y  Z
  4.   'tutorial-room-0)                     ; 默认的入口
    - b. m$ \4 s3 W4 G4 {+ z
  5. : T; m0 N  R5 U* S! u: W) x+ E
  6. ;;; 游戏大厅
    8 y  M" S6 O9 ]
  7. (def-room living-room
    # H/ |: X; x& A9 c; E/ s, D
  8.   ;; 进入该房间后的提示语
    : k5 d. S( D- V. u0 @/ l& F
  9.   "1. 教程: V$ B5 @8 _$ O( w
  10. 2. 入门(3x3)9 E$ M& I5 s* c) h
  11. 3. 初级(5x5)
    - t9 ?0 Y& J1 G: v! V  \  x( i
  12. 4. 中级(7x7)
    / V9 {. z1 I' U4 B' \  K* m7 U! Z
  13. 5. 高级(9x9)
    6 C$ f5 H% a2 E+ L! |% D8 d
  14. 0. 关于作者
    + O# E& E* p" `9 c  P4 B8 [& E
  15. 请选择1-5开始新游戏:"" F! C8 x* L& y/ u7 t
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名7 P2 l. b% l9 h7 y) _0 Y* l. q. p. R
  17.   ("1" tutorial-room-0)
    . X1 W6 s4 `+ w9 B9 J; B2 F& b
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配0 C5 w  v+ K+ X( I9 m. {+ _. c
  19.                                         ; 相应的返回也可以为函数,动态返回房间名: G, X" r" w2 k# P* o/ u/ S
  20.   (t living-room))                      ; 如果条件为t,为永真# K. R  A3 n& U% p% Y5 y- A
  21. + n; @# g% _( P% ~1 l' o4 W6 Y' `
  22. ;;; 作者信息8 k* M! o- e) k" I1 X! O
  23. (def-room about-room
    6 K* |  {' J% e7 R" u5 q( s
  24.   "作者:redraiment% C% H* `, N# L$ P  H
  25. 微博:http://weibo.com/redraiment
    3 |) _" [& q6 ^$ P7 i
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    ' D$ [: O9 M% X* t  f
  27. 请输入任意数字返回游戏大厅。"
    , G; G6 n! Q* d) h  Z
  28.   (t living-room))& M: X! \3 \, ^2 }
  29. ( p/ j" C) P+ t  h, g8 Z4 K& Y
  30. ;;; 教程
    * E2 D. d0 J& d' }  d9 o
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    6 |+ ]. }) ?: ]+ I6 N
  32.   "The number of tutorial rooms")
    ' L" _( s' I% N* ~- y
  33. 1 j* K; j- e5 v" C0 _. B
  34. ;;; 简化教程的定义% o( T- A; M! d  U! u
  35. (defun string-last-line (content)1 L3 [6 G) }/ ]/ ^9 ^
  36.   "多行内容组成的字符串中的最后一行"
    % {8 ^- k3 Y/ O2 g; d
  37.   (with-temp-buffer& Y4 i5 }' p( Q+ z) E/ B! @3 _
  38.     (insert content)% R  u3 t3 u* Q2 Z* }: }- y
  39.     (buffer-substring (line-beginning-position)( s- C/ m' A- R. Y
  40.                       (point-max))))# b* [" T: d( b. q. b1 T
  41. ) j- H2 N/ h$ b0 K
  42. (defun def-tutorial-room (prompt)( q) k' Z# P  F* N
  43.   "根据提示语自动生成教程房间。1 d* u% G) g4 p3 X$ _3 f
  44. 4 p9 Y7 _, |/ @, B2 P+ r
  45. 1. 提取最后一行作为问题;6 S+ }( I  e; p+ k5 p
  46. 2. 分析问题,获取期望用户输入的内容;
    # y; v1 C; w/ E
  47. 3. 定义教程房间和重复提问房间。"
    / ^& l  P" X9 I6 ?+ o6 t
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))8 d; Y+ J6 J2 l$ q) M9 v
  49.          (repeat-room (concat room-name "-repeat"))& ~% R/ Y& l) A  \  C7 `2 F
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))) X5 Y4 C- s6 ~3 Q4 ^; t
  51.          (question (string-last-line prompt))" `# j, ]6 D" y& g! P6 h& i
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    5 u$ V$ N; ?1 h0 K8 Q9 J0 z3 ?
  53.                      (match-string 1 question)))
    $ h; O' j3 D' K' J9 h. l
  54.          (doors (if except, z: X  g( B9 ^1 j
  55.                     `((,except ,(intern next-room))) ~' \3 u1 a9 W$ v1 W
  56.                       ("q" living-room)
    8 R* Y- e$ |2 p9 r1 J
  57.                       ("Q" living-room)
    7 a: F' r0 r1 w& ^
  58.                       (t ,(intern repeat-room)))
    * x; b6 g# V4 i1 f9 U' ]/ V
  59.                   '((t living-room)))))
    0 k7 ?: @* _/ ]- d/ r3 t: {
  60.     (def-room-raw (intern room-name) prompt doors)9 o) l9 {/ s, U0 Z- S/ f8 b9 m
  61.     (def-room-raw (intern repeat-room) question doors)))  k, `' U! v, m( h& Y9 v! p
  62. ! H9 Z$ R7 t! o8 P
  63. (defun def-tutorial (&rest prompts)6 T8 x5 H2 N1 w1 ?# }" {: \
  64.   "批量生成教程房间。". F6 Y0 P/ ]4 l
  65.   (dolist (prompt prompts)& y5 J. a8 V6 ?( A
  66.     (def-tutorial-room prompt)))
    - X1 Q- X7 u# l% q' {6 s

  67. ! ?0 O& W% F" u0 L2 S* K) l
  68. (def-tutorial( i: O  O" P7 |1 Y
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。& O# O( `' ?4 m& a0 k
  70. 1. 教程$ i0 ]: v8 ?! x7 y# G
  71. 2. 入门(3x3)
    , ~4 m" |5 g* d( q
  72. 3. 初级(5x5)0 c/ ?" ]; S9 C9 O) j
  73. 4. 中级(7x7)
    5 B$ v7 ~% K9 P9 s, e& ?; v
  74. 5. 高级(9x9). Y! f+ l7 i6 v& E$ Q+ I
  75. 0. 关于作者
    * a% W4 S6 ]  a% g  \
  76. 请选择1-5开始新游戏:
    4 i+ g- I) g" P' H
  77. 您现在正在游戏大厅里。; j1 s3 z0 W, o; T+ g/ j; G! h5 s
  78. 请输入“2”进入入门级房间"
      j5 }' B2 N7 a/ A+ E. @1 M, [
  79.   "  ①②③. _9 S4 d& p, S( w% }9 Z# G7 \
  80. 1 ■ 
    5 d9 Z" E6 W( A' [% r2 \
  81. 2■■■( O' n) ~: x9 s% V1 ?) v2 Z
  82. 3 ■ 7 ~, u' L) K& F3 t' Z* G# d
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    ( C  I) W# r8 v9 ^
  84. 请输入“22”来关闭第2行第2列的窗户。", {/ L0 ?* v* y& \* H; y# E
  85.   "  ①②③
    ' t6 P) ?2 _# T3 O+ Q7 B) O1 r
  86. 1   
    % y* |0 t  |. ~, _0 i/ g6 ]( d
  87. 2   
    - s5 A0 }- f: p$ l
  88. 3   
    : |* n7 m& I7 E! X
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。! L1 u& N  J3 r: |& F( G' E6 ~+ p
  90. 请输入“11”,试着开启左上角的窗户。"
    + S6 K- h; X- W# \% h. t9 u
  91.   "  ①②③' J. I4 T3 F+ \; }+ j/ `. v
  92. 1■■ 
    $ U- ?2 p! e3 l9 p: k, Q
  93. 2■  & l2 O. k- b3 |( X+ b: x9 Y
  94. 3   8 ?' E3 L8 z# w7 Y- \1 _- }+ e
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    1 [3 g+ E4 V7 J; t  c4 O
  96. 请输入“13”开启右上角的窗户。"& H; D$ B* L2 ?, M- K
  97.   "  ①②③
      x+ W  B$ M( u0 N$ h3 A
  98. 1■ ■. V) r* G* G: V% [& I
  99. 2■ ■& e" Y1 D# z. L4 @" G
  100. 3   
    7 h4 S# [/ {2 O3 ?' A' z
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    : K& u; Q. }9 f. i3 Z+ S3 i7 d9 t- l0 ?
  102. 请输入“31”开启左下角的窗户。"
    ! @* r& v4 }) ]+ u
  103.   "  ①②③6 z) U2 W8 w% i/ P  s
  104. 1■ ■+ f7 V; T1 B& ^: I6 r1 b
  105. 2  ■: e. w/ r# M! t3 i( w6 U
  106. 3■■   J( j) l& M2 {3 q
  107. 此时,总共有5扇窗户被开启了。6 _( w+ d$ }( t3 F' @2 o
  108. 请输入“33”开启右下角的窗户。"
    0 H3 ]6 R& V" s& O
  109.   "  ①②③" `% q" S, N# I& U: _' t4 S2 w( ]6 ?
  110. 1■ ■
    % s0 S) E# X! @- M2 o
  111. 2   , B& L' t- O) ?+ m$ e& S7 k: q
  112. 3■ ■& _" N- \$ n* D' K) Q
  113. 现在,只有四个角落的窗户被打开。! J( u% s. y; W$ a6 l: o' D- z
  114. 请输入“22”完成最后一击!"
    ) h/ v& {& y: A( ]4 t
  115.   "  ①②③
    " U  f1 _& d2 |& Q3 [$ l
  116. 1■■■
    ; t) B% C* t( ]
  117. 2■■■
    9 n& g  x! `: ^3 b) ^* a
  118. 3■■■5 \' Q4 e! y4 N+ {( G$ l1 F" @
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!"); n6 L2 X+ q5 ^+ `$ l4 F
  120.   \3 `4 b; l+ U" Y
  121. ;;; 棋盘
    : y7 U9 E- e* `& q
  122. (defconst *wechat-5x5-white-chess* 12288
    1 S4 \' P; y6 S6 b7 d0 k0 g
  123.   " ")
    . D  r# _' S& T# m/ q
  124. : R; q; R) B1 ?  M. E% L7 r0 h: w$ |
  125. (defconst *wechat-5x5-black-chess* 9632' M8 \' ^  ~5 n9 [4 [  J& p
  126.   "■")
    ' a  U& F& c7 w

  127. 2 J1 @- [& Y: v4 W) r
  128. (defmacro with-board (&rest body)
    , h- A1 e' |# k6 y2 t
  129.   `(with-temp-buffer
    % j" Q& J+ p2 l- w& ?% c% s
  130.      (unwind-protect+ m6 y1 n+ y- }
  131.          (progn
    / f+ D4 }: m5 U5 ]7 h7 T4 F$ u
  132.            (if (session "board")/ D5 E3 S1 i; p0 z( ~+ L  \6 ]
  133.                (insert (session "board")))& [6 i% H; l1 A/ a. x  X
  134.            ,@body)$ T3 d; f3 X  e% z9 P
  135.        (session "board" (buffer-string))))), T, S3 _1 N0 E; S
  136. 4 n- o+ s+ B2 x- _0 I6 ]* _6 v
  137. (defun board-init (size)
    ) N3 c# [( ?! _" u( U: f
  138.   (session "size" size)
    : |- \' L5 P" z  P& C
  139.   (session "step" 0)* u) t, J" W- h, j* A  X5 }
  140.   (erase-buffer): B5 V% B' h3 i. n( `+ B$ f: D+ o* K3 a
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨")), @+ ?6 H/ R' L; T- Z' [% o$ K
  142.   (dotimes (row size)
    ) V( \2 {7 @( Y6 p6 J: d
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    9 @" V* A0 t% o6 F  S

  144. & `. U; f. R) T7 d% N; A! S$ t
  145. (defun board-contains-p (y x); L5 g" `- L4 b$ K+ T
  146.   (let ((size (session "size")))
    0 o+ B5 @) @+ [! T/ S
  147.     (and (<= 1 y) (<= y size)
    + A! r: n* f; z& U5 y) y
  148.          (<= 1 x) (<= x size))))
    3 a% l. \) I/ T
  149. , M6 s/ K( s/ ?" C
  150. (defun board-toggle (y x)6 T7 @! M  V4 q) @. g- s* t
  151.   (when (board-contains-p y x)
    & ]0 {7 c& i! |4 z1 q
  152.     (goto-line (1+ y))# b* y6 u4 j; z4 n- b. Q9 a
  153.     (beginning-of-line)& w" d  H+ s* A- K9 y7 N( [
  154.     (forward-char x)- ~- Y% y+ A9 ?
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    ) q- [( j0 @  S( F7 Y& _
  156.                 *wechat-5x5-black-chess*( P: f0 Q, _+ ?" }0 I/ {
  157.               *wechat-5x5-white-chess*))
    : F" P, ?0 w* j. A7 h1 _6 I* p
  158.     (delete-char 1)))
    + t: u7 r/ O! x) }( S( u5 B
  159. 0 G4 R3 `5 p! Q  Z" r
  160. (defun board-put (y x)
    6 E3 F& j* k8 I/ X7 w% H4 T; h
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    6 b! ]: l# A' y" s2 x9 n, E
  162.     (board-toggle (+ y (first dir))
    & y  @- K- J' t9 L8 X+ B
  163.                   (+ x (second dir)))))
    8 X3 g* A3 K7 i- T: c
  164. 1 c. p0 G" U$ w5 E* x4 p5 {, l
  165. (defun game-over-p ()) t  C1 x* K$ t5 m& O  x4 y+ V$ V
  166.   (beginning-of-buffer)
    8 Y. U. y, i6 g; V' e2 g
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    0 \* x" B! V6 d2 X9 k8 A
  168. . O. B, _; J5 z. _% E! `- t" w
  169. (defun board-show ()3 i% Z( H! A; F
  170.   (with-board
    2 K6 i) G; b# P2 o& l
  171.    (concat (buffer-string)
    6 ]9 [6 k" Z; q: a3 [
  172.            (if (game-over-p)9 f7 s) q& ^* t6 ~+ k4 `( Q& G
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    ) a) o9 e4 T5 a- p
  174.             (format "第%d步" (1+ (session "step")))))))# d; R9 O3 T1 o7 g: m2 ?

  175. 8 E8 w1 v- I0 c
  176. (defun board-position-parse (cmd)
    7 N: O3 e8 y! c- u
  177.   (if (= (length cmd) 2)
    ; S5 ~3 ?& ^& X+ S2 D3 Y
  178.       (list (string-to-int (substring cmd 0 1))3 g/ Z0 `, C/ u5 B, {6 T; L
  179.             (string-to-int (substring cmd 1 2))). E, Z" _( E6 Q0 w  @
  180.     '(0 0)))+ M" h+ W& P$ z1 X
  181. 2 P% A8 j' b# h
  182. ;;; 游戏房间; C0 z; m4 i- ]7 `0 P0 y) A) w
  183. (defun game-room-init (cmd)
    # a+ N/ U3 I  W8 o# e6 r1 \
  184.   (let* ((middle (string-to-int cmd))( x% e$ R# g5 M
  185.          (size (1- (* 2 middle))))
    ( I. h  Q0 g+ M  \0 P
  186.     (with-board+ K' y) }" V7 ^% U" k7 y0 T
  187.      (board-init size)
    9 }" X" Q1 }; h# {9 O. k- K0 Z9 I! `
  188.      (board-put middle middle)))
    + ^" m' M- |' ?9 q) N2 f
  189.   'game-room). B5 D4 Z$ I9 I+ l  q4 o
  190. ; v4 K9 Q7 K' G9 y4 M
  191. (def-room game-room7 S& n. M6 ]2 Z+ u$ T
  192.   #'board-show7 I+ G  Z: Y) W+ [" B( O: E: ]
  193.   (t (lambda (cmd)
      r3 p, n  E# R% E/ g
  194.          (with-board" r1 I& |- U5 {4 b8 `
  195.           (if (game-over-p)
    0 _- f# L; L, n# k; o% d( M3 M/ m* f
  196.               'living-room
    & c& C$ Q$ b1 L3 i" i
  197.             (destructuring-bind (y x) (board-position-parse cmd)
      G8 K7 t9 S% \5 X0 w
  198.               (when (board-contains-p y x)
    8 }# Q$ ?" t& B7 p  ]' p
  199.                 (board-toggle y x)
    4 p4 o( @& m* M7 G3 @
  200.                 (session "step" (1+ (session "step"))))
    # [  p5 n" q( p% t: I
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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