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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。0 l6 l5 b& P0 L; Y/ J+ D
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. 8 j% @0 }* M& f
  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;">;; 定义新的游戏地图
    ( _; I0 ]/ |: X( A/ }  Q+ F/ N
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    ( r" c3 }! D9 k3 Q8 W1 ?5 [
  4.   'tutorial-room-0)                     ; 默认的入口
    " z' Q2 F# y1 C1 _1 b

  5. ; g# d# [- p" q" N1 J5 K/ l9 R) N
  6. ;;; 游戏大厅! x% k: x' M1 P7 p, l
  7. (def-room living-room
    " ~2 }: A0 l7 o+ P$ w' `1 j
  8.   ;; 进入该房间后的提示语% F0 n8 F5 d: g7 z' z+ O# E9 ?4 m& E
  9.   "1. 教程
    - z! ]* r  d& ]: J  ]
  10. 2. 入门(3x3)
    + s! T! |/ @9 k8 L, h& Y( k0 ~
  11. 3. 初级(5x5)1 X0 M9 Y8 B" z+ z" M( \
  12. 4. 中级(7x7). G% [7 I9 O9 p- j) M9 A
  13. 5. 高级(9x9)
    5 ^9 J, [, p' _
  14. 0. 关于作者
    / e& Q6 C8 f. ?" |5 L$ l
  15. 请选择1-5开始新游戏:"" G, v) Z7 f! B" R: }- f8 C; T( D
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名  n4 E6 E" L+ ^$ t
  17.   ("1" tutorial-room-0)
    ( u. p4 `6 p9 }6 r  L
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    $ @3 C0 h& A6 I% O
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    2 D& q+ X# P( J! G; r0 Q
  20.   (t living-room))                      ; 如果条件为t,为永真6 _0 |8 y3 y' |7 n$ K$ ]

  21. + B: N2 |9 R! @5 [% I, h
  22. ;;; 作者信息
    # e4 O( k1 j  g1 H$ Z$ i# ^
  23. (def-room about-room+ _1 C/ d$ N8 y7 W
  24.   "作者:redraiment  Q6 W/ H/ Q! R  d
  25. 微博:http://weibo.com/redraiment4 l% A* w- l- c& ]% S- d
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
      y, M. q7 E0 l- Y( {
  27. 请输入任意数字返回游戏大厅。"1 K# R# T" V" G' V% a3 c# O
  28.   (t living-room))
    8 a5 D$ Z/ K. {% @

  29. - a5 _$ x9 }9 Q6 S* |
  30. ;;; 教程
    3 Y! a; T$ l# t# N8 s6 J2 b1 H4 v2 W
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    & Z1 [+ E% R) `0 f  }
  32.   "The number of tutorial rooms")
    5 }* y+ k- }* `3 J
  33. 6 B( p& o8 j4 t: J- {- P: p" a
  34. ;;; 简化教程的定义
      }7 B- m* \& [3 j- t9 f3 J5 g; R
  35. (defun string-last-line (content)9 O! p7 }0 x* C. I. T
  36.   "多行内容组成的字符串中的最后一行"1 u. }4 x$ M( d9 d! H
  37.   (with-temp-buffer- s3 X8 k/ s% d# w) X, {
  38.     (insert content)( `! p0 `) g' F6 e* J( G$ ^
  39.     (buffer-substring (line-beginning-position)
    / u4 Z0 m- k  W8 U* U
  40.                       (point-max))))" U0 s( b7 f- J$ P" ]# S. d
  41. - h7 g0 [3 b2 j0 X- s) b( Y
  42. (defun def-tutorial-room (prompt)
    ) l* y5 ?/ ~) `  j; {
  43.   "根据提示语自动生成教程房间。
    ) V5 ], S0 _* t! \; P

  44. ' h6 z7 b4 l5 M  c. @/ L2 C
  45. 1. 提取最后一行作为问题;" b# e5 z# z, \
  46. 2. 分析问题,获取期望用户输入的内容;  b8 d9 J" D! M, y2 w) m; N
  47. 3. 定义教程房间和重复提问房间。"0 @8 n# T0 }- i! B+ ~, H7 y9 d8 N
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))2 W# M6 a3 v/ r
  49.          (repeat-room (concat room-name "-repeat"))
    ) a5 J7 {6 L: j+ q! G0 ]' `
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))5 ]1 P8 n( ~" Q
  51.          (question (string-last-line prompt))* O% w# k: E; u
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    5 g* ]2 c" d8 q! O
  53.                      (match-string 1 question)))% W- P0 H- N  c0 F; j9 A$ t& }
  54.          (doors (if except
    . G0 B& h# C  X9 D0 L
  55.                     `((,except ,(intern next-room))
    ) l5 Q6 a3 u! G# ^6 b; T: F
  56.                       ("q" living-room)
    - g4 i$ B/ D1 q3 k9 k7 Q* S% S2 |2 I
  57.                       ("Q" living-room)
    $ c; R9 {, h, |
  58.                       (t ,(intern repeat-room)))3 g. @4 p: g0 T& V2 j
  59.                   '((t living-room)))))
    ' x' [* l" W, U. z, @
  60.     (def-room-raw (intern room-name) prompt doors)
    8 A) t- O! u: G3 N3 R. V
  61.     (def-room-raw (intern repeat-room) question doors)))
    # r$ c" w4 [% R1 N3 @

  62. % ]0 ^2 B) H+ ~! X
  63. (defun def-tutorial (&rest prompts)- E: i, D( o4 u( i7 F- E1 I% w
  64.   "批量生成教程房间。"
    7 V9 ?9 z7 Y" u2 P: \5 a9 e; J
  65.   (dolist (prompt prompts)
    9 ~) Q( k4 E+ I: G: n0 S2 E
  66.     (def-tutorial-room prompt)))
    : _& D' R. u( {
  67. 0 M; n* R. x8 r2 W
  68. (def-tutorial/ d7 h8 T4 L4 M+ [
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    ; T2 s2 h" A& ]7 \$ b8 T' j
  70. 1. 教程! h6 E2 w# u( a: O/ {' M) a
  71. 2. 入门(3x3)
    , ?; R0 i. q" `% N( n) y7 ]) A5 M
  72. 3. 初级(5x5)
    " U2 p+ \5 v- P! ?2 Y
  73. 4. 中级(7x7)
    - Z0 S7 d3 C) k% Z$ J
  74. 5. 高级(9x9)
    & \  {) Q' [7 z; o8 O* k( P
  75. 0. 关于作者7 z7 h: c+ [' k+ n3 b, H4 _
  76. 请选择1-5开始新游戏:! q6 p2 a. U, x) w" z, ~, R* r
  77. 您现在正在游戏大厅里。
    * r; T4 H0 h! [  `* |# U
  78. 请输入“2”进入入门级房间"# `1 [( l1 O# Y/ ^1 N7 t
  79.   "  ①②③
    : U% b" |* t% T: A
  80. 1 ■ 
    . m  _) k0 F1 \
  81. 2■■■% J! q0 d3 b* Y. n: k5 \$ W/ X
  82. 3 ■ " m" s5 V" e  D3 Q( n( w/ e
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!) t/ d; W8 O! T" e- k
  84. 请输入“22”来关闭第2行第2列的窗户。"
    # K9 B+ j3 L# K
  85.   "  ①②③( |+ d) t  v  }% e
  86. 1   
    ( n) l/ G. B. _/ L9 Y& F
  87. 2   
    * Y) l' Z: J" S. i- X7 I9 u: `
  88. 3   ' _3 a0 f7 X5 L) F$ @
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。' G3 k3 j& I# r( T; m$ M
  90. 请输入“11”,试着开启左上角的窗户。"* G+ U# ~: v0 Q) c  J$ F& f; S. U
  91.   "  ①②③
    & X) p0 q' Z( _: T+ ~
  92. 1■■ ! _* U, x4 B. L5 I" F: o
  93. 2■  
    " X' i6 _" J4 ~" U( y( B0 c( I( U. G
  94. 3   7 X% W& ]+ z* S- N) I( q# y
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    " L) H( {5 r! z: z; a0 X# J/ V
  96. 请输入“13”开启右上角的窗户。"
    - Q5 N/ [9 k, k7 U% ~, s, J- s9 k
  97.   "  ①②③* e& P3 q9 C( m# y) a1 B
  98. 1■ ■
    1 c4 j! {, g9 e, Z/ B
  99. 2■ ■: _7 |/ r& \; F2 L. {" \
  100. 3   - ^* g* H7 P" W% K  y* F) V, ~
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    % A6 A2 ~8 [2 @0 b! L
  102. 请输入“31”开启左下角的窗户。"! m1 j9 O: L2 \5 `
  103.   "  ①②③. D5 E- H! s3 d% L; x5 Z
  104. 1■ ■
    7 H0 J7 s( a( i' Z4 z
  105. 2  ■( r7 P0 K5 U; {- v5 Y
  106. 3■■ & b$ T3 G# x) F) U  M( Y6 p, o. B
  107. 此时,总共有5扇窗户被开启了。1 l. i( S5 B# ]' ]
  108. 请输入“33”开启右下角的窗户。"
      g- k6 I1 |; J! B- ?/ Z2 U
  109.   "  ①②③
    8 d: ~, h4 Y3 p; t4 \6 _; b( {/ z
  110. 1■ ■) g% D$ h8 d. |0 G/ b& h" _) z
  111. 2   % T9 O8 d  e! b, j: x/ U& t9 K
  112. 3■ ■: ~9 G" [# Q$ c. m# O# X1 E5 g
  113. 现在,只有四个角落的窗户被打开。
    : |, }( A. U$ Z9 K) O: B
  114. 请输入“22”完成最后一击!"5 z  |# J7 M) l) }/ B  |
  115.   "  ①②③
    2 ^6 u' B  t1 p1 E1 i( j9 ?9 L
  116. 1■■■
    , f% |# x% X) z8 T% c+ a
  117. 2■■■
      m- x5 Z: ]# \4 `
  118. 3■■■  ^0 k5 u  M% U
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    / r7 A; `4 d6 g

  120. 0 ]. x/ @6 F6 g0 a
  121. ;;; 棋盘( I1 U7 j! ?/ K# n. u+ `
  122. (defconst *wechat-5x5-white-chess* 122885 B: n, g. o1 L% a6 z; R
  123.   " ")
      I* ?' X" `: o0 _& ]* W9 Z

  124. / j4 P1 E9 |) e, ]
  125. (defconst *wechat-5x5-black-chess* 96321 B- v; h# f4 P) ~3 o$ {$ \8 @
  126.   "■")4 R2 X: l- S# ~5 S

  127. 6 d" T+ b  v6 c# T. D5 Q
  128. (defmacro with-board (&rest body)1 N/ [! m. G8 Q0 C
  129.   `(with-temp-buffer* d0 p: `* j* P, a" s2 ?
  130.      (unwind-protect
    - c& w  ?% l* r
  131.          (progn
    / @& R" D* z, `7 \4 X- b
  132.            (if (session "board"): f- H, ^. v- B1 o' z7 Q6 V
  133.                (insert (session "board")))7 n. e7 o4 {# e& _, s4 @9 N  V
  134.            ,@body)
    1 k& o+ m6 O0 B
  135.        (session "board" (buffer-string))))): r; t, [7 Z4 f6 H! X! K- N
  136. 9 E: Y/ b9 Z& d- `  N4 y1 }8 w4 K
  137. (defun board-init (size)
    # {1 i$ |" L' L& X! ~( ?) c
  138.   (session "size" size)
    - R- Z6 b! M6 B; }
  139.   (session "step" 0)
    / J: }) I; \3 a1 \1 e9 K, Q
  140.   (erase-buffer)( o* ~1 L/ S9 i5 g7 m4 Q* L" f/ T
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    . x8 h$ x7 J  C9 P; {# r
  142.   (dotimes (row size)
    1 X. [# t. m4 G- j
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))3 |4 y1 k0 [" E6 C3 h

  144. / {2 B" z* |: U% [: M6 y1 e9 m) w
  145. (defun board-contains-p (y x)
    + s3 u$ l6 l% e/ b  p- ?8 s' c6 N- w
  146.   (let ((size (session "size")))
    " D3 K. N5 V/ A
  147.     (and (<= 1 y) (<= y size)9 P9 l2 {5 V: i
  148.          (<= 1 x) (<= x size))))
    ( x, \9 [1 @* O3 q

  149. , |# k5 ]: s+ p- v6 b- g8 W
  150. (defun board-toggle (y x)
    , X3 B9 l1 _* W% v% z% N
  151.   (when (board-contains-p y x)
    % A: d" s# v% Q: Q1 {$ \
  152.     (goto-line (1+ y))- C, E3 G7 e8 A" r$ }. d) [0 {
  153.     (beginning-of-line)
    1 x& j3 |% B$ x
  154.     (forward-char x)
    0 A8 O- a8 V* f- o. U% R9 w
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))9 G$ \9 l1 v. G- m
  156.                 *wechat-5x5-black-chess*4 y, ^5 n8 h* G+ C# O
  157.               *wechat-5x5-white-chess*))1 Z0 j2 P4 u8 A! o2 b5 o
  158.     (delete-char 1)))
    8 K8 [5 ~: y" M6 X1 r3 [( L

  159. 3 ?. a0 ^, i; T# s6 n6 x* Z
  160. (defun board-put (y x)# C0 w+ ^. }( H
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))& E3 F) p, j+ y# A( ~- K/ z
  162.     (board-toggle (+ y (first dir))
    5 ^& D! X! G4 G5 i5 e
  163.                   (+ x (second dir)))))0 E# H" A7 B: K8 y( |/ A, ~- T, A
  164. / _* H% K( V2 n9 U6 b1 E8 ]
  165. (defun game-over-p ()
    " {% }5 \7 j% I4 A5 o0 G9 K
  166.   (beginning-of-buffer)3 N+ P1 p* ]# P3 q6 s+ _: e
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))+ @; `$ c6 ]; f- y8 y
  168. - d- _6 |- T( I2 S  v
  169. (defun board-show ()5 ~# M, S$ k. w7 X% s7 F6 X
  170.   (with-board; C' \6 e' k; r7 b% ]) G
  171.    (concat (buffer-string)
    5 F) V. S8 [" L9 T8 r, V4 T
  172.            (if (game-over-p)8 H+ d! V7 l0 _. I; e5 j& M
  173.               (format "共%d步,输入任意内容返回大厅" (session "step")), m" E9 J  d$ \- ?
  174.             (format "第%d步" (1+ (session "step")))))))
    9 G7 d/ l/ k# S) ]( x! Y
  175. ) s% O" w: L9 e9 B; r2 k% @" K
  176. (defun board-position-parse (cmd)2 }& Y$ D6 y4 ~; D
  177.   (if (= (length cmd) 2), F  \3 N! x0 O/ p
  178.       (list (string-to-int (substring cmd 0 1))  w" e5 [* s8 B, T% a
  179.             (string-to-int (substring cmd 1 2)))' a" \% E( i* y" f2 k' r& P/ g
  180.     '(0 0)))
    7 H( L! m  w( R8 K! G- q
  181. ' L* ^& M+ t/ t9 T5 x' H5 @# }
  182. ;;; 游戏房间
    ; l1 u( R4 T! w/ H2 x
  183. (defun game-room-init (cmd)
    ' S7 B0 X) Z5 T' h, O# s4 p
  184.   (let* ((middle (string-to-int cmd))5 S/ i, }( q8 o+ e- U
  185.          (size (1- (* 2 middle))))) C9 W& V* U; i5 h! D1 j
  186.     (with-board+ u  J, F0 B: O) X
  187.      (board-init size)- e- ?5 U# N0 }2 C4 B
  188.      (board-put middle middle)))6 Z# H' h5 t) f/ Y% b0 M/ O
  189.   'game-room)# x. ~6 k3 n- a) K% ~2 F7 r
  190. 6 i, x$ F4 b5 y- ~! J
  191. (def-room game-room. K2 ^8 U& d# H$ a4 {
  192.   #'board-show. g) O6 n% D9 \; c3 ]
  193.   (t (lambda (cmd)
    # J* K/ Y+ a) I! N& ^
  194.          (with-board
    & t( [* h, f  O5 Q
  195.           (if (game-over-p)
    : X, o' p# C7 M1 l# e. I/ t9 s
  196.               'living-room0 z. f- W- x' U( w
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    , \  M9 L, Z2 T: C
  198.               (when (board-contains-p y x)& g  [7 c) l% F) ^: I9 q! k% d
  199.                 (board-toggle y x)
    ( k5 @" d1 O; \! Y# Q
  200.                 (session "step" (1+ (session "step"))))0 a' d1 B: U9 p. C1 J1 P
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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