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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
1 D; I6 s7 e/ d2 t借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. / D. Z! w( L2 Y
  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;">;; 定义新的游戏地图
    / `# J8 r7 P. r- E
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    7 q. Y8 F1 J/ Y, _! \& D
  4.   'tutorial-room-0)                     ; 默认的入口/ _( W4 l# C8 N) A3 s+ D2 l
  5. - \, c5 c8 z3 q( E0 }( i. x9 H
  6. ;;; 游戏大厅6 n9 I- t; p- c6 A( w1 Q9 \
  7. (def-room living-room- V; s  Y' q5 [4 J
  8.   ;; 进入该房间后的提示语2 A& F! t! P9 }  ]
  9.   "1. 教程
    ( |( K5 b" ^5 @: i9 O
  10. 2. 入门(3x3)/ i( w& {% h& h1 ?+ Z
  11. 3. 初级(5x5)* z; W! T$ o2 S0 z( a
  12. 4. 中级(7x7)
    - u$ N% F, q: r0 a, Q  P7 k
  13. 5. 高级(9x9)
    ( B' R7 Q$ [# F2 H9 F, X
  14. 0. 关于作者& T, X+ J" {# w4 K' y4 D$ G  g
  15. 请选择1-5开始新游戏:"
    * d- f" {1 K+ D' D5 r
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名& h$ l' d1 P/ o7 z$ f  ~  f. M
  17.   ("1" tutorial-room-0)
    ( W& A% r% m0 S+ w% M3 B$ j
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配0 c+ ^5 D  }. N
  19.                                         ; 相应的返回也可以为函数,动态返回房间名( g% N0 l9 C  L% M+ n, g
  20.   (t living-room))                      ; 如果条件为t,为永真
    + W& X3 d6 V1 i7 |6 Z8 [, e  e$ `5 e

  21. , t8 t/ h: T6 O7 p) B
  22. ;;; 作者信息! ^! z: |' ~/ D1 k4 C* }& |
  23. (def-room about-room" ~# C: f* K; B* S" g* M) ?; H& M
  24.   "作者:redraiment
    7 s1 `& Q8 M( X& x6 H) W& h1 t5 n! L. r
  25. 微博:http://weibo.com/redraiment
    ) _+ ^2 E. d  e4 n. A
  26. 有任何建议,欢迎在微博或微信上联系redraiment。- V8 v- T4 i; `! T* j# Z
  27. 请输入任意数字返回游戏大厅。"
    - I. k( B6 i" ~8 S
  28.   (t living-room))
    + ?; d" e0 p8 Y, o, g% h  q6 s% v
  29. ) j: q) @0 R, c# p% [; F
  30. ;;; 教程
    7 N( Z  [! l2 b
  31. (defvar *wechat-5x5-tutorial-rooms* 00 Q; W9 A% H" j- @" A
  32.   "The number of tutorial rooms")
    % W. x: D+ k3 G
  33.   O: j2 H0 x2 s4 @  d
  34. ;;; 简化教程的定义6 x* p9 ~- K' b" y  {( o& `
  35. (defun string-last-line (content)' t, J  x( y! P  q7 l$ ^$ q
  36.   "多行内容组成的字符串中的最后一行"
      V- b% L3 d: I) V0 |7 I1 m
  37.   (with-temp-buffer
    ( H2 Z6 z  z: C' {7 R! w+ E
  38.     (insert content)
    ; e+ x# `( M! H2 l7 X" e6 g
  39.     (buffer-substring (line-beginning-position)+ V4 S6 E8 Z7 e; O# f3 B5 v
  40.                       (point-max))))
    # ~" n% r! F+ Z

  41. " ]7 V, M6 j! l# A
  42. (defun def-tutorial-room (prompt)
    $ y7 @" _9 B- U) h8 a; A
  43.   "根据提示语自动生成教程房间。& |  F7 F3 c  Y. k5 s- U
  44. ' t3 U  O% h% _' l
  45. 1. 提取最后一行作为问题;
    : |4 |/ ^# f' s$ Z: Q: {" R" X
  46. 2. 分析问题,获取期望用户输入的内容;* R3 G0 ?% g4 y- ^$ z
  47. 3. 定义教程房间和重复提问房间。"
    0 @: S7 H* ?1 E
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))4 U% Q7 |3 ]# V7 k' M
  49.          (repeat-room (concat room-name "-repeat"))
    / @# U9 r. C# E+ f7 H0 _
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    / \/ J" \1 Y: F; m
  51.          (question (string-last-line prompt))
    5 p2 K1 S0 O# c8 \& R: w& E/ }
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)3 V: r9 d4 k$ J6 l4 _
  53.                      (match-string 1 question)))
    - X- ~0 c7 X- {  J' ]3 ^
  54.          (doors (if except
    , ~! q8 y' c% ]7 y. |8 p- c
  55.                     `((,except ,(intern next-room))
    ) U6 G  @# V) G
  56.                       ("q" living-room)5 P2 I  m2 q0 m! [/ {. `+ G
  57.                       ("Q" living-room)
    # }4 I, d) _3 ^
  58.                       (t ,(intern repeat-room)))
    2 z8 H# U, O( M
  59.                   '((t living-room)))))
    3 }% }4 O: J  E" Z9 ?- C- E7 ?
  60.     (def-room-raw (intern room-name) prompt doors)
    ( T( m- |6 e$ g
  61.     (def-room-raw (intern repeat-room) question doors)))( `4 Y5 r, W4 B4 T6 o1 [2 ?; r4 O# W2 q
  62. ' O! |. {& w+ S% o
  63. (defun def-tutorial (&rest prompts)
    . r. t  P$ Z5 i+ q
  64.   "批量生成教程房间。"
      ^. B: |+ W0 x9 L' V1 i7 p
  65.   (dolist (prompt prompts)
    ! z9 x" ^1 S' P* E
  66.     (def-tutorial-room prompt))); b' X9 @* e0 ~1 [
  67.   p+ g8 W+ C3 I! q8 ^  z; L
  68. (def-tutorial8 j) d: U/ Z5 v* d
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    " e7 N; B6 N( r7 {& d
  70. 1. 教程4 `$ K! O4 g5 o* @4 ]
  71. 2. 入门(3x3)! c- r( y  z  {0 j
  72. 3. 初级(5x5)
    8 n# s' |+ y3 \4 Q! B1 n
  73. 4. 中级(7x7): R$ |- S2 ^" Z4 \4 J- l' t$ l0 r/ E
  74. 5. 高级(9x9)
    6 ~! s) |. O) S7 u
  75. 0. 关于作者
    ) a  e; }+ W8 q: \2 _8 S
  76. 请选择1-5开始新游戏:1 X) y: Q4 Y0 D
  77. 您现在正在游戏大厅里。# I1 [) R" J) p; @1 ?
  78. 请输入“2”进入入门级房间"  |+ t- h8 T8 {( ]# t2 p9 m
  79.   "  ①②③
    1 J! I& V. T7 K0 m
  80. 1 ■ 
    7 [7 J* t: P  O- k6 ?# Z
  81. 2■■■7 o8 l' r& J# L3 r, a' |9 a3 L
  82. 3 ■ 
    0 ?" _% e: Z0 |! W
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!4 J! N$ l: K* H, V' }
  84. 请输入“22”来关闭第2行第2列的窗户。"$ Q/ R. w; Z3 L# t9 w- w) g) K
  85.   "  ①②③
    2 K& F2 q7 b! e/ r! O% C) y
  86. 1   
    3 o# H+ M, L+ p6 F. C
  87. 2   ( }, L5 x3 n0 a3 L
  88. 3     T, {" j2 ~( @: [: H* F# w. f' h; l
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。$ w% N, B  f% i- B0 B7 g# {
  90. 请输入“11”,试着开启左上角的窗户。"
    & E3 f9 }9 E" d4 z1 ~
  91.   "  ①②③
    " H. g) z6 ^1 K% X& B" B
  92. 1■■ 
    & ]* Y* ]) \( \8 e) B& c
  93. 2■  
    ' G" E- T# h. [6 i- n2 ?
  94. 3   ! i7 U9 m, e2 B, R' N
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    * F1 Q$ g. e" F8 ^# {) p' J
  96. 请输入“13”开启右上角的窗户。"
    + Q$ S& y/ x2 F  {$ [% V# @
  97.   "  ①②③
    7 _5 G  S0 t! z& R+ B
  98. 1■ ■
    & E* E* p% \! F* x4 L6 c
  99. 2■ ■$ l7 j2 l5 W! z1 n6 [
  100. 3   
    7 D+ h  P  J- Z% \2 p
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。+ ^  D' _( |! s8 H
  102. 请输入“31”开启左下角的窗户。"9 M+ k1 ?3 L+ a( l
  103.   "  ①②③  M6 g% k' |7 S3 K9 r- n
  104. 1■ ■
    ! x: n: `; C9 K. a3 @  V( Z
  105. 2  ■8 f3 ~7 A; I2 L& R% [7 n4 l
  106. 3■■ 
    * i% p( J0 ?; e# h% ?, \1 o) H
  107. 此时,总共有5扇窗户被开启了。9 T& n5 L+ Y' W5 F7 r0 S  L
  108. 请输入“33”开启右下角的窗户。"( [$ T! t  U% z/ H
  109.   "  ①②③8 Z- y: l+ s; v9 {6 B3 G
  110. 1■ ■1 K' b' B+ v" \/ X
  111. 2   ) Y5 k' O; s% X9 c+ h1 ?7 Q
  112. 3■ ■
    + N- N# m8 D9 K3 p! r; W5 W
  113. 现在,只有四个角落的窗户被打开。
    . k: B( y# f: I* y; w; Y* o% Z2 g5 D
  114. 请输入“22”完成最后一击!"
    ' K% t% n, g3 ~: G6 z! u% r
  115.   "  ①②③
    9 |1 l- |$ [, V+ i& c7 `; M5 \" ^+ o
  116. 1■■■* }" z9 j4 r0 t( U: @
  117. 2■■■
    ; {2 Y' u; a  K+ q7 q4 j
  118. 3■■■
    : [, _- F0 {% \& |
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")3 s# s" ]/ L% f7 C

  120. * F; o2 e$ i0 ^% @9 |6 `" g& \( N
  121. ;;; 棋盘0 m0 |. S0 v1 q# q1 O
  122. (defconst *wechat-5x5-white-chess* 12288& ?9 H2 ^1 e. L# |9 S. y
  123.   " ")  f/ G1 R" o9 B. e: o5 [* d

  124. % T# P6 ?  _0 {* W$ @" l
  125. (defconst *wechat-5x5-black-chess* 9632
    ) O" o- x3 R, x9 m
  126.   "■")
    4 `0 l7 f7 E; {; ^3 l2 a
  127. % X$ P0 @  n$ r2 w. E6 s
  128. (defmacro with-board (&rest body)
    * O# j  O( T! E9 v7 N6 o
  129.   `(with-temp-buffer+ y' [  J; {5 a( h2 w$ Y, b
  130.      (unwind-protect) g* {) s  y7 j/ Y  l# v( I3 I
  131.          (progn3 C8 L% D- U- Q# G( x0 Q; y
  132.            (if (session "board"); S' k. F+ B2 r" i, o3 g$ h
  133.                (insert (session "board"))), |  C; f" u4 A/ ?* t0 j
  134.            ,@body)
    . u( w# G# `- q9 h8 K- [! e9 T9 V
  135.        (session "board" (buffer-string)))))
    - o3 E& ~, H& p

  136. 9 W0 h  [" J& {! y
  137. (defun board-init (size)+ y/ C5 A1 |7 d5 q. B5 f! x1 N
  138.   (session "size" size)" D3 M; V  h+ }; g
  139.   (session "step" 0); K* B4 }: e' E$ K& h+ J" I- ~9 f  \
  140.   (erase-buffer)/ t0 I( k: W: i7 G
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))* w) m' o& ^/ r# ^
  142.   (dotimes (row size)  x- y& T# k$ x0 G% k0 p) ^1 m+ P
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    $ h! }1 z6 b4 O: k$ w; {& p

  144. + S% a, Q8 R+ W' z3 r+ {
  145. (defun board-contains-p (y x)" }+ ^' A; d" k/ ?9 S
  146.   (let ((size (session "size")))
    6 L; ^$ q. w: V, d- G1 I
  147.     (and (<= 1 y) (<= y size)' d4 f' L; Y, v3 O/ G. a7 t( g
  148.          (<= 1 x) (<= x size))))9 m& |# O: W: K* k) }% R+ ]% l' b

  149. 4 I, t5 ^, D& p$ }. b2 M& {
  150. (defun board-toggle (y x)
    . x& n4 e* O: s; o( o
  151.   (when (board-contains-p y x)
    1 l' ~5 k3 L' `) S; S! o# K
  152.     (goto-line (1+ y))
    6 R3 k# b$ v1 O1 I: c# u5 v
  153.     (beginning-of-line)
    - L$ v- H% z, K
  154.     (forward-char x)
    7 a) X. c+ M5 v' T. P. ^2 v
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    2 K9 J+ \* k- ]
  156.                 *wechat-5x5-black-chess*$ Q8 f$ W" n6 r
  157.               *wechat-5x5-white-chess*))) X. p  @& v4 A4 a
  158.     (delete-char 1)))
    ) `8 D2 [) w+ t& `1 `6 {
  159. & o9 v; Q' u: z0 K
  160. (defun board-put (y x)5 q, }; q* y$ ^1 g6 G
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0))). G- \8 s+ w) V3 }: n& z" ^0 _
  162.     (board-toggle (+ y (first dir))4 c/ z& E& T+ r
  163.                   (+ x (second dir)))))
    4 N% l# w# ?3 T
  164. . q/ Z; X- k8 o- U( A
  165. (defun game-over-p ()' v( D: P- Q+ Z) t8 F7 u; I
  166.   (beginning-of-buffer)$ k# f7 d- y$ k1 I' d* E) l
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    ! x/ j- {- }! f* M! Q4 o0 s

  168. # Y$ z$ r2 p0 M/ h! _: ]% }- ?
  169. (defun board-show (): U9 ^. {* A7 ~7 x! v
  170.   (with-board$ F6 g: H- H3 n, d: A- Y: `1 T
  171.    (concat (buffer-string)
    - Y- M0 E5 ?' X' n$ H; p9 i. p
  172.            (if (game-over-p)
    $ M4 a/ @& e1 k  C* S
  173.               (format "共%d步,输入任意内容返回大厅" (session "step")); V7 S' N- q8 a$ [) K# @6 U7 \
  174.             (format "第%d步" (1+ (session "step")))))))
    7 X7 ^) h$ Q0 {: h0 j

  175. " I/ h( n$ B9 |& J/ z
  176. (defun board-position-parse (cmd); p( R% |; B- A9 ]
  177.   (if (= (length cmd) 2)
      ]' D8 k2 l2 {* K2 Q. S, D6 D: H0 t
  178.       (list (string-to-int (substring cmd 0 1))
    ( o/ Y8 Y$ B) x3 E
  179.             (string-to-int (substring cmd 1 2)))! w( x4 D$ J, a) K' u
  180.     '(0 0)))9 x8 V! L  f) O. }# _

  181. / Q( f! j; s! H8 b0 ^6 H
  182. ;;; 游戏房间
    # P0 m3 f! U9 C7 P
  183. (defun game-room-init (cmd)! j4 d* [0 J+ r- X2 W
  184.   (let* ((middle (string-to-int cmd)): f- b. q  K0 g0 n9 l5 l
  185.          (size (1- (* 2 middle)))). r. ^$ y! _( ?9 X- u+ s+ z& ~
  186.     (with-board
    & N4 O& a' M& C! p1 G. L
  187.      (board-init size)
    ( B) |- T, y5 t7 Q- k
  188.      (board-put middle middle))), N5 I& |4 P! _, H' i
  189.   'game-room)
    . w4 h% ^: R& c. l) K  l. X

  190. 7 O- }7 Y% P8 T' H3 h( q4 \
  191. (def-room game-room
    . r* T5 W7 ]. H8 I2 L* M- U
  192.   #'board-show& N' d& d; I, R$ K' N0 b  |
  193.   (t (lambda (cmd)6 y2 N, @- t+ I% Z7 L8 {
  194.          (with-board; Q. n/ R) u; s$ L( u
  195.           (if (game-over-p). k8 ~6 m4 a* H% c4 E
  196.               'living-room0 h! t) C3 i1 v, r7 s
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    % ]5 |" k6 H; i/ U* Y
  198.               (when (board-contains-p y x)
    8 ^) ]3 }8 q2 U
  199.                 (board-toggle y x)
    : L6 Q3 p2 u, u9 H2 I) r4 y
  200.                 (session "step" (1+ (session "step"))))
    / R1 N  J. Q% i" v3 q' m
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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