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

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

  1. $ K0 T7 @4 r! G- W1 q, \* J
  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;">;; 定义新的游戏地图
    2 H, ]- ^7 {, I
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL  e- o, x# l: f1 h% X
  4.   'tutorial-room-0)                     ; 默认的入口5 }) \" M: l: N) `1 g) N! q
  5.   |3 L# c* D1 n! K
  6. ;;; 游戏大厅2 N, o* q) L6 X8 N
  7. (def-room living-room
    ; h5 @, L) y. l$ ~) W
  8.   ;; 进入该房间后的提示语
    + a  Z1 T( M# T" x: r, ~0 R# k
  9.   "1. 教程
    ! J; E5 H: j) {- y9 J$ l
  10. 2. 入门(3x3), E$ Q9 L. A; \, _! \* N6 T
  11. 3. 初级(5x5)4 Y  _: R$ M# c  B  B$ g
  12. 4. 中级(7x7)& M; ]& g9 r  n9 V1 a
  13. 5. 高级(9x9)- t  q- W4 q, ?) v( e& x1 S0 Y
  14. 0. 关于作者
    " C* K  [. E) B2 ?
  15. 请选择1-5开始新游戏:"& ]/ r0 H  x/ W! Y8 m
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名1 j7 w- t: V! g; m4 Q8 C( X6 D
  17.   ("1" tutorial-room-0)- H" M; u7 i( |. R
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    1 M/ V- z- _! ]' @( G3 b$ Q
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    7 c$ o9 k. V3 z1 W0 R' ~
  20.   (t living-room))                      ; 如果条件为t,为永真' Q0 M" \+ c: p! G4 l. l

  21. 6 [7 y' ~, V+ {
  22. ;;; 作者信息
    2 h+ p6 A- B8 Z
  23. (def-room about-room$ `$ i- B8 c, d. K, g
  24.   "作者:redraiment
    ' h& ~* a: I' A) m- U2 A6 j
  25. 微博:http://weibo.com/redraiment* O+ h  l: K& ?: i4 m+ q6 E+ U. N
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    / p# U$ `4 O" K3 i$ C
  27. 请输入任意数字返回游戏大厅。"/ k) X" t# E6 u
  28.   (t living-room))
    * G- h5 d- Q5 G" R
  29. 3 v" R0 Y( }+ I+ W- J
  30. ;;; 教程
    + P, B- Z" _4 {8 r0 C
  31. (defvar *wechat-5x5-tutorial-rooms* 09 S! {; g+ }$ C* T+ ^+ u
  32.   "The number of tutorial rooms")# z- Q$ \! Q& G
  33. # p1 {% M$ g* L9 r$ z8 l" j; h" e9 J
  34. ;;; 简化教程的定义
    # q2 b# |* F" `& ?+ h3 U( ^
  35. (defun string-last-line (content)
    & ~# q/ g5 A5 H- y3 V, C+ P* `0 y4 a
  36.   "多行内容组成的字符串中的最后一行"
    5 L8 `( M  u5 F. k4 |7 _
  37.   (with-temp-buffer
      E2 [& k  s  Q7 s8 y( y
  38.     (insert content)
      G+ r0 G1 ]$ D3 l2 `' i, o4 w" c
  39.     (buffer-substring (line-beginning-position)& t. ^* ^+ t7 e. L6 ]4 T6 g5 w
  40.                       (point-max))))- U3 F  T* b1 s8 B' ?) R2 ]  p

  41. / ^" D$ @. A9 I) v" w6 y- y- x
  42. (defun def-tutorial-room (prompt)# n; [3 Y5 `8 X. y& o, O+ c, `
  43.   "根据提示语自动生成教程房间。
    # y5 b6 |) a! U! _; l9 H& a: G

  44. + X" O& U7 G4 T  N
  45. 1. 提取最后一行作为问题;6 S# L: c6 b2 ^6 f7 D+ k7 t
  46. 2. 分析问题,获取期望用户输入的内容;
    9 s. u3 m3 y: j5 ?1 {1 N
  47. 3. 定义教程房间和重复提问房间。"
    1 p, A6 I* a9 q  T( o
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    1 a+ u% c, ^# Q9 @! b
  49.          (repeat-room (concat room-name "-repeat"))
    ' n& U3 u: }5 Q4 L/ X5 M( O
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))3 X1 C2 w9 Z5 J4 j
  51.          (question (string-last-line prompt))
    ) `9 a' p# z* }/ M/ r; s7 ]
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    * r( q. ^; ?1 z7 M& F. R
  53.                      (match-string 1 question)))) S- `* ~% M. e
  54.          (doors (if except7 U1 E- A' I  G: N1 Z- @3 L$ N
  55.                     `((,except ,(intern next-room))
    9 k: Y! H: t) {# S
  56.                       ("q" living-room)
    % r6 {/ `8 r4 z: H! }5 h; P
  57.                       ("Q" living-room)
    # P9 i8 P, h# w1 f& Q# h
  58.                       (t ,(intern repeat-room)))& g, K. K7 {. b  u
  59.                   '((t living-room)))))* T8 b+ ?# [' l4 v
  60.     (def-room-raw (intern room-name) prompt doors)
    $ E& t6 y( E( c; l/ K
  61.     (def-room-raw (intern repeat-room) question doors)))
    ; S6 A. `* D- o# W) j/ z
  62. 8 Y- T0 L+ O  p: B6 N* _7 O
  63. (defun def-tutorial (&rest prompts)
    3 Z* ~7 r6 S" F8 O* |8 `
  64.   "批量生成教程房间。": W; M& w! ~5 H: L: d  s
  65.   (dolist (prompt prompts)
    3 ?/ L/ H' K8 T1 s3 [
  66.     (def-tutorial-room prompt)))
      F0 l4 t9 t8 L+ z5 P# x

  67. + l; \# @% p9 `- }5 `
  68. (def-tutorial
    $ S4 C5 u2 z: {# \# i$ a, ~4 z( r
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。% S2 m8 \* @0 A6 T, O2 p3 b$ g& `
  70. 1. 教程  z! d3 a# W+ s2 O
  71. 2. 入门(3x3)
      t8 J: ^7 p/ P4 Q& ~! U7 m
  72. 3. 初级(5x5)
    . ]# y0 ~) ^! y4 }0 G
  73. 4. 中级(7x7)
    2 j! O6 t9 U% P! ~7 m
  74. 5. 高级(9x9)
    # ~2 R5 i4 ^7 n) y
  75. 0. 关于作者, ]* t' @! z# I9 @
  76. 请选择1-5开始新游戏:
    - }% t( Y% g' n& v
  77. 您现在正在游戏大厅里。8 U2 D. ~+ F# n8 I& R; @5 ?$ e
  78. 请输入“2”进入入门级房间"& o' S! P6 i* ]9 W* v- V( M4 [6 z
  79.   "  ①②③
    9 ~5 }- \: \% m3 m4 s: X
  80. 1 ■ % d2 f& Z* u4 A; Q# ]: L0 n2 y
  81. 2■■■1 ?2 u' q/ Y, I: W( t6 o3 g: r6 W
  82. 3 ■ 
    0 z4 ^5 O1 _9 `7 m! s
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    7 _5 A4 A0 _. `
  84. 请输入“22”来关闭第2行第2列的窗户。"
    : c4 @( Z% D/ |; m$ m& ^) f  ?
  85.   "  ①②③
    5 O. C* f: Q3 o) x/ a! F
  86. 1   
    7 s3 o5 M/ K  `5 m4 l. t. t
  87. 2   
    5 \1 M" o) S$ m$ S
  88. 3   8 n& e4 i4 |  p8 }3 y2 X$ U( T
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。/ H6 l' y9 D- P0 u  l% o
  90. 请输入“11”,试着开启左上角的窗户。"
    % g8 F6 J) R- `5 c' F* B# q
  91.   "  ①②③4 f2 W; w: y$ H9 S& t' E: G
  92. 1■■ + I# K# [4 |3 \. r& h
  93. 2■  
    ' j0 g' {. d& e! b( a9 V- a
  94. 3   
    + L" w; @7 C# `- I1 k. {
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    3 p; m* \$ B! |1 P0 i/ r1 B
  96. 请输入“13”开启右上角的窗户。"
    , n4 o0 `. b  S# a6 l6 ~
  97.   "  ①②③% V# F8 r3 L6 w' o  A4 D, y
  98. 1■ ■8 K0 G  X# f0 O. M% G  T4 R8 S
  99. 2■ ■; `% r6 {, W+ g1 [, t# X
  100. 3   
    $ ^, i7 {' k0 j9 K8 a
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    6 g. z% Y$ K- O2 j; N
  102. 请输入“31”开启左下角的窗户。"' q5 a: O) a" w  E! ]! w* i
  103.   "  ①②③
    - F: n2 ^% [# R* \
  104. 1■ ■
    * _% {# V2 j" ~# i) Y' F
  105. 2  ■
    8 i, {# G: K+ T- q
  106. 3■■ 
    8 t* G& L* b* X1 {! I6 ^. E
  107. 此时,总共有5扇窗户被开启了。
      k1 T5 Z, ]) m+ b$ Q
  108. 请输入“33”开启右下角的窗户。": s8 N6 t9 N& _( g8 K" z: e5 {
  109.   "  ①②③
    * u6 E9 Y- F) E+ O; x+ D) {: q7 o
  110. 1■ ■
    0 Y! o  v# M" q+ i( [/ ^
  111. 2   
    * e7 W: y8 \# F* f3 C
  112. 3■ ■
      }, P) W& S9 C! m" ^# ]! k
  113. 现在,只有四个角落的窗户被打开。
    6 \( T6 b0 h9 B9 O( f
  114. 请输入“22”完成最后一击!"1 B% ~; \$ Q. i$ ]4 U
  115.   "  ①②③" M% s& v7 F2 x/ s
  116. 1■■■, _& v2 D6 V# C$ M' I
  117. 2■■■7 E& y7 G8 F, w2 z: Z) L" z
  118. 3■■■
    1 Q) W" \8 t3 U! x- f) l. B
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    - F* @6 U5 y8 r2 a
  120. " x' Z( K) l) l+ \
  121. ;;; 棋盘" K$ C! q9 K5 h, l
  122. (defconst *wechat-5x5-white-chess* 12288
    8 e4 I7 ^+ s2 l
  123.   " "). ?" j1 O- x  K- n
  124. 3 d1 \1 |6 C) k' T- |; O: ~
  125. (defconst *wechat-5x5-black-chess* 9632
    + E, Y* S" s# v# w* y1 R2 p6 r
  126.   "■")
    ) u( g% |$ X, i! ]1 m" h0 S
  127. $ _) g8 u4 \7 K/ l
  128. (defmacro with-board (&rest body)& R; A! s% I. c8 A$ @1 J
  129.   `(with-temp-buffer
    0 a8 _7 l6 K( t9 a2 t* r+ s% E
  130.      (unwind-protect' ^7 a$ q! x: b( }/ ]
  131.          (progn! @9 M6 t# B% @7 H0 P
  132.            (if (session "board")
    $ c2 [: o' U8 ]% y1 M: F
  133.                (insert (session "board")))
    6 o, m) r$ {0 H8 W
  134.            ,@body)
    3 o4 @4 Q* ^1 N& e
  135.        (session "board" (buffer-string)))))
    2 X: R' v( ?. s8 X2 Z: O

  136.   `! h7 T* y* Y& @+ y0 ]
  137. (defun board-init (size)& m% `) R/ m7 h( d. ~
  138.   (session "size" size)- z3 K  [1 j$ G# D
  139.   (session "step" 0)$ C" G6 m7 y, j  m3 c. S0 H- s
  140.   (erase-buffer)
    ' M4 |& b9 Q1 L
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))" k2 M, H3 T1 N: _* `1 J
  142.   (dotimes (row size)
    ' Z5 C3 `4 D* u$ s& R) H
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    % F0 W! D/ V$ d% r& ?
  144. + a; ~, v1 ?1 O) L& M
  145. (defun board-contains-p (y x)
      l) ~" G) g/ s; e) p  [5 J7 E$ U$ `
  146.   (let ((size (session "size")))! Q" Z$ @. L4 T0 n% A& ?
  147.     (and (<= 1 y) (<= y size)
    8 D& R6 k. [/ ?5 ?) E0 E
  148.          (<= 1 x) (<= x size))))
    , \4 m" J3 I! T

  149. 9 h3 [" X$ y% F9 ^( s$ p
  150. (defun board-toggle (y x)
    , ~- V  q" `( m8 c, ^
  151.   (when (board-contains-p y x)" H6 }5 g, ]8 i
  152.     (goto-line (1+ y))/ b1 @+ Q3 c8 ?0 w) _
  153.     (beginning-of-line)
    1 P& ~# F/ g6 M$ C8 ?
  154.     (forward-char x)
    ( E& u* i+ [" e. m4 O
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))* f1 i, n2 j5 D  [2 C
  156.                 *wechat-5x5-black-chess*
    ( E" X  M. Q. U4 v9 @
  157.               *wechat-5x5-white-chess*))
    & k' e2 S5 V1 w* J
  158.     (delete-char 1)))5 \7 z5 y7 u9 f1 Q0 {

  159. ; s3 {' u; J/ G. l2 D  Q
  160. (defun board-put (y x)* d6 A) h5 R$ ?" Q
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))# U+ n3 x0 e2 E
  162.     (board-toggle (+ y (first dir))
    0 `' \% C; {2 J6 B' s) @/ h
  163.                   (+ x (second dir)))))* \- r% B1 L; b
  164. $ O* `  a. F7 R. K4 S8 ]' e) c" T
  165. (defun game-over-p ()5 w/ ]5 h; g/ z' T. A4 A+ ?* s  S  R
  166.   (beginning-of-buffer)
      m* o: a" N' D: B
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    : o. w7 x9 n# `% P, h( \
  168. 7 I( S; j2 z# v. Y5 t9 j
  169. (defun board-show ()' Q8 i6 X9 e3 {7 F
  170.   (with-board
    ! x1 O# r0 s* u
  171.    (concat (buffer-string)2 r* Y2 @7 ]4 B' A2 O0 q
  172.            (if (game-over-p): p& ?! F# ^( G" ^! e7 Y. V- o4 ?' x
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    9 x' c. ~" k% m2 U1 Q7 S- \, E: o
  174.             (format "第%d步" (1+ (session "step")))))))+ x* |$ F- t( L* R7 a

  175. ( \% P" B. Q7 h, t- m: c
  176. (defun board-position-parse (cmd)& z) T7 R5 G# X- b( o1 \
  177.   (if (= (length cmd) 2)% l# @: T/ y' u1 `
  178.       (list (string-to-int (substring cmd 0 1)): w3 W/ k( P6 j& M% O! Z
  179.             (string-to-int (substring cmd 1 2)))
    1 ^( }/ m. b0 p' ~
  180.     '(0 0)))
    ) o( b0 w: S* `5 o- m9 M
  181. , X  v6 f& n& a# m6 y
  182. ;;; 游戏房间5 r4 K7 U8 p, V# B
  183. (defun game-room-init (cmd)% d" E) r* J9 d. G1 O! O
  184.   (let* ((middle (string-to-int cmd))$ ?4 A1 @) C* O: q  ^& _
  185.          (size (1- (* 2 middle))))
    . o. G6 p' m* P
  186.     (with-board2 h+ J& e: ?  u/ \6 [1 |5 N
  187.      (board-init size)
    1 M0 Z* S# M& R" k" p# h1 H6 W
  188.      (board-put middle middle)))
    7 G' _& N1 I4 X. D( w" C: [" B" V
  189.   'game-room)5 z3 x. P; E$ k. K6 R7 R9 ?( \
  190. # ^5 q0 V) y8 y$ i' H
  191. (def-room game-room& b$ L+ U2 I2 h* C2 ~
  192.   #'board-show* v. K+ e& \( ]  V9 a+ n; s
  193.   (t (lambda (cmd)
    # @' V* E: {( Q( t7 F# r; c5 Q5 x
  194.          (with-board( Q/ j2 m; L- g# d0 X2 z
  195.           (if (game-over-p)
    ; R1 k) z" _. P  _, j% s
  196.               'living-room
    $ u+ K, r! ~' k6 s
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    2 A. D! ]& G6 z: B  k5 g* {
  198.               (when (board-contains-p y x)8 X( U2 Q7 o# M" Y1 u' q. H
  199.                 (board-toggle y x)# N: ]; b  }$ N7 G' r& |
  200.                 (session "step" (1+ (session "step"))))$ K+ S+ L5 K/ J3 k; c' |5 v
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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