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

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

  1. $ r3 t# p: S, u. J2 I& 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;">;; 定义新的游戏地图1 _0 \) [3 R) \$ o# |
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL" s, y' [+ S6 Y+ b3 {
  4.   'tutorial-room-0)                     ; 默认的入口
    % O. j) o% E+ V& j' X! J0 _( I
  5. ) `4 ?/ A' P1 ^) ]7 |
  6. ;;; 游戏大厅
    ) ~& B, S3 D0 v8 Q1 ^  B0 ?1 D
  7. (def-room living-room
    . k4 ^* n% k$ i0 A9 v7 G4 _8 W2 v3 `4 Q
  8.   ;; 进入该房间后的提示语
    ; G7 @4 C2 W: U/ b# }, N2 [1 N
  9.   "1. 教程4 I: J! X! i4 w
  10. 2. 入门(3x3)/ ~5 i# B0 C0 q
  11. 3. 初级(5x5)3 F. t8 H. S1 I& b2 }& {' j
  12. 4. 中级(7x7)
    ! _+ g1 S2 m1 h
  13. 5. 高级(9x9)0 \! h: T) w, @( t7 h' x6 D
  14. 0. 关于作者$ K5 F# g) `& z% P
  15. 请选择1-5开始新游戏:"7 Q$ a; X; N1 `* x  t
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名% |2 a- }* P# k& C. F0 e( Y
  17.   ("1" tutorial-room-0)
    2 b# X& f4 B" D( @8 d/ d
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配6 A. ^. J* @6 E; H$ U5 D
  19.                                         ; 相应的返回也可以为函数,动态返回房间名" c. F) g3 v5 D
  20.   (t living-room))                      ; 如果条件为t,为永真+ k' @6 v1 z3 t9 k/ d6 I( E4 f4 d

  21. 4 J0 m6 S9 V+ M6 Q4 J
  22. ;;; 作者信息% E) D9 Z6 I# V& f- X0 o  E# e
  23. (def-room about-room, b: I0 {1 V: H8 e1 @
  24.   "作者:redraiment
      f# @5 f. v* e+ s
  25. 微博:http://weibo.com/redraiment
    1 k( m; h- Q1 A+ H3 ?+ [
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    - y; O4 w* Q: c0 ]+ k, @
  27. 请输入任意数字返回游戏大厅。"8 F2 I0 |8 G4 ^% C
  28.   (t living-room))
    - H2 X+ o" W. [" a4 j- V

  29. 2 B# X9 y1 w+ d) e
  30. ;;; 教程* D' p) _9 P/ k( z8 q/ w' P+ n
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    3 \& M5 T7 u6 M( ^1 v! G8 o/ k
  32.   "The number of tutorial rooms")
    6 \# h! O! s% g* |" {2 N& S# ]

  33. ( f7 i% k9 ^3 ^( p# |
  34. ;;; 简化教程的定义: \( J8 I7 S' d7 W; O; ?
  35. (defun string-last-line (content)* v7 }9 M6 d$ {9 c; Y$ M1 H8 f5 J4 F$ n
  36.   "多行内容组成的字符串中的最后一行"  _2 }4 T' I2 Q8 _; w5 P+ i; d
  37.   (with-temp-buffer
    1 i/ X/ g4 T( G" S) W: B/ d' _6 `
  38.     (insert content)
    2 {  g/ m! c+ r1 `4 \) W
  39.     (buffer-substring (line-beginning-position)3 M, E9 |+ w  C0 ]! R
  40.                       (point-max)))), N' F9 q- ^5 v+ ^
  41. 2 M3 W: L7 E( s2 ?+ L! r5 `0 |
  42. (defun def-tutorial-room (prompt)
    & l" y$ g+ p5 }+ f) p
  43.   "根据提示语自动生成教程房间。% E3 j8 c0 C/ M8 ~

  44. 4 j; c/ N# T  O+ `' w
  45. 1. 提取最后一行作为问题;
    % H) M3 U% f5 @: n4 U4 ^4 j0 h5 T
  46. 2. 分析问题,获取期望用户输入的内容;
    8 T; d0 e  H0 h; t5 g, P% Q/ k7 s
  47. 3. 定义教程房间和重复提问房间。"
    / H0 r5 X9 s9 c/ R: n  V) H& L( x
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))0 Y4 f; f# U! R% }: @
  49.          (repeat-room (concat room-name "-repeat"))
    + O0 B/ D0 a( ~+ @- u( _! i: L
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))3 |( F0 A' d8 ^
  51.          (question (string-last-line prompt))
    / r9 a) q+ t# f) a5 t/ B2 F9 Q6 v5 [0 U9 b
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    % T4 ~2 P' ~8 s# y+ J' x6 z' f
  53.                      (match-string 1 question)))
    1 W# ?7 w& H" R" E$ _+ H! N
  54.          (doors (if except
    , m0 R8 b+ C  \8 M' b' _: n; L
  55.                     `((,except ,(intern next-room))& U- Y: X! d# w
  56.                       ("q" living-room)1 T- J/ ?& P9 t/ C. Y% L! K
  57.                       ("Q" living-room)# V6 z$ L2 l. m. }! P
  58.                       (t ,(intern repeat-room)))4 g- i4 S- L1 o; Y% L7 z
  59.                   '((t living-room)))))- B" L8 q# g# @: f7 o
  60.     (def-room-raw (intern room-name) prompt doors)# _1 O# b% _/ ^3 f
  61.     (def-room-raw (intern repeat-room) question doors)))
    - r$ X3 V6 ~9 r6 i! l1 J& T7 M

  62. & U& f4 ]& ]7 q; g- {1 P, M7 p
  63. (defun def-tutorial (&rest prompts)
    ' T" [) _9 p6 c* S# Y
  64.   "批量生成教程房间。"* N6 e( U5 S% W6 k
  65.   (dolist (prompt prompts)9 H  \$ u8 r# c. `
  66.     (def-tutorial-room prompt)))
    # ]1 p7 k0 c' J8 x3 [: n
  67. $ S1 c! g( I% q3 b$ i% ^. D$ P
  68. (def-tutorial% y; v- j7 a# p$ R: [5 I
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。6 k% \: f4 z  ]- w! j
  70. 1. 教程
    : u* h' G" ]8 U& n+ w2 C
  71. 2. 入门(3x3)! e/ |/ E  C; Y6 X
  72. 3. 初级(5x5)7 e) m% ], m8 x* O9 i# q% W
  73. 4. 中级(7x7)# @2 Y, U5 ~8 S; K; S
  74. 5. 高级(9x9)
    7 B/ L6 K2 q  I- l0 ^- t$ H9 }
  75. 0. 关于作者
    : O7 a' Z. ^/ H/ z# |" h8 T" ~4 ~
  76. 请选择1-5开始新游戏:' p% E3 Y; U7 z
  77. 您现在正在游戏大厅里。
    1 v5 W5 q1 @+ ~( ]$ N0 a
  78. 请输入“2”进入入门级房间"
    1 _, q5 e# o/ ^6 o% }
  79.   "  ①②③$ F, B1 U# h' K5 y
  80. 1 ■ 
    ) a5 t6 Q" F3 D* V; g! B
  81. 2■■■+ ]2 U% l, x+ ~6 h
  82. 3 ■ 3 e% [- r' }  K6 u  O4 C
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    6 v$ B  |" B+ |$ u- t
  84. 请输入“22”来关闭第2行第2列的窗户。"9 ?% ?; Y7 G( k8 W1 N
  85.   "  ①②③
    & z( ]9 e9 Y6 U5 s3 R3 x/ b
  86. 1   - @/ B- z9 w5 _
  87. 2   
    ; }/ o5 t: L* z
  88. 3   
    : W& W+ H5 [( Q% R0 v- L6 T
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    . H/ C; t# W' m2 N- ~
  90. 请输入“11”,试着开启左上角的窗户。"" n1 g9 z% k% A4 ]4 Z
  91.   "  ①②③
    9 x. [& @" s& M6 G  _: G% }: x. x6 e/ E
  92. 1■■ 
    ! t* x; y4 F3 C0 Q. X0 R2 A* T
  93. 2■  ; O: X6 @3 `% u) f) p
  94. 3   ; u7 n; ^2 f! f$ _4 v0 h) i' C6 T4 r
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    ( U! e% r1 L0 T  d$ B% ^
  96. 请输入“13”开启右上角的窗户。"% e: ]8 C0 B, z# p- `2 P
  97.   "  ①②③
    : b. F$ E9 J8 t+ ~: @1 h
  98. 1■ ■
    & o9 A( \3 `2 w3 h& _, B
  99. 2■ ■( Q+ Z! [2 x9 H4 h% ?
  100. 3   
    6 R) j: y% @0 R) x9 f
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。* s; s9 f4 e- D3 n
  102. 请输入“31”开启左下角的窗户。"
    0 q7 y2 k: z- R9 o1 k6 N9 {
  103.   "  ①②③7 S+ [' ~; M7 h9 j; u
  104. 1■ ■: G1 j; E! c3 ]2 Y. t9 ^! @& W
  105. 2  ■
    % d- ^! s4 U1 o% ]# l0 v) @
  106. 3■■ ; Y) ?6 z8 w# Z" x
  107. 此时,总共有5扇窗户被开启了。
    ' D7 i$ X% G1 _' {7 |7 c: T
  108. 请输入“33”开启右下角的窗户。"' _3 ?, x, ~, V& H
  109.   "  ①②③
    ' F  e6 f% d; w" ]9 s2 x
  110. 1■ ■+ }# \$ Z; `9 r" v: o9 [7 e3 r% t
  111. 2   # M: d& k( }3 t4 y6 M
  112. 3■ ■
    . g. l8 D& u# @5 T7 R# u( [. Y
  113. 现在,只有四个角落的窗户被打开。
    3 A  e4 ~% I, F8 Q1 U
  114. 请输入“22”完成最后一击!"
    9 T) `: k3 _+ X' d2 M$ v' m
  115.   "  ①②③; q0 h3 w# T- k6 T0 M3 N, W
  116. 1■■■
    & h; M. d* J3 h3 ~# `/ \
  117. 2■■■
    2 A7 R. a, ]: L
  118. 3■■■
    7 L, X- z" Q2 w( i  B1 b3 W
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")8 w& m8 V7 k  [# c; l6 W

  120. ' U' Y+ ]: D2 u5 n+ d) e1 V
  121. ;;; 棋盘- K5 S: W2 N( i( X2 c/ P4 ~& i2 o& z4 i# d
  122. (defconst *wechat-5x5-white-chess* 12288
    ( {8 M' O/ m2 Y! W
  123.   " ")- K& W5 T* e( o  `, V' ?( N
  124. ! f& y, c& {4 \# X0 t$ {
  125. (defconst *wechat-5x5-black-chess* 96321 _$ P3 r% e% b+ ^6 a3 p$ h$ ?
  126.   "■")
    $ Z& d& D, N; l2 W8 c; {

  127. ( R( n, {  B4 A, g  l9 H8 l
  128. (defmacro with-board (&rest body)5 Z; Y! |- I2 ~
  129.   `(with-temp-buffer
    - ?1 G! U  _0 c
  130.      (unwind-protect8 Y' Y3 B, ?9 b: }/ L5 B2 G5 ]
  131.          (progn
    6 R8 |( o: I  ^& ?6 q  E+ [
  132.            (if (session "board")" @* w! L+ w, N- k; a; @2 H
  133.                (insert (session "board")))1 J4 B- }+ n; j' p
  134.            ,@body)
    0 s3 r; E0 ~% s/ g+ u) C
  135.        (session "board" (buffer-string)))))) B- X1 H# E" k# j( \7 ]
  136. ; e, D, q% q2 H+ A
  137. (defun board-init (size)" P5 ^6 _3 L2 o) y# K% P
  138.   (session "size" size)
    5 q' \! M3 ]8 M1 [
  139.   (session "step" 0)
    - O, `/ j$ h: N5 n' k/ x+ ^
  140.   (erase-buffer)
    & I. V' w) X5 K: n
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    + M( m9 u" ]) R6 i$ p
  142.   (dotimes (row size)
    + p! r3 Q2 i4 Z: i4 o
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    8 E9 r: i+ j9 U' s
  144. + y0 x8 C5 [4 G1 H7 u
  145. (defun board-contains-p (y x)
    $ K  o" J+ W# P9 R' s
  146.   (let ((size (session "size")))& V0 K1 \: G7 I
  147.     (and (<= 1 y) (<= y size)8 {3 ]8 f( |  P7 D
  148.          (<= 1 x) (<= x size)))), u/ C) t- D0 N. m. ]) q
  149. / {; c" ]9 H0 f0 b
  150. (defun board-toggle (y x)4 x6 Q- x! w# m3 T' \4 p0 a
  151.   (when (board-contains-p y x)( q: W; p8 k; m" ^1 I: B( d
  152.     (goto-line (1+ y))
    + ]" f) m; H( ^0 n0 }
  153.     (beginning-of-line)
    $ ]. }; W/ s$ }
  154.     (forward-char x)& l9 Q  b8 ]- M# b
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))  O! R2 e, _- x* h* E% ^+ b
  156.                 *wechat-5x5-black-chess*
    ; V2 o$ N0 m2 [3 K
  157.               *wechat-5x5-white-chess*))
    4 F% `8 r% e# b% p- J" }
  158.     (delete-char 1)))
    0 o9 R1 M7 O0 {1 O  F

  159. $ J# L/ A3 |  c( u. Q
  160. (defun board-put (y x)
    4 u7 ?( j% E5 P2 X" x; X! B
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    / k) K/ z, G! V$ B' P$ ?& Y  [
  162.     (board-toggle (+ y (first dir))& ^; b$ c& @* `0 y- W% A0 ]" y- |( ^
  163.                   (+ x (second dir)))))
    $ k# D6 i/ p* S. i0 L+ _4 l# H  Q
  164. , K0 Y9 C; E  }' d) G1 {
  165. (defun game-over-p ()
    ( j5 L: S# G% }4 t+ I
  166.   (beginning-of-buffer)3 k/ A# b" [8 ?$ m# y( J
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))+ O% h  C$ l5 X2 Z! _0 w$ @

  168. ! i7 \" K+ a3 P% a* h; X0 A% I" t
  169. (defun board-show ()
    : N* a# f$ u: g
  170.   (with-board
    7 ^9 Y# N( y% m3 \+ B
  171.    (concat (buffer-string)
    7 X0 \6 ^2 S. [6 J& K9 a
  172.            (if (game-over-p)
    $ J7 {' v9 ?6 L' {: }" h/ g
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    - V+ k% i0 O' c" E( _% a5 c5 M
  174.             (format "第%d步" (1+ (session "step"))))))), r2 p, H# P2 d& h  R
  175. % a8 X5 D0 d" s! a$ _, l
  176. (defun board-position-parse (cmd)
    6 o4 y; @2 D1 s. {2 `% R
  177.   (if (= (length cmd) 2)0 ?7 w' a! ]) M6 v0 U& C
  178.       (list (string-to-int (substring cmd 0 1))
    4 N. @, u( U1 {1 G' N( U+ W
  179.             (string-to-int (substring cmd 1 2)))
    : I4 P0 S( L* w2 t3 G4 K
  180.     '(0 0)))9 D4 T$ ^+ _* E) O) v( M
  181. * q1 M/ v# p2 r' g/ L
  182. ;;; 游戏房间
    9 e1 f6 a- R/ l! u' z6 S2 q
  183. (defun game-room-init (cmd)" O, W7 ^" t7 X+ V
  184.   (let* ((middle (string-to-int cmd))! x$ `' {3 A& u, ^
  185.          (size (1- (* 2 middle))))0 ^' e. ]5 S9 p7 r
  186.     (with-board
    ) @  r9 j, T' F0 k2 y
  187.      (board-init size)% N% l# {& R+ ~
  188.      (board-put middle middle))). P: q& N7 e- |: d6 a
  189.   'game-room), y3 O: B6 W9 Q5 j6 B

  190. 6 p0 z4 k1 B, Q. C" H
  191. (def-room game-room
    0 E- z3 W: q* H$ e" _/ [
  192.   #'board-show( M# Q( D% {# v  }8 L! S0 X
  193.   (t (lambda (cmd)& H+ d- a1 e0 O! W  b$ P! M
  194.          (with-board
    , T6 c' A6 f9 _$ g
  195.           (if (game-over-p)' s$ t: V$ t  h
  196.               'living-room% x; U1 T6 }: p+ d
  197.             (destructuring-bind (y x) (board-position-parse cmd)0 I8 |3 w3 i1 z% i. O8 m
  198.               (when (board-contains-p y x)
    ; r8 D8 Z# z- W# p
  199.                 (board-toggle y x)
    7 E0 C% R- l) @' q: ^
  200.                 (session "step" (1+ (session "step"))))
    4 @7 j* V8 I. I" H( r) L/ |" V
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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