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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
& d5 k2 a( [* S0 u; X  G4 q借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. # U5 y6 a2 n) e* Z" }
  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;">;; 定义新的游戏地图' b! |) g) ~1 J! |
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL4 `7 p* e" D3 M0 f+ h
  4.   'tutorial-room-0)                     ; 默认的入口
    ' W/ @2 F- P4 g2 V( }( b

  5. 9 f( z1 H; O: M; U) B8 W
  6. ;;; 游戏大厅
    # Y' u; `* K; q* z9 s( Y
  7. (def-room living-room
    / C7 B: Y$ x" j0 ]# A
  8.   ;; 进入该房间后的提示语
    * ^" R+ j$ N+ I$ H! v  v& l
  9.   "1. 教程
      Z! j5 g, R2 d
  10. 2. 入门(3x3)
    ! h: W! M9 M% P" ~# ?+ f: J
  11. 3. 初级(5x5)
    ) h$ R$ Q6 w9 q# O: i  n* }9 n
  12. 4. 中级(7x7)
    4 J! l: K$ r$ q8 D& ~( i
  13. 5. 高级(9x9)
    6 e% E- _# ?- S# ?
  14. 0. 关于作者
    # D% D: \1 z4 Z" v" s
  15. 请选择1-5开始新游戏:"
    ; R- V8 Y; Z  b0 u$ M* V) c
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名7 a/ B1 l% l+ c" U! X
  17.   ("1" tutorial-room-0)  r8 f1 r  L. H( Y8 O
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    + P" f; U+ p: {7 {# q
  19.                                         ; 相应的返回也可以为函数,动态返回房间名% W: h3 f$ W! f: ], y3 T9 j
  20.   (t living-room))                      ; 如果条件为t,为永真1 Q/ z0 U) s) m: ?  l. {: ]

  21. " u9 h5 u( Z: G0 _0 _1 n9 ~+ N7 h
  22. ;;; 作者信息" Q7 f( L+ A5 O# Z
  23. (def-room about-room8 r3 [0 F) J4 h7 b: w
  24.   "作者:redraiment
    $ y0 t& j! T" G, P$ N. }! N* G# t$ E
  25. 微博:http://weibo.com/redraiment
    & _2 j, T' A  x$ ?  o8 `
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    6 W  D  R/ }" |1 ?
  27. 请输入任意数字返回游戏大厅。"
    ; W5 o: D4 T: F/ I* m! i, C
  28.   (t living-room))/ i# k* D  ], Z- V4 P$ R/ I) X. `) ^
  29. 8 O4 @! w. c4 E
  30. ;;; 教程6 e( F' M, k; x$ }% L0 J9 e
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    # d- U' m- }- q' d
  32.   "The number of tutorial rooms")
    - U# K5 y1 a% i

  33. . Q; U% F1 @3 Q( n) ^
  34. ;;; 简化教程的定义- |/ A3 u* ?5 @% P: f
  35. (defun string-last-line (content)
    4 t8 A9 g: F% y" b3 u- x6 z
  36.   "多行内容组成的字符串中的最后一行": K+ o, T) R' G
  37.   (with-temp-buffer1 @- t) m$ S1 r  M
  38.     (insert content)
    ' N0 }% |$ s1 J7 s
  39.     (buffer-substring (line-beginning-position)$ p7 D1 e# w1 B$ a; m$ W
  40.                       (point-max))))  W7 B2 h7 p" X' p) Z) a
  41. % s9 E3 u" s5 L" w* [
  42. (defun def-tutorial-room (prompt)4 L, m& f' |/ ]; E4 t: y8 R* W2 {8 J
  43.   "根据提示语自动生成教程房间。5 k- b9 N4 m" q6 t- K: ^
  44. ! S* n+ M' a  p+ P7 Q& X
  45. 1. 提取最后一行作为问题;- |( X8 e* V  U( ?1 M8 T
  46. 2. 分析问题,获取期望用户输入的内容;  K/ {$ t5 t# w7 U9 Z( _
  47. 3. 定义教程房间和重复提问房间。"7 j# C6 c  V- ~" a& ?4 ?9 Z! p
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    / W9 L  V- s$ f% u( ?
  49.          (repeat-room (concat room-name "-repeat"))/ y- V7 \3 ~5 k4 i
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))0 s' |* l: P! F  a
  51.          (question (string-last-line prompt))
    ) q( g5 @# o8 E0 o# ]
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)& H) i- g% A  n- |
  53.                      (match-string 1 question)))
    1 q9 s/ G+ |5 o4 s. g0 m
  54.          (doors (if except- T& c' d% n8 G: p( I
  55.                     `((,except ,(intern next-room))
    ( l8 I2 i: n/ [* k
  56.                       ("q" living-room)
    ! q# R  w4 U5 F
  57.                       ("Q" living-room)
    , t9 @* \+ ^; d, x( E/ W7 h0 N
  58.                       (t ,(intern repeat-room)))
    * r. d: s8 O" T1 L
  59.                   '((t living-room)))))
    ! |+ \$ ]' j  T$ T' {
  60.     (def-room-raw (intern room-name) prompt doors)( x5 m0 P; [7 R/ X
  61.     (def-room-raw (intern repeat-room) question doors)))" t0 @6 Q: z& j5 A! s7 k. ~

  62. 4 t+ l6 p8 u4 A) C) ^1 h
  63. (defun def-tutorial (&rest prompts)
      X' k# f! @; s; G+ F) X
  64.   "批量生成教程房间。"
    4 _" }7 t7 I/ w/ G
  65.   (dolist (prompt prompts)) g$ s# m& V, d9 Z4 i5 J  r
  66.     (def-tutorial-room prompt)))
      y; {$ G" P, n  y; ~

  67. & }' p4 i( L: J) b
  68. (def-tutorial; S& {+ \4 _# K; u# e6 S& R
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。$ m6 n! j2 q9 w/ q/ ], M* ?
  70. 1. 教程4 R8 ~9 X# V6 Q) e
  71. 2. 入门(3x3)
    , I4 _$ x8 B6 u. N, [
  72. 3. 初级(5x5)
    0 P3 t& i+ x2 p& p+ _9 p
  73. 4. 中级(7x7)
    ; D. ^, {: w4 K8 m
  74. 5. 高级(9x9)0 z9 ~) _2 |2 }' B
  75. 0. 关于作者
    ' ~/ E9 D6 @3 Q& s5 n  i
  76. 请选择1-5开始新游戏:/ Z5 \) N: _! A9 x/ H% {
  77. 您现在正在游戏大厅里。
    , J7 B$ d' ^2 J
  78. 请输入“2”进入入门级房间"' G. r' X$ r. `( n
  79.   "  ①②③  K8 H  N  a& w& L/ \7 p
  80. 1 ■ 2 N) r: ^( }4 l# u- w6 x
  81. 2■■■
    ( ?+ g* X# |3 t5 V, X
  82. 3 ■ 
    / X8 P' m# g8 I3 ?2 Q2 |
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    , o- `) F& a; f$ m. g7 J
  84. 请输入“22”来关闭第2行第2列的窗户。"7 J5 \- ~: X  A  m: E
  85.   "  ①②③
    ) s0 }  l! l1 O0 \: r4 i, t
  86. 1   # M9 J9 h* V  W: g, m  i
  87. 2   
    , a# m1 J% k, s1 M
  88. 3   3 h- l% [! x7 L: s: i9 v
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    + x6 K" j3 v5 Z6 c- C/ u6 [6 H; P
  90. 请输入“11”,试着开启左上角的窗户。". E: K2 F! k( d
  91.   "  ①②③% S6 m2 C. X8 `6 b9 ^
  92. 1■■ 6 i) b/ z; p+ l$ T5 M
  93. 2■  ) }: _7 `) F6 q2 c0 e/ c/ n/ [  L
  94. 3   
    7 }0 W! b. n( `8 y0 \
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    & V, ]; }7 A1 I; s" J: U
  96. 请输入“13”开启右上角的窗户。"0 Z& {, L  r5 V
  97.   "  ①②③  B' I0 r# j" S7 C9 X
  98. 1■ ■
    , w- V4 ~& C' V5 R$ e- P
  99. 2■ ■* A2 `) j0 e! t- R% w& _
  100. 3   
    2 W. D, {( O1 z$ w" l5 D) ^8 }
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。, Q) v, \+ F% H4 v* D
  102. 请输入“31”开启左下角的窗户。"- f9 k+ i9 A2 h0 P0 s6 h  `1 P
  103.   "  ①②③
    # q5 K) [) o! j! @& g( A
  104. 1■ ■
    0 N$ n3 o4 N2 y) r
  105. 2  ■" |) {. q5 x! C8 h( M# B' P
  106. 3■■ 
    $ }* e, k3 N" c8 k
  107. 此时,总共有5扇窗户被开启了。( q: G- z( {3 G4 P
  108. 请输入“33”开启右下角的窗户。"
    ! T" f/ u, }' u8 L3 l# B/ o3 O
  109.   "  ①②③. x# r1 K. l8 Y
  110. 1■ ■7 W" [' K  x0 j5 r7 ^- s8 B8 h
  111. 2   + W* r1 R$ h' r& e6 \
  112. 3■ ■# Q3 e: c! u+ u) w! S/ c% y  b
  113. 现在,只有四个角落的窗户被打开。
    $ m) x% e( Y$ C/ K
  114. 请输入“22”完成最后一击!"9 {/ L; l2 t% r8 }( i4 d
  115.   "  ①②③* d& v* U; o+ G9 r6 w' J$ }5 V
  116. 1■■■  [! n; ?8 a+ T1 [$ n& ~
  117. 2■■■
    8 `3 R* r/ X" J! ^
  118. 3■■■9 S8 n) ?. O, i" P
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!"): {; Y2 f6 x0 p8 R' e
  120. 7 l2 ~) M1 ~0 g5 O- S. [, G% d
  121. ;;; 棋盘
    / ^! S" C$ k- |! E: B: {& \* G
  122. (defconst *wechat-5x5-white-chess* 12288
    3 H; l" W) ^! i8 p! t" ?
  123.   " ")
    9 }  N: V; e( D; Z4 ?/ x$ Q9 c
  124. + ^, r* a: N' ?" a" V7 ^$ c
  125. (defconst *wechat-5x5-black-chess* 9632; ]' H: O4 U! ^( j# e4 g
  126.   "■")/ x) I( v! ?3 ~5 x$ M

  127. 0 a. |9 k: q8 B7 E2 X, X8 a
  128. (defmacro with-board (&rest body)
    $ g4 @( z+ O2 V  j
  129.   `(with-temp-buffer
      S* n, N# Y2 O% F1 i
  130.      (unwind-protect
    2 i$ @6 H' q& k7 S# h- M; u+ J
  131.          (progn
    6 t. r$ p2 u4 K& H( x8 p
  132.            (if (session "board")
    * g& W* F( j, R( E
  133.                (insert (session "board")))3 k$ }5 O9 N- n; E. x, j5 i
  134.            ,@body)
    ( y4 {' A4 t+ f8 u0 _3 I* w
  135.        (session "board" (buffer-string)))))! \4 w" y7 L9 R2 s4 H
  136. ' P# Z" D8 J2 Q
  137. (defun board-init (size); M/ }8 g4 |( w0 f% {
  138.   (session "size" size)  A% ]6 J+ S: _  |6 {
  139.   (session "step" 0)- u0 K4 V" _% k3 b. v5 g; h" j
  140.   (erase-buffer)
    + u: {, j5 K- E& B
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))9 u+ Y5 F$ c. G5 @! ]! Q2 h
  142.   (dotimes (row size)
    4 X$ `. |3 e$ s, w/ i4 l9 ~
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))8 c5 w' |$ n' i7 k5 G- ]0 M9 ]0 O

  144. ' @# ^/ J3 L. j+ j& ]' r2 H
  145. (defun board-contains-p (y x)- ]; a. Z4 R; |" D, R
  146.   (let ((size (session "size")))
    1 Y% F, t0 G9 Y: k- }; r$ E; z1 g
  147.     (and (<= 1 y) (<= y size)! P2 P+ D+ w, Q2 G# e6 G$ l8 c7 ^
  148.          (<= 1 x) (<= x size))))
    + y9 A* _- s+ Z5 C1 q
  149. : p' J5 m; L# P. t2 z& c# U
  150. (defun board-toggle (y x)
    : H+ g& y# z/ N! p( X6 Z5 k1 N
  151.   (when (board-contains-p y x): W2 J. _& Q" Q% \
  152.     (goto-line (1+ y))# M4 z  l* D8 n9 P1 o4 n- j
  153.     (beginning-of-line)
    6 A' t; w: Q* d: i3 V$ R
  154.     (forward-char x)! w" K8 d$ {! ]$ ^# N% c$ [
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))- I8 C5 X+ `7 _! R4 K, F  L- ?3 [$ R
  156.                 *wechat-5x5-black-chess*& F8 B& f; h7 B' c. O
  157.               *wechat-5x5-white-chess*))
    & e2 V1 S  I# l" O, }( w
  158.     (delete-char 1)))
    & U9 [1 N! w* a- x6 U
  159. : e+ X5 w# s6 H; C4 Q- F
  160. (defun board-put (y x)! @4 Y  a- p- \/ `! [' E  @$ a
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    7 @4 P0 B7 a1 X+ q, _
  162.     (board-toggle (+ y (first dir))
    ! v* g7 t' ^" X8 ~6 q
  163.                   (+ x (second dir))))), ~& s/ q/ o, u2 ~( b) s1 }+ A

  164. 8 n6 Z' b. A6 \. I1 p
  165. (defun game-over-p ()1 P- j) I- t$ Z. \0 F  L# ^; \
  166.   (beginning-of-buffer)8 _. W& n& Z# g( `
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))' d% T" W5 C; p( b5 q9 `' b
  168. 4 d/ q. g% m+ Q* @
  169. (defun board-show ()% Y$ m1 D4 ^# T
  170.   (with-board8 l7 @4 C% {1 n8 z0 _: H2 q6 \
  171.    (concat (buffer-string)8 ?: [( ?: H# T2 D6 @
  172.            (if (game-over-p)* r+ u- m) B, }3 C" P* a
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
      _; c/ f' `) Y0 x" M  F2 w# D( c
  174.             (format "第%d步" (1+ (session "step")))))))  \9 e) R6 s. P
  175. " z% ~1 K/ w  n9 A
  176. (defun board-position-parse (cmd)
    , y4 l2 o7 q( Z" S* V
  177.   (if (= (length cmd) 2)3 \  S4 Q# }8 A0 h9 J
  178.       (list (string-to-int (substring cmd 0 1))2 b0 D9 e3 E5 E" j/ P/ A- A# r
  179.             (string-to-int (substring cmd 1 2)))
    + L  G9 Z9 d4 h3 v, l' M, n
  180.     '(0 0)))2 s) W* B, T7 W
  181. 6 a/ w" _% z! U1 C$ {
  182. ;;; 游戏房间
    ' D* o% T5 o  h0 ]- J, A
  183. (defun game-room-init (cmd)) N5 D8 ]  L" }) s& ~: T
  184.   (let* ((middle (string-to-int cmd))) T* \" ~9 O# O
  185.          (size (1- (* 2 middle))))8 {3 s, [$ g2 k/ j5 ^. M' F2 A
  186.     (with-board
    . R$ z- `( [" {5 A, C
  187.      (board-init size)
    3 a! o4 j6 y, l. |4 h8 z
  188.      (board-put middle middle)))7 [3 T7 E6 c1 O0 D
  189.   'game-room)7 T! s8 o: U3 D" G8 n
  190. 0 w% N0 I- B, J# T' ?$ i
  191. (def-room game-room
    - }, ?& `- a% Y- F9 l
  192.   #'board-show# R* i* V2 I7 b8 S' X+ Z
  193.   (t (lambda (cmd)9 }: I+ {% Y7 P
  194.          (with-board; o! ~  {' @4 D; V! e# `
  195.           (if (game-over-p), u" e4 \; h% f2 N+ h2 G. e
  196.               'living-room
    ; S, {0 M6 I! f, e5 ?9 ^3 h
  197.             (destructuring-bind (y x) (board-position-parse cmd)3 z* c# l2 {5 e5 [$ N) Z( Z9 _
  198.               (when (board-contains-p y x)
    , P/ I& Z$ }8 g
  199.                 (board-toggle y x)6 y' @; Y0 c& l% x4 L5 _$ t
  200.                 (session "step" (1+ (session "step"))))
    / x' X, ]8 y: N1 D
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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