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

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

  1. 2 _3 \! c6 T0 _3 p
  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;">;; 定义新的游戏地图
    8 T/ V' W9 b* J3 a, g; z/ V
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    / @' s* _. W7 ~9 t
  4.   'tutorial-room-0)                     ; 默认的入口
    + _3 i9 s# H6 ^- C  m
  5. 4 G7 h: Z- f/ @5 t3 `6 p4 i( R8 Q
  6. ;;; 游戏大厅! N) A4 I: H5 ^2 C8 c7 N
  7. (def-room living-room
    ( [, t5 `6 g4 H9 L
  8.   ;; 进入该房间后的提示语  c4 E- t/ V( \8 U; t# G' `+ v, i$ s
  9.   "1. 教程
    0 C7 r  r: [# V2 C
  10. 2. 入门(3x3)% [5 G) J/ G7 e5 ?0 d) m0 Z& _9 x
  11. 3. 初级(5x5)
    9 I$ z# d- A! `* u0 t8 q0 g
  12. 4. 中级(7x7)
    1 t( a: x6 Q- T. [. n4 o
  13. 5. 高级(9x9)
    / N2 f/ o: L0 e/ H% H6 B$ q
  14. 0. 关于作者5 `' d# r- S2 k( l
  15. 请选择1-5开始新游戏:"$ d, P$ @  Y& Z6 B( K9 o# p
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    $ Y; p  R8 X1 P( ^' r
  17.   ("1" tutorial-room-0)
    . Y3 C! q: G- [* i, {" M0 P: ]
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    , C8 L6 U3 I: k* v. W: v9 n
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    ( |% I; w0 p, p$ R) P$ b6 G
  20.   (t living-room))                      ; 如果条件为t,为永真+ p, M' c3 B) a4 Z, `, h
  21. # A" @4 f* v! `+ w% j
  22. ;;; 作者信息
    , {# F; l. A. o2 t
  23. (def-room about-room, [  F2 I, b. M( a9 }
  24.   "作者:redraiment
    , d  q/ M" c! s7 U$ g( T' }
  25. 微博:http://weibo.com/redraiment
    . R) m& V. _+ t1 }7 ]$ M5 m
  26. 有任何建议,欢迎在微博或微信上联系redraiment。+ L$ f: B8 P/ y& k" H; l& `; ^
  27. 请输入任意数字返回游戏大厅。"
    + [7 |3 x2 f1 |5 z& ^+ X
  28.   (t living-room))8 K8 e1 u+ A" g$ g

  29. 2 s. E! S' l: R  V# }( ^
  30. ;;; 教程3 T$ d6 d$ M  n$ k1 P: n  j+ x
  31. (defvar *wechat-5x5-tutorial-rooms* 0  t2 n: k  Z: Y9 I9 a7 v9 g1 h
  32.   "The number of tutorial rooms")
    2 K) y* N9 {! z% z( M) \: y

  33. : W( }: T' f6 Y$ J& b+ @
  34. ;;; 简化教程的定义# g4 y3 [  M  c+ k# H) D
  35. (defun string-last-line (content)
    , q$ }0 r1 o$ H0 P# D7 Z1 `
  36.   "多行内容组成的字符串中的最后一行"
    ) q8 ~* R! v8 C2 d0 W
  37.   (with-temp-buffer
    9 `/ @- w7 @3 u/ T% Y
  38.     (insert content)
    - y1 q* K0 O+ ~1 Y. z. t: i2 B
  39.     (buffer-substring (line-beginning-position)
    7 @+ y" n( J2 I
  40.                       (point-max))))
    3 f2 {$ M6 n7 e4 R. o1 d
  41. ! n' t- g1 H, ], z  |9 q" z: a% Q4 {
  42. (defun def-tutorial-room (prompt)
    3 t/ `3 m1 L$ L) O
  43.   "根据提示语自动生成教程房间。
    $ F) h7 L* w, W) l; z, J! s5 W9 Z, F

  44. 4 g7 p3 p; M' c! |7 }0 O; m
  45. 1. 提取最后一行作为问题;
    2 }7 q+ A( }* @0 o7 e7 m" I
  46. 2. 分析问题,获取期望用户输入的内容;5 v6 A( w4 _  Q$ z7 G- x
  47. 3. 定义教程房间和重复提问房间。"% r2 [% O4 G7 p7 C
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    & |( S5 W- B' U$ v  }. t
  49.          (repeat-room (concat room-name "-repeat"))
    ( }2 n- X$ @1 ?
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    6 R: X2 F) U" W0 C* U
  51.          (question (string-last-line prompt))
    ' c5 C  i4 Z+ d: [
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    3 _  u, |# \' S+ M; s
  53.                      (match-string 1 question)))
    3 Q- k6 w& I" g. ~" S, }' }: q: Z
  54.          (doors (if except% }$ y" e+ z  n$ X) a0 t& x7 [! J) z
  55.                     `((,except ,(intern next-room))
    + [0 P; a4 k" K4 a& i
  56.                       ("q" living-room)
    . W8 j# Y; s: P
  57.                       ("Q" living-room)- r6 O. M2 e( q* ?% l' \+ L5 [  L
  58.                       (t ,(intern repeat-room)))
    . I1 Z" G3 d& Z
  59.                   '((t living-room)))))
    , }+ `; i8 g+ p/ I
  60.     (def-room-raw (intern room-name) prompt doors)
    % e$ o& P* _0 s0 I& Q1 ?4 ^
  61.     (def-room-raw (intern repeat-room) question doors)))) O0 Y  p  H- Q, X
  62. + S; @* t0 a8 t( P$ X* X; j
  63. (defun def-tutorial (&rest prompts)
    7 @  b. i* |/ Y; {
  64.   "批量生成教程房间。", m* ^& x* @5 i) R+ |1 Q' ]. K! I
  65.   (dolist (prompt prompts)( y+ E/ h- B$ M% d; S( o& ]
  66.     (def-tutorial-room prompt)))* D/ [7 W& D! V$ u: d
  67. $ B8 i) {# ~% m: ?0 ^& x* O
  68. (def-tutorial
    ! W; U# s( y3 g1 H
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。4 R; L3 _1 q/ N+ b
  70. 1. 教程
    5 f1 R* F2 D% M) m1 @
  71. 2. 入门(3x3)
      p! ?, j% @7 \
  72. 3. 初级(5x5)9 \$ {& n9 L9 }2 b- S% G
  73. 4. 中级(7x7)
    4 u, t" _- E! _! Y
  74. 5. 高级(9x9)
    ; E5 D$ U6 y0 T
  75. 0. 关于作者/ [, }  M$ H: ~5 Y% l* m
  76. 请选择1-5开始新游戏:
    ' Z7 Z* f7 m8 k6 H. `! q+ o+ s/ S, c
  77. 您现在正在游戏大厅里。
    . {5 z) y7 K9 ], i! g
  78. 请输入“2”进入入门级房间"
    7 i# P( g9 M8 a, I! {2 _, L2 u# g* u
  79.   "  ①②③
    ! y8 A! d, i5 R. u& \2 u: U
  80. 1 ■ 
    * ?9 M' c/ r5 F/ o$ P/ |
  81. 2■■■
    ( i$ `& {& _/ s
  82. 3 ■ 
    / D5 w: R  i; s2 B
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!! \; A* m. O! r& c: `; |& W( o
  84. 请输入“22”来关闭第2行第2列的窗户。"
    # ?: ]1 [! t4 M2 P8 |* y+ O. S8 ]
  85.   "  ①②③/ x3 ^; V7 F- h7 }
  86. 1   
    0 D* l& x" I% ]# s! M
  87. 2   ' a; W( ?8 q5 m+ `% @& Y( I
  88. 3   7 D4 g2 l) @( }/ H& _9 w  M
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。) x. ?# P( ^5 q# n, R
  90. 请输入“11”,试着开启左上角的窗户。") d/ d; M. Z& Z5 w
  91.   "  ①②③
    : R5 |+ F/ w) f9 r5 R
  92. 1■■ # v: d/ I$ V) A  a  t* V  B
  93. 2■  
    6 K0 D% p! J/ ], _0 c# x
  94. 3   
    # f1 O* O4 Q& W- o: T" B9 \  f; A
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。* i& x# ~( f+ }- K
  96. 请输入“13”开启右上角的窗户。"
    - E& n5 x% o! W  [
  97.   "  ①②③6 y5 M: S" L7 T9 P# k; l3 K
  98. 1■ ■
    % R; Q. `4 `. z' Y6 i6 b
  99. 2■ ■; Q' p# N& V3 }( E; Y
  100. 3   
    8 _+ I& ~, M! q# {7 s8 H5 j
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。9 [3 A+ l  u1 M. p% A7 i
  102. 请输入“31”开启左下角的窗户。"  A$ H) E" O; Y4 j/ n
  103.   "  ①②③
    ! ?% G5 k; ~, F! ]7 p8 w* c. L
  104. 1■ ■
    7 H( |; Z( }2 M
  105. 2  ■- Y- H4 U  D, Y  a
  106. 3■■ 
    : f: U( O; i( w
  107. 此时,总共有5扇窗户被开启了。
    5 l+ X2 H4 n2 |; D$ `9 O  t
  108. 请输入“33”开启右下角的窗户。"
    : \7 R2 [/ v9 P8 [. d" f
  109.   "  ①②③
    / J& z1 N" D% j5 g# D( X
  110. 1■ ■4 c* X! Z4 |* \8 _' Z3 y9 A; \0 [
  111. 2   . M: m: W) u1 j
  112. 3■ ■- Q8 J2 \/ U3 j8 q: j' I
  113. 现在,只有四个角落的窗户被打开。
    : s# Z, X5 z8 F0 e8 l3 E
  114. 请输入“22”完成最后一击!". T1 R6 W* a0 e' C6 s. F0 G* }
  115.   "  ①②③9 k+ m1 f' J* ^
  116. 1■■■
    4 L& Y6 n. R- B' w
  117. 2■■■
    / Q& k% R5 D, ^5 e4 R& k
  118. 3■■■$ Y% _5 O3 r! D
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")) Z" R6 m9 v$ q7 {

  120. , I+ u6 Q/ {  c- o# _' _
  121. ;;; 棋盘
    , z) {) j- X: w
  122. (defconst *wechat-5x5-white-chess* 12288
    ) c* b, }1 H8 U  N
  123.   " ")1 M& u! \9 T2 q# I' b" B
  124. ! c6 R" [0 j: A# D; @: j& K
  125. (defconst *wechat-5x5-black-chess* 96322 T8 b) K& i" I6 M2 e5 Z, `
  126.   "■")
    $ S; ~* F7 Q; `: n' d- f, y

  127. 0 ^3 Z& H  m3 _5 ?+ I! K" K% j& f
  128. (defmacro with-board (&rest body)
    # b/ u, x$ C% {# j. b1 g
  129.   `(with-temp-buffer' I% H* ~$ T9 R' Y- H% J
  130.      (unwind-protect
    , |( n# |& E' C- j( f
  131.          (progn
      t7 T3 p+ e% ~6 i$ T
  132.            (if (session "board")
    ' h) v+ w! l  O3 R& Y6 y
  133.                (insert (session "board")))
    # \! _: z- `6 f8 _  v/ I
  134.            ,@body)
    : q# s* ?* U0 t7 W
  135.        (session "board" (buffer-string)))))
    ! o+ r1 B% Y- H# |3 E

  136. " z: [: L3 F  J! J7 g5 ?0 t
  137. (defun board-init (size)* q! l# @5 y/ o8 G, i9 d
  138.   (session "size" size)0 |+ F3 ?& E0 [2 G; K/ E1 e* f6 J
  139.   (session "step" 0)9 |4 R: N9 g( C
  140.   (erase-buffer)
    ( q- m3 X& p6 K) z7 P  v
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))7 ]; W3 F$ e+ n7 N6 O
  142.   (dotimes (row size)
    8 n$ r. ?# C" j( K' d( A
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    1 H* L' x, I( d5 `

  144. , p4 z- W) s+ Y% e. U
  145. (defun board-contains-p (y x)
    : \, b5 l2 i1 F5 D! A* ]
  146.   (let ((size (session "size")))" m3 K( U; A, e; S+ }3 y: E' a* K3 }
  147.     (and (<= 1 y) (<= y size)2 u8 m1 X! p5 k& D+ O% S
  148.          (<= 1 x) (<= x size))))
    2 {4 }4 b( O/ r1 p
  149. 2 A3 K7 J7 e0 m7 w
  150. (defun board-toggle (y x)# g6 s  Y6 |* Y
  151.   (when (board-contains-p y x)! e# y) B6 h. H, ^7 ~
  152.     (goto-line (1+ y))9 Q  U$ a$ i! m. |/ g
  153.     (beginning-of-line)* S7 W: o, P* q' ^$ [8 w
  154.     (forward-char x)
    / `7 B+ r5 S/ \, h* p
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))% C+ t* m" c; R6 \, J: W
  156.                 *wechat-5x5-black-chess*
    - u( }0 D' D. f, [$ s) O2 `, t5 E0 }
  157.               *wechat-5x5-white-chess*))* \' n1 N! d& K4 {4 U: I2 ^
  158.     (delete-char 1))). P5 K; f* O& ^9 i$ f8 V
  159. ' H* D9 }& O9 N/ ^. X
  160. (defun board-put (y x)
    % J* g  R7 r3 t+ _% ]+ o) `
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    / \# l, ?# j; ?) o; [1 V
  162.     (board-toggle (+ y (first dir))
    + X" N% z$ X! O1 Q- g8 W, W
  163.                   (+ x (second dir))))): B/ @% s% {. [5 `1 g/ _
  164. 8 U4 @0 l" c0 A6 g& y/ P9 Y
  165. (defun game-over-p ()
    ) P: X" B$ z9 N9 f4 @6 O( R0 p
  166.   (beginning-of-buffer)* [- m9 R3 G; Z# w! B
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    2 O4 [& r. w" }  J' r9 E
  168. # P. m0 X, s' w: N  ]) z
  169. (defun board-show ()
    8 x( I2 B. p( N# X# _# q
  170.   (with-board" A' B8 L7 `2 l- h
  171.    (concat (buffer-string)
    0 z" [* l' j/ h3 e$ I# W
  172.            (if (game-over-p)
    ' v/ _6 X8 c9 a( {" g: C$ b
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    6 L- L- v' R% V! m3 d" a% P1 D
  174.             (format "第%d步" (1+ (session "step")))))))
    + V: i; Y7 ^. ^$ m+ P

  175. , P1 ]; \+ j% R) [
  176. (defun board-position-parse (cmd)
    ! i" Y9 l2 M! U, G
  177.   (if (= (length cmd) 2)! |+ m. r3 t+ Y' t7 K% [
  178.       (list (string-to-int (substring cmd 0 1))5 y0 D$ V/ l+ F/ _0 ~! Q! y5 x
  179.             (string-to-int (substring cmd 1 2)))
    + ]- K( S: ~& z  l( [! l# T, k* q, d
  180.     '(0 0)))
    : w6 n* s) G' u- {) K, ~
  181. * A0 y8 Z% W; a$ ?2 e- F
  182. ;;; 游戏房间
    ' H0 Y* ^' ^* U
  183. (defun game-room-init (cmd)% F$ ]5 Q8 B) y' y
  184.   (let* ((middle (string-to-int cmd))$ |0 c* V' E- ~6 ]2 z
  185.          (size (1- (* 2 middle))))2 Y! A$ G$ M: a& W9 l7 v# e! U
  186.     (with-board
    / {; L7 j2 s( I) a- _9 `, @
  187.      (board-init size)
    " z8 ~/ M- Z* k% @; r
  188.      (board-put middle middle)))* c) {+ a, q5 b4 S* J! f6 {
  189.   'game-room)
    ; O/ u" X" O) z+ w5 I

  190. $ X. Z7 f& b9 e  h# S: ]) w% M9 _" {
  191. (def-room game-room
    ) n1 P9 ]* [, K3 a( ]
  192.   #'board-show1 H  A, e7 \2 R1 M
  193.   (t (lambda (cmd)/ s' ^) P5 b1 j6 G
  194.          (with-board
    ) \" M+ o4 K  K) |# j3 ]5 m& k6 x- ?
  195.           (if (game-over-p)( K3 }+ O4 @1 [- ]1 @, Q" \" E
  196.               'living-room
    3 Z* B. j, R9 F- i7 u1 a7 b) N
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    4 U+ w  C/ `3 l
  198.               (when (board-contains-p y x). E. @  m8 K! H' X6 W! k" X
  199.                 (board-toggle y x)
    # Z, r2 z/ l; W4 Q
  200.                 (session "step" (1+ (session "step"))))% G: y) R4 u9 w2 Z# e% V' d
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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