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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。$ ]/ r) @' \- g- h3 K
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. + I& c% G) K. t# _$ 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;">;; 定义新的游戏地图% U3 A. h* u- S
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    5 \# j: A7 p; m. g% X
  4.   'tutorial-room-0)                     ; 默认的入口
    : G3 W9 |) Q$ l/ p6 s
  5. 6 e6 Y. @) s6 z8 j0 g
  6. ;;; 游戏大厅0 v# I- ~* v8 O; A  `/ r
  7. (def-room living-room! h! ~/ W. e7 u8 p
  8.   ;; 进入该房间后的提示语( ?6 B8 W2 F3 S3 ]/ R0 t8 R8 y
  9.   "1. 教程
    3 `' K$ R, H( u# \* O8 D0 h' k& u
  10. 2. 入门(3x3)2 c/ ~# `( M$ d* \+ |: p- M
  11. 3. 初级(5x5)
    3 X4 s* X4 I; }" w* T8 O
  12. 4. 中级(7x7)
    3 b% ?3 F& @, s- q- q
  13. 5. 高级(9x9)0 y5 E  }/ g6 [+ ~( E0 I2 i& b; p* z
  14. 0. 关于作者
    # m4 ?+ w3 G8 m: N: c
  15. 请选择1-5开始新游戏:"; Q, n- j) K5 J* W: S$ o
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名# X* l' y& ]% w- S8 B/ O; x
  17.   ("1" tutorial-room-0)8 U7 \; y2 M( v2 |
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配, d5 W: O2 W: c) R5 T0 H+ U
  19.                                         ; 相应的返回也可以为函数,动态返回房间名4 _' L( q5 W/ U* ]6 e4 d7 @: N
  20.   (t living-room))                      ; 如果条件为t,为永真1 R' Z  n3 h3 H$ o# I$ i+ ~; S

  21. " F6 e, M8 A) W' A3 C. o
  22. ;;; 作者信息+ k$ p$ f; S0 k+ M% N
  23. (def-room about-room6 r* f- h# b/ _2 P- y* }$ v7 `& \+ R% m
  24.   "作者:redraiment
    1 ^  A  h+ i0 p/ i7 U, ~% c7 q
  25. 微博:http://weibo.com/redraiment- ]" s" x. M" y; q+ }
  26. 有任何建议,欢迎在微博或微信上联系redraiment。- D% N# E7 w( J; r7 H( \8 N9 G, V
  27. 请输入任意数字返回游戏大厅。"
    & S- U0 [# N  z$ \9 P2 m! l3 z
  28.   (t living-room))
    6 N) }/ c/ S& L2 Y( G0 n2 m
  29. 0 `8 @& b/ p8 I  o! Z' g
  30. ;;; 教程
    ) E1 t' ~( l* Z4 y
  31. (defvar *wechat-5x5-tutorial-rooms* 03 s4 m# l! d$ h5 ~6 m
  32.   "The number of tutorial rooms")( |- W* j0 |; z" ]

  33. & x4 ~0 U1 N+ P) R9 N! c
  34. ;;; 简化教程的定义8 b: L5 G, q) w# V1 {0 Y
  35. (defun string-last-line (content)1 c2 g, o& s0 }1 y0 V9 g# |4 x
  36.   "多行内容组成的字符串中的最后一行"  ^+ [& t; }9 a2 O
  37.   (with-temp-buffer
    , u' _& A2 F9 ?! V) k9 X. q8 W* L: o
  38.     (insert content)
    4 T: C( Q- ^, M2 E
  39.     (buffer-substring (line-beginning-position)2 I4 \( d5 t+ c8 S, H4 o
  40.                       (point-max))))
    ( o# R# S8 T1 S' Y
  41. - o5 }  f) M1 O2 G+ \1 i5 A
  42. (defun def-tutorial-room (prompt)! N# @+ B; ?9 k
  43.   "根据提示语自动生成教程房间。
    8 o9 J% N8 ?5 ~8 |$ Y  N9 P

  44. : J* k- P' t+ ]! Z! H$ c1 [9 w
  45. 1. 提取最后一行作为问题;
    # [7 D% ^2 m+ Y+ c' x& v7 ~# d/ F
  46. 2. 分析问题,获取期望用户输入的内容;2 Y" x: @: j3 \4 Z' c  |
  47. 3. 定义教程房间和重复提问房间。"7 W" m/ @$ t% r% X% b
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))  o- g. `: \- T, j; u/ @
  49.          (repeat-room (concat room-name "-repeat"))
    7 u2 J! W3 o1 i( \2 `& P7 d# f
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))9 l/ r& k( N" v1 Z6 V
  51.          (question (string-last-line prompt))
    & A; R7 [% K" v1 `
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)6 a. c) [# O$ |9 N
  53.                      (match-string 1 question)))2 E& k/ ~  W) d! k+ J
  54.          (doors (if except  r( Y- H5 D/ g% G* ~
  55.                     `((,except ,(intern next-room))
    ) k* J6 J+ B. m/ L' Q
  56.                       ("q" living-room)
    ; N4 z# w$ f) {/ u# R! R
  57.                       ("Q" living-room)
    ) f+ U% V! _1 O' x4 A) l% }2 s
  58.                       (t ,(intern repeat-room)))
    ( ?; @0 o1 k5 i% M  L
  59.                   '((t living-room)))))
    2 Z6 u7 Z( G+ z& y- s
  60.     (def-room-raw (intern room-name) prompt doors)
    ( @2 T3 E, S8 I6 E% K7 x
  61.     (def-room-raw (intern repeat-room) question doors)))
    4 f# ^) Q* G) c0 I
  62. . w' W8 U2 s( l# o1 v
  63. (defun def-tutorial (&rest prompts)
    6 M. `5 B$ ?/ ?5 l4 Z
  64.   "批量生成教程房间。". C. w2 U8 u# \: U" a. s
  65.   (dolist (prompt prompts)$ X/ i3 A5 R8 T7 l4 ~
  66.     (def-tutorial-room prompt)))
    8 b* I& O% V$ e- A) ]; x
  67. 2 v& t. Q3 q4 s" C! T
  68. (def-tutorial( R8 X$ i1 e) g5 W: S, ^8 r
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。% p/ K8 N* P6 ?& B  e
  70. 1. 教程, d+ ]* m0 E7 _, n( z3 ^& d0 F
  71. 2. 入门(3x3)
    / C7 v- J- d! t7 `7 l3 ?7 ?' S
  72. 3. 初级(5x5)% x( @2 t, L3 a% J! E3 B8 N1 b
  73. 4. 中级(7x7)
    5 B. q' w. U0 D# O
  74. 5. 高级(9x9)
      {$ M4 ]5 Q8 m  \0 \+ a
  75. 0. 关于作者; A' i# n4 \; ~1 J$ O/ u/ N  c
  76. 请选择1-5开始新游戏:! }5 c% a5 N- v$ ?' h) A
  77. 您现在正在游戏大厅里。
    + ]% t* \5 K1 V/ g
  78. 请输入“2”进入入门级房间"
    " t( _; A7 B/ C8 k# I( J/ b! X7 z
  79.   "  ①②③
    + {- U  c. A( |3 a
  80. 1 ■ 
    # g3 V: }8 f% [" j& s' n- f/ g; @
  81. 2■■■# w& h+ x. P( C9 |; N& f4 L% B! V
  82. 3 ■ 
    2 d! x/ E- j3 f4 p% x
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!1 P* i+ w% }+ d! y: L" ?
  84. 请输入“22”来关闭第2行第2列的窗户。"
    $ H$ p( Q( Z8 \1 i) J
  85.   "  ①②③
    / }' `" K% U8 K3 Z2 Y4 p* Q
  86. 1   
    . N5 p& Q' O+ V" h9 C8 y
  87. 2   
    % H$ [6 c! `7 `& T3 B2 A
  88. 3   
    6 q6 G8 O+ M& W
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。* w. y; O0 i+ K9 |2 k* ^" i
  90. 请输入“11”,试着开启左上角的窗户。"4 {- t! h! \( y
  91.   "  ①②③4 ^5 `0 g2 U* Y" Y$ j9 M
  92. 1■■ 3 o# E4 ?/ e/ q, j- I0 [: W
  93. 2■  
    6 D& R: L2 P8 s
  94. 3   
    & d$ |9 H0 C% {
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    ; c2 G4 V2 Y" y9 Z6 L- ?5 _+ u
  96. 请输入“13”开启右上角的窗户。"
    4 f* z% h) k- u' ?: t- F" U
  97.   "  ①②③7 H( B8 b3 S* G
  98. 1■ ■
    & l2 p1 ?# y+ R( E
  99. 2■ ■
    ; \* d6 p  x/ ?& G0 H: F( d3 O
  100. 3   2 L1 @4 U. A1 q% I3 r
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。( W: S+ k4 Q6 n9 s8 T+ _9 D
  102. 请输入“31”开启左下角的窗户。", S* D. H# L* c$ V* D' K
  103.   "  ①②③" x; K  \9 K* b/ Z7 l+ ?
  104. 1■ ■
    5 L3 |  L9 s% \$ W
  105. 2  ■
    % C, `5 b9 N/ w6 A$ z/ @+ @8 ^3 x
  106. 3■■ % _6 r$ O6 \7 `# g" b/ {1 j: J, U* l
  107. 此时,总共有5扇窗户被开启了。
      s( m6 {  X5 t6 p+ h
  108. 请输入“33”开启右下角的窗户。"
    + O- {. \) m7 j
  109.   "  ①②③' X3 h( E- K  B1 g
  110. 1■ ■% V7 ^2 t$ H% ~
  111. 2   
    9 N8 |7 G: M& A# ]4 X% k
  112. 3■ ■
    ' ?9 c- j! z: W- J8 \
  113. 现在,只有四个角落的窗户被打开。
    2 J: O& r' r' ~0 w2 q
  114. 请输入“22”完成最后一击!"
    ( R1 u; T+ |8 a' M: F$ f
  115.   "  ①②③: g$ H5 L1 v+ Y+ y3 p. h3 o
  116. 1■■■
      G. ?. u# j1 z) L6 w, @
  117. 2■■■
    9 i- E2 E$ U/ b7 G
  118. 3■■■# u9 J1 H& Z7 l( ?# }
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")& B. B* }* `: H! d3 ?9 e
  120. ) |! p( @# l9 |, j7 d
  121. ;;; 棋盘: Z, s1 ~3 B5 f
  122. (defconst *wechat-5x5-white-chess* 122881 M% d5 k. k1 Q- R; e
  123.   " ")
    % O+ O) ~* {: b

  124. 0 ^$ H) Y4 _3 M; s
  125. (defconst *wechat-5x5-black-chess* 96322 N5 X  Z1 q6 o% d
  126.   "■")- Z  o. i& q6 w% |" U; |: C/ l
  127. / ~' C0 L7 b$ y
  128. (defmacro with-board (&rest body)2 N; v5 y7 ?1 u* @$ {1 j/ C- ~
  129.   `(with-temp-buffer
    5 J$ |! U% T: U2 T( u& E
  130.      (unwind-protect
    : [! _+ `) ]3 `3 b2 v5 y
  131.          (progn
    + |% d2 F" g& E& ^
  132.            (if (session "board")" @- F, d9 b0 J$ Q
  133.                (insert (session "board")))
      Z* D. F7 e* B6 Q
  134.            ,@body)7 G& Z: b9 q  l
  135.        (session "board" (buffer-string)))))1 S) |* j) d" I; _3 W  p3 S' B
  136. ( M. I* ?% I$ y! Q) o% ]8 q; W( v
  137. (defun board-init (size)
    7 b  D. d4 ?0 s2 A" B+ S. l: I2 H0 g% s
  138.   (session "size" size): [1 \8 L# o+ |9 h" G7 a
  139.   (session "step" 0)
    : A5 ~! R; V( U/ C
  140.   (erase-buffer)
    # E' F# P) ?& z  C- n- x  l
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    ; t+ x5 |' q, D; F: R. B4 J
  142.   (dotimes (row size)  m& F* ^) r* u# o' s( Q) i$ c
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))3 s$ @: E  p* T

  144.   Z2 Q8 |2 r6 T: ]# ^) ^8 T! H
  145. (defun board-contains-p (y x)
      T. t4 u4 }* o
  146.   (let ((size (session "size")))
    7 G9 w0 Y& h* l& k8 j: M8 Q1 s% P
  147.     (and (<= 1 y) (<= y size)
    - O. u! z  g% e
  148.          (<= 1 x) (<= x size))))
    7 I$ q9 p" Q9 D7 U
  149. & I) L: U' P2 B! s
  150. (defun board-toggle (y x)+ k  A1 w$ v# R9 Q2 R) T9 M' w
  151.   (when (board-contains-p y x)
    3 g' q# {% Z  C, ]8 w
  152.     (goto-line (1+ y))/ u! m9 C: y* e5 A' M2 j, H
  153.     (beginning-of-line)3 t! n& c4 j/ z, r2 k7 C
  154.     (forward-char x). W! Z5 n" U$ u' l* I1 m# `
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))0 D4 x3 c7 U( \/ m  S# I7 W: C
  156.                 *wechat-5x5-black-chess*3 v  l# n7 K* u: p9 s/ I- c; V
  157.               *wechat-5x5-white-chess*))7 f) E% O1 J4 ~4 P1 D0 i
  158.     (delete-char 1)))
    $ Q. C- c5 i# ^2 R* s) I+ s

  159. 5 H6 f2 \: ^6 Y8 N( `9 p  B& @- n
  160. (defun board-put (y x)
    0 u* A0 |6 l* M+ W6 g' c& ?  D/ g
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    1 {+ ~6 F- M; i
  162.     (board-toggle (+ y (first dir))
    9 e2 q0 Q8 T$ x+ x2 C8 `
  163.                   (+ x (second dir)))))' ]5 ?0 T0 A& w5 m) ^% T! H, P
  164. " U5 L- Q" P- h% @
  165. (defun game-over-p ()
    9 Q! I5 J, x$ C! n2 [
  166.   (beginning-of-buffer)
    1 j$ v6 u& w1 }' m) q
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    7 G; N' h# {2 z
  168.   M, ~* j* c* v
  169. (defun board-show ()& f+ ]2 N( G, N. r
  170.   (with-board
    ' t' S" e3 K* s- m
  171.    (concat (buffer-string)
    ! P/ g* q- N8 |7 @' M& G0 r; I
  172.            (if (game-over-p)
    0 Q  E+ }; A. K$ g9 [: z
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))+ e. B" _  {9 r+ ^. Q
  174.             (format "第%d步" (1+ (session "step"))))))). _1 \. c8 m+ C$ ^) k3 i
  175. 5 B6 r" i3 J$ t) ~& {4 t$ |+ j! ]
  176. (defun board-position-parse (cmd)" ?5 ?8 T' O- l) c
  177.   (if (= (length cmd) 2)
    % d: f0 r1 S: b) O" J
  178.       (list (string-to-int (substring cmd 0 1))4 j. G2 O$ d# |  r& S3 M
  179.             (string-to-int (substring cmd 1 2)))4 ?, q6 }" v! z9 }- F
  180.     '(0 0)))& H: {( q" `  N1 P5 o9 V
  181. 4 x* h- s; s% I: h: C' y' ]! R
  182. ;;; 游戏房间
    ' e1 d' x( Y8 y% E- G9 u
  183. (defun game-room-init (cmd)4 |* [0 {9 j/ o
  184.   (let* ((middle (string-to-int cmd))
    # L# r8 u. Q* M9 O$ R5 c
  185.          (size (1- (* 2 middle))))- t: z5 |+ d/ T  D! I9 ^8 a- b8 m
  186.     (with-board% w1 C& S; d( l% N- V
  187.      (board-init size)
    : n+ a! F& H. v, \
  188.      (board-put middle middle)))
    6 Y  b) Q5 N. j/ a! m% D  x6 v
  189.   'game-room)$ t! Z6 o5 u2 t9 W# A) S( W0 Y
  190. 4 U1 X8 }! Y8 S8 ^9 c7 a# c7 J
  191. (def-room game-room3 U+ j$ f, C( h
  192.   #'board-show; R- \5 Q) Z) G( i) _8 m5 ?
  193.   (t (lambda (cmd)( m$ b; U+ v  X% n% j1 Z
  194.          (with-board: n0 L4 R3 Y1 S
  195.           (if (game-over-p)
    % x/ a& P: K* Z3 J4 [3 h
  196.               'living-room) c' [0 w& |* u) F% v: |
  197.             (destructuring-bind (y x) (board-position-parse cmd)0 u3 h/ i" H( P5 m
  198.               (when (board-contains-p y x)
    % p  z: I: i" j$ o/ e
  199.                 (board-toggle y x)1 \0 f* b2 ]4 i; }; T/ i2 g/ Z
  200.                 (session "step" (1+ (session "step"))))
    3 E9 u7 y; z. |  u
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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