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

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

  1. $ b; U' L% B2 w# M, [
  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;">;; 定义新的游戏地图
    $ Z  s+ S1 E  c
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL2 A& l8 \) y" P# k4 Q7 f
  4.   'tutorial-room-0)                     ; 默认的入口
    # x! Z( R9 |/ X5 ?
  5. . a4 E3 [0 `7 ?
  6. ;;; 游戏大厅; r4 S, P. e) z! B  p
  7. (def-room living-room
    + }7 ^: j/ H# S7 ^5 t' {* O
  8.   ;; 进入该房间后的提示语$ f' A  P: Q: E
  9.   "1. 教程. d$ O: L: g' {' p
  10. 2. 入门(3x3)
    - C1 y; T- G* t3 P9 H8 \  i9 s3 c  r
  11. 3. 初级(5x5)1 b# |, ~9 X4 d' @7 v: k* I
  12. 4. 中级(7x7)  }2 P6 {+ L% s- x! p3 v
  13. 5. 高级(9x9)
    / s0 |2 x( N) f7 ^
  14. 0. 关于作者) R* g! |6 L% U7 t, @" Z5 p
  15. 请选择1-5开始新游戏:": ^5 F# V; [9 k* e: B2 T' }9 V
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名5 w/ c0 W3 {. ~. ~: m- ?
  17.   ("1" tutorial-room-0). u9 f0 R' a% H9 H
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配1 k! {5 N4 L2 T9 c" ?. K
  19.                                         ; 相应的返回也可以为函数,动态返回房间名0 ]7 l' M  c, C* H# D7 ~2 F# w
  20.   (t living-room))                      ; 如果条件为t,为永真: m9 Q. Y# x  I' t" f

  21. ) C4 H) J, w+ u- s' `0 k
  22. ;;; 作者信息
    % I" |' r7 }$ P6 p
  23. (def-room about-room
    % L2 Z5 p- Q' m6 |2 f& \9 K
  24.   "作者:redraiment+ w( R: l. e4 k# X) E+ K4 ]
  25. 微博:http://weibo.com/redraiment1 N( o, w# x+ D4 c2 p# W$ f8 t
  26. 有任何建议,欢迎在微博或微信上联系redraiment。, D3 k: T2 g# x3 W7 Q
  27. 请输入任意数字返回游戏大厅。"0 S- I2 Z- |$ r! J
  28.   (t living-room))
    9 B) X4 i, G! Z' h

  29. . Z' D+ i, ^  B* f9 J( i" Q+ _3 _
  30. ;;; 教程6 }) t/ H) q( i" {4 c% G, ~! k; E
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    " G! j$ s: x1 @2 _) U3 x
  32.   "The number of tutorial rooms")8 K; ^6 \+ x; Y. L# ^

  33. # o1 w, i/ v8 C& V+ ^/ o0 p: B
  34. ;;; 简化教程的定义! A4 d- l/ i1 [6 o4 I0 `4 e6 @
  35. (defun string-last-line (content)
    # n+ \# A; {& u$ h, A! h# k
  36.   "多行内容组成的字符串中的最后一行"
    $ a- Z# I$ R' J9 i( @- L# `
  37.   (with-temp-buffer" Q- h1 ~& ~; K
  38.     (insert content)1 v, L+ y" \, U- R8 [: [. O/ J
  39.     (buffer-substring (line-beginning-position)
    9 ], {3 V: d- ^! V, ~
  40.                       (point-max))))
    + l3 ?8 b$ D; v, T+ t1 O6 w
  41. / C( @. e6 r0 g4 }8 N* e/ u0 P
  42. (defun def-tutorial-room (prompt)* G' G9 J# l, [6 G1 O
  43.   "根据提示语自动生成教程房间。3 R9 G8 c+ K& g5 ^
  44. ; V) w; e0 I. B5 a- O4 \
  45. 1. 提取最后一行作为问题;
    : W+ n5 l/ g* Z; |3 f$ S6 q
  46. 2. 分析问题,获取期望用户输入的内容;
    $ A( P3 ^2 Z! F- P
  47. 3. 定义教程房间和重复提问房间。"
    ) L5 O1 m! q! L" ?$ k8 |
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))1 ~, p9 M& `7 j2 M. u) B- p* \
  49.          (repeat-room (concat room-name "-repeat"))) W5 P. D: Y2 q" P5 w( c
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    " k9 T: e7 V( l1 I) L9 N  C5 b+ A
  51.          (question (string-last-line prompt))
    1 i: k: C* R3 C9 a2 j
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)) k4 Y: k" q/ u: S; M# i, y4 Z7 V
  53.                      (match-string 1 question)))
    : \* ^( T4 Q0 e8 e
  54.          (doors (if except! G# d- X, S% t+ ]* M$ I/ c7 Q2 i
  55.                     `((,except ,(intern next-room))3 O: n9 M. F  t) f5 v2 ~
  56.                       ("q" living-room)
    * D0 N/ v( k, x* Y
  57.                       ("Q" living-room)
    8 d1 N! d. p, H, ~# a5 Q
  58.                       (t ,(intern repeat-room)))
      I* I! h, s+ \) u9 F# ?/ g
  59.                   '((t living-room)))))
    . y& _6 _' W' Y9 C
  60.     (def-room-raw (intern room-name) prompt doors)5 ?* M5 q" f" N0 T6 \5 i% Z
  61.     (def-room-raw (intern repeat-room) question doors)))
    7 D4 a$ r8 P& P/ X# d, G) O8 x1 h, k

  62. 3 X# t  o3 |0 l: b/ ^
  63. (defun def-tutorial (&rest prompts)) l; }8 T. m1 H6 o5 Q6 W: ^
  64.   "批量生成教程房间。"8 M, E, L: M3 k: C. @
  65.   (dolist (prompt prompts)3 a/ f3 Y( J, E4 S$ Z9 q
  66.     (def-tutorial-room prompt)))
    ' h; n# T2 M. `
  67. " z- W" Y! s4 @$ F/ P, Y" W' B
  68. (def-tutorial
    5 A) d  w* U/ |4 p" r
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。0 M& V! p" o. W# m" V
  70. 1. 教程8 i  F2 L4 f2 }# J8 {8 D7 \, j6 K
  71. 2. 入门(3x3): A0 g# V- Q8 d) O* e5 J$ l
  72. 3. 初级(5x5)
    $ e4 U$ p5 C3 Z: x: E- H5 v
  73. 4. 中级(7x7)' J2 l" z9 Q" \
  74. 5. 高级(9x9)
    9 _5 N/ `* C3 M/ S% k
  75. 0. 关于作者
    * `* j: T; @" M4 Z- K* W
  76. 请选择1-5开始新游戏:5 c/ Z6 y) _* ?
  77. 您现在正在游戏大厅里。9 p9 G2 B8 G7 i! t% h7 r% d
  78. 请输入“2”进入入门级房间"
    2 g/ R% \. {0 p8 q0 W
  79.   "  ①②③, }6 k; \. ~, }& d2 R3 o$ H
  80. 1 ■ 
    $ |# x, G0 d+ j* d) e: f+ R6 [+ \
  81. 2■■■1 h6 K$ ?/ D# o5 G/ Y# Y+ N
  82. 3 ■ 4 q9 ~4 ~. l" C! K+ n: }& u
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    ' k' c* U- X) s  a, D6 X* b7 J
  84. 请输入“22”来关闭第2行第2列的窗户。"
    " ?. [3 p) ]$ N8 @  ?
  85.   "  ①②③! l6 ~" h3 H/ D, N2 X
  86. 1   + E9 s1 m: m" O' S* \
  87. 2   
    # c1 i0 i- a6 c. j$ ^
  88. 3   , }/ `0 t  R2 f* ~" O3 Q
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。! R6 D" S2 s# A4 R
  90. 请输入“11”,试着开启左上角的窗户。"/ s8 p6 f* E# W
  91.   "  ①②③
    . N9 A- H: I9 ]0 g$ ]1 s# f
  92. 1■■ 
    3 X/ O, Y; l% K2 m/ B; T) V( b3 C
  93. 2■  
    $ c+ F3 p1 |% h+ u. h
  94. 3   
    . ?, [9 }) ^; }9 {+ _
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。0 {6 b, Q5 Q8 W  ?* t3 |/ P- t
  96. 请输入“13”开启右上角的窗户。"! O  q1 I' W0 {7 a8 a
  97.   "  ①②③' ]) |& N. H2 L0 \/ k
  98. 1■ ■. q  e3 ^- g$ C6 G
  99. 2■ ■
    & H, |, S4 O! L0 Y8 q. W
  100. 3   
    ' F+ c+ U) h4 G- b4 s! j: C
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。  V& c8 O9 Z" I) L
  102. 请输入“31”开启左下角的窗户。"& w# \1 k0 C( a  k8 `
  103.   "  ①②③
    $ |# G$ v4 q% w% {; I# H! ?
  104. 1■ ■
    0 N" b0 U2 X( x* d
  105. 2  ■3 F2 i8 K* I( [) w6 X6 M
  106. 3■■ 
    8 x) I! \  s& V# f
  107. 此时,总共有5扇窗户被开启了。6 e8 J' k! k3 K3 T
  108. 请输入“33”开启右下角的窗户。"
    - W0 J. t. j! _3 v6 ]# {
  109.   "  ①②③7 ]5 C( U7 }0 _* M9 E2 O
  110. 1■ ■
    3 g9 ]! o# x! i9 y& F; x/ ^
  111. 2   8 n/ {, w- f% r# E7 w: B- z% l
  112. 3■ ■2 j1 g4 Y* x; N
  113. 现在,只有四个角落的窗户被打开。
    5 j3 u0 V1 Y6 L) y. z: T
  114. 请输入“22”完成最后一击!"1 ~$ s4 }9 ?9 d! W/ E5 p& O  p: d
  115.   "  ①②③; h! X; R1 w  u+ k$ R7 R
  116. 1■■■
    1 P; s/ \" y! [5 D1 p, j7 y' ~
  117. 2■■■3 C: L6 P6 u. D
  118. 3■■■
    8 u1 r0 [- r' p4 }
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    . C. j; O% c8 ?/ n0 b8 n* M1 [+ \+ ~% q

  120. $ n% F; r# s$ ^7 _: o
  121. ;;; 棋盘- N2 X9 r8 n4 Q7 O3 u
  122. (defconst *wechat-5x5-white-chess* 12288
    ( u" u' h9 i' K  E7 `; E
  123.   " ")
    2 B3 o7 ]1 s: ?) w4 U
  124. , }( K+ s2 ]" ~, Y: s% u  a
  125. (defconst *wechat-5x5-black-chess* 96321 G" y6 q) G' E  Z& U# B& ?
  126.   "■")
    6 n& p4 i+ j% m( _! d  y, z9 X8 S

  127. + x. o1 u2 o% H
  128. (defmacro with-board (&rest body)" b$ ]( p2 X0 D/ |. L) k
  129.   `(with-temp-buffer" B. u1 N( @+ H0 N0 @  @
  130.      (unwind-protect
    * R5 `& \* @& S: g9 r" E( ~% H
  131.          (progn
    6 g4 o. x/ x, N; }, N# A
  132.            (if (session "board")
    2 ~+ A3 ?) z7 B$ k1 H7 W: c! w5 _6 ^
  133.                (insert (session "board")))7 j& z/ f( z- Y. J8 `
  134.            ,@body)3 h# }# S4 z) k! W! K
  135.        (session "board" (buffer-string))))); Q8 \0 b# J9 r' i9 L* |

  136. 0 l$ a! p/ P0 J+ S+ {4 w
  137. (defun board-init (size)
    ( [- o+ O6 v3 ]! o
  138.   (session "size" size)
    9 H. g6 E3 V% H$ }0 s" W, w$ {1 u
  139.   (session "step" 0)
    ! g* V. J1 v. f% U
  140.   (erase-buffer)! N) g1 E6 J- o; [/ ^+ [) E
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    . ~, d, F0 N+ l! ~
  142.   (dotimes (row size)
    ( U8 h& U+ N* U0 b# s0 \3 E4 q9 r
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    & A$ D) }, a7 r, ?8 K! J& z
  144. . J9 P; }) I3 @  _  G* m+ j, s
  145. (defun board-contains-p (y x)
    & D; ]5 ^/ I! ^4 r& R
  146.   (let ((size (session "size")))6 T! E# {& A: b. d" R& a5 b% T
  147.     (and (<= 1 y) (<= y size)" w5 @8 n( G% S% l; m& K# M
  148.          (<= 1 x) (<= x size))))( ]9 l7 V3 |: M
  149. 1 P! I# `4 P; k4 u2 Y: f3 A
  150. (defun board-toggle (y x): |, S; G9 x7 h" p& x) h
  151.   (when (board-contains-p y x)
    ! ^3 t0 L2 q7 H0 _9 J
  152.     (goto-line (1+ y))$ @4 k  U- V6 E
  153.     (beginning-of-line)
    ' }6 v( C! L1 q. S+ k
  154.     (forward-char x)3 C2 g4 ]2 I) }3 L8 v
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))" i* d! [9 E: S+ W; \. Y
  156.                 *wechat-5x5-black-chess*
    ; h7 B  m6 f4 A  \  K, D
  157.               *wechat-5x5-white-chess*))# ?# Q9 M+ o" q- a
  158.     (delete-char 1)))2 k# T3 X) g8 `9 C8 T
  159. 6 P& q2 _* E2 P5 B) W
  160. (defun board-put (y x)% W+ o- R$ D: T/ T+ Z
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))' C# ^0 m1 v9 r. f. Z. S7 C2 w5 w
  162.     (board-toggle (+ y (first dir))
    6 a( j7 n; w/ b4 `1 ]1 h2 t
  163.                   (+ x (second dir)))))
    4 @% B7 a& i* T3 F/ Z$ Y; K
  164. ( \: e5 l8 K0 U- ]
  165. (defun game-over-p ()
    % U2 J4 U) n$ d+ g
  166.   (beginning-of-buffer)/ Z& v/ g" }9 G2 x. f: K2 U
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))/ T& X& K+ @6 {( ~  o0 Q) V
  168. ! P: W8 D# x4 d3 u. r
  169. (defun board-show ()
    0 @# C  ]3 D2 N
  170.   (with-board
    6 N! `7 H9 h% _1 |
  171.    (concat (buffer-string)
    ' o1 x5 ^8 U0 c3 W  ^2 r
  172.            (if (game-over-p), H! _( `+ b% J% W  B' p
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))5 d5 k/ V' @9 e; z8 w
  174.             (format "第%d步" (1+ (session "step")))))))
    & c7 u$ y- B: ~8 N
  175. % {6 U) j; X$ A( N, g  d8 D/ ^
  176. (defun board-position-parse (cmd)% Z& D4 |( V' Y1 E+ U1 v) X% |: n& {
  177.   (if (= (length cmd) 2)( I4 u( G" }8 k9 G, j
  178.       (list (string-to-int (substring cmd 0 1)); j2 q( `7 F4 ?# u) d" q( D3 I
  179.             (string-to-int (substring cmd 1 2)))" P) {' j( n7 l$ W6 J$ z  l
  180.     '(0 0)))5 g: i& u4 r' R' f' h* n
  181. # d9 U9 b$ V% C! a3 a' z
  182. ;;; 游戏房间9 i! D- M* s# H8 k) i) U; a7 a
  183. (defun game-room-init (cmd)4 V$ v) G0 K* P* \$ U1 F
  184.   (let* ((middle (string-to-int cmd))
    3 J& d" l' A% Y
  185.          (size (1- (* 2 middle)))). i8 S2 \2 U. N8 r' \
  186.     (with-board4 W$ i  Q2 O3 W0 S0 N
  187.      (board-init size); R5 ^6 i" |& L. U, g" L
  188.      (board-put middle middle)))
    - o, s" t1 O3 c
  189.   'game-room)
    2 @$ [2 K! o% N: Q4 T: r3 D( X

  190. 4 [  h" s+ S- R/ h0 \: R2 o
  191. (def-room game-room4 B% g6 @4 M4 H7 W8 M1 b0 @3 J/ Z
  192.   #'board-show
    ' h( c3 u$ v0 U, J1 {
  193.   (t (lambda (cmd)6 D3 {9 V  R0 W+ T2 c* Q( x
  194.          (with-board0 {. q$ j6 p2 e
  195.           (if (game-over-p); C9 Q# \  L. y( h# F) \! ]( g
  196.               'living-room$ E' g5 E1 t- c8 f
  197.             (destructuring-bind (y x) (board-position-parse cmd)- U: k& d. T* ?* l
  198.               (when (board-contains-p y x)5 w( x4 s& c) F8 L
  199.                 (board-toggle y x)
    ( J2 [4 f- ]0 h1 k
  200.                 (session "step" (1+ (session "step"))))% B0 h! k- y) |# t$ y6 o
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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