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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。/ Z! E0 c5 h+ p8 p2 S1 P) {+ o
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. ; @1 ]" Q4 e5 K: V5 G/ r
  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;">;; 定义新的游戏地图# Z0 W0 P) t. F' D. J
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL; K* E$ j  q6 x3 u
  4.   'tutorial-room-0)                     ; 默认的入口( O& u& f) X. i( l, n+ w7 @/ u! E6 P; G
  5. : j# P) J" ?) h* ~4 s  [: K
  6. ;;; 游戏大厅' w. Y# Y! \4 t
  7. (def-room living-room# e- V+ g- @8 p. n. T7 u
  8.   ;; 进入该房间后的提示语* `' W: N$ I, Y
  9.   "1. 教程; n  d8 U, ~7 G$ \# e- |
  10. 2. 入门(3x3)
    6 k9 ^9 v' U% v  S5 ~
  11. 3. 初级(5x5)' K* _" d) R8 C( e8 X5 S
  12. 4. 中级(7x7)
    * u' c# q$ h0 i& G; N
  13. 5. 高级(9x9)( m) n+ A. M5 H8 B" S3 K5 c% C* b
  14. 0. 关于作者3 M# s% T! D5 t: m3 }0 o0 f$ D  V
  15. 请选择1-5开始新游戏:"
    9 p4 \  r( m- }
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名/ D* x2 H5 T0 C5 Q
  17.   ("1" tutorial-room-0)
    : h+ F* q7 Y# m) T; G
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配4 p3 k' m* G/ p! U# A
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    " H, A9 p& i  z8 q
  20.   (t living-room))                      ; 如果条件为t,为永真
    5 o, X  u8 i$ N  w5 N
  21. 4 z, S3 ^3 F7 I2 [+ D* @2 I- \
  22. ;;; 作者信息
    5 _$ t: Q! e; g
  23. (def-room about-room( F7 G9 a: s* X; D& a% W. X
  24.   "作者:redraiment
    ' w! p4 i' B# _5 e
  25. 微博:http://weibo.com/redraiment
    # k! P/ A' k0 t' e( a8 v! a- A
  26. 有任何建议,欢迎在微博或微信上联系redraiment。+ N/ a  h- l) m* ?" w
  27. 请输入任意数字返回游戏大厅。"
    0 ?/ L( i) z; m3 g" v
  28.   (t living-room))
    ! u3 n4 }7 D( A6 k$ m! }
  29. 5 y& P# c& l: P
  30. ;;; 教程
    $ R8 w4 p1 [3 Q: A
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    " Q% i8 \9 ~5 {$ m' s+ [
  32.   "The number of tutorial rooms")0 A$ l0 O' w7 |. z; _% V* Y1 f
  33. 4 m3 ]' r" d, z" F
  34. ;;; 简化教程的定义+ Y7 e" Z* O0 r; i% Y5 ^
  35. (defun string-last-line (content)
    - n- V  I3 E0 K  }* w/ T
  36.   "多行内容组成的字符串中的最后一行"
    9 `- }/ d5 H& v
  37.   (with-temp-buffer- k5 C2 |, [: k, G7 [) O. l
  38.     (insert content)- c: Q$ y$ z) N
  39.     (buffer-substring (line-beginning-position)+ W5 i# h7 O: i3 y8 `5 b5 I
  40.                       (point-max))))0 o7 Z0 H% A& v+ d6 w! p2 v4 L$ A
  41. 7 u# M" f0 [2 P" y+ p! O: I* `
  42. (defun def-tutorial-room (prompt)  c8 s6 ^! S! j+ T: s+ G# I6 u
  43.   "根据提示语自动生成教程房间。
    , S* V- f' X& T
  44. 9 J) M- N$ y5 e5 s; D
  45. 1. 提取最后一行作为问题;1 q1 z3 u1 p! R' O$ f% p
  46. 2. 分析问题,获取期望用户输入的内容;
    - v8 }) `# K7 q+ d
  47. 3. 定义教程房间和重复提问房间。"0 W: B$ p* D5 X
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))- m( ^  {4 I0 x+ m5 t0 J8 F
  49.          (repeat-room (concat room-name "-repeat"))) F! G' n6 E7 ~- c8 \
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    2 Y: J9 p2 O. I; X' F
  51.          (question (string-last-line prompt))
    , B/ Q9 w# M; Y& W- i
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    % s+ F* e# D4 p
  53.                      (match-string 1 question)))
    + |$ h' E- d3 D, c
  54.          (doors (if except
    9 o$ g$ e; M4 J8 ~) a
  55.                     `((,except ,(intern next-room))# O3 U! m- P) k- I& z/ f
  56.                       ("q" living-room)$ W: U5 Q5 s0 `" ]
  57.                       ("Q" living-room)
    8 B/ o0 ?& `# h2 q0 D
  58.                       (t ,(intern repeat-room)))' V, n* l: u, Y- ~& S$ z2 n
  59.                   '((t living-room)))))7 x) n# A; @1 z
  60.     (def-room-raw (intern room-name) prompt doors)0 V; d' ~" A5 t, M( a
  61.     (def-room-raw (intern repeat-room) question doors)))
    ! H) j' ^2 X, H- Q, }

  62. ' ?& y: O, ]2 V0 \" `4 [3 A7 J
  63. (defun def-tutorial (&rest prompts)
      E4 E' k; v5 x$ Y8 ?
  64.   "批量生成教程房间。"
    * {; s+ V; d% ]+ L* z+ w+ L6 v
  65.   (dolist (prompt prompts)' b0 [8 x# g: ?' E4 M/ b) e
  66.     (def-tutorial-room prompt)))" b. i& T5 `+ B$ I
  67. 4 v) j! U5 b2 i8 Y! b. \' Z
  68. (def-tutorial
    # @1 Y$ T# Z" T! }" a6 J% s
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。5 d- x! s3 C, E1 z. Q/ |* D4 f* W
  70. 1. 教程: T) B( k! I9 [+ a
  71. 2. 入门(3x3)
    4 Z' X7 x$ W1 J2 F, n9 Z
  72. 3. 初级(5x5)( ]- {( w# F* w
  73. 4. 中级(7x7)
    : L/ `7 `  c9 N% Q. T, e
  74. 5. 高级(9x9)
    8 h+ n) p/ y. `" F& W
  75. 0. 关于作者* f  o. q; T* `4 W/ Z
  76. 请选择1-5开始新游戏:
    . d2 B' p0 J# @9 a. \" c
  77. 您现在正在游戏大厅里。
    9 ?; I$ k. [0 E! [; O: n; q3 o- z# X
  78. 请输入“2”进入入门级房间"$ S, Z3 @: W, u& [+ d; {
  79.   "  ①②③
    " [, j; @/ A2 X$ V" L& o
  80. 1 ■ 4 [0 |/ x/ e- q
  81. 2■■■
    . q9 S5 ^6 p! _) J- C
  82. 3 ■ 
    4 A& n/ G3 n1 s3 s9 J$ C2 j
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    & L+ o6 K! ~. j6 ~$ ~3 u$ M/ d! U& ~( @
  84. 请输入“22”来关闭第2行第2列的窗户。"
    $ v5 j2 s8 E& m$ V
  85.   "  ①②③
    / M. Y" H0 U$ C- y% ], J  b) g. Z' l8 G" R
  86. 1   
    ! D7 `+ t3 j/ {) Q& ?
  87. 2   
    4 _6 b& |8 ^5 p  v
  88. 3   # I- O1 G% P2 B
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。$ j2 l' M2 b# [0 e
  90. 请输入“11”,试着开启左上角的窗户。"2 X+ d5 o8 x5 ?3 i( s* n
  91.   "  ①②③/ H3 Z4 f" j1 W' S! `
  92. 1■■ / o( F5 Q1 Y6 p4 g7 M* b! [
  93. 2■  
    ; K* {2 `  ?+ `& J* l) H
  94. 3   
    7 W8 h1 D6 u$ {& l5 ~6 V, h5 y; y
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    ( q6 z* v; e. s- W2 e. E$ h
  96. 请输入“13”开启右上角的窗户。"
    0 b! Q+ V$ U+ S- V1 k7 {% p
  97.   "  ①②③
    ' d8 C( q4 S- d8 V
  98. 1■ ■
    " C+ T- s" z# s5 B0 K! c+ {# `
  99. 2■ ■* G+ n3 o. R' N
  100. 3   
    + `. ~/ z7 H+ w% C0 i; u2 @2 m
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。2 _8 n+ l# O* b" ~( G) X
  102. 请输入“31”开启左下角的窗户。"
    8 q6 v. y- G8 [$ b& F! o& ?
  103.   "  ①②③1 M+ _$ P: J5 g0 N" G  B& Q1 r
  104. 1■ ■1 _+ K, f! W5 s
  105. 2  ■0 O# ]/ P$ C# ?: K
  106. 3■■ + ]" [9 e# v2 c( J& J
  107. 此时,总共有5扇窗户被开启了。: ^# b8 [9 g9 H
  108. 请输入“33”开启右下角的窗户。"( j& f; z! ?2 L$ i/ ^
  109.   "  ①②③
    1 R( n7 h' m* y0 {: ~& Z9 J
  110. 1■ ■6 e* p+ U' r5 x6 O- N9 i$ J
  111. 2   
    ) [$ ^  n2 J, ?- l: `7 `
  112. 3■ ■
    * r' O* ^$ E, q* p- y2 r
  113. 现在,只有四个角落的窗户被打开。
    0 y" x4 d, V  P- N& O  E+ N: S
  114. 请输入“22”完成最后一击!") I& _& ^8 u/ E8 f/ C: Y9 r
  115.   "  ①②③' O# A4 z* P3 l% n3 C5 J+ v
  116. 1■■■
    / k0 ~2 o2 |! K# Q7 d
  117. 2■■■
    * p# {7 {. s6 [
  118. 3■■■
    " ?" G0 l2 k) {. f8 F9 ^5 j
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")) \! F" F, r/ K. g& M6 Q
  120. 7 q; ]: S: `$ K. @: J! h
  121. ;;; 棋盘5 a# K2 F: y4 B  [# {! I5 j, W  ^
  122. (defconst *wechat-5x5-white-chess* 12288
    1 I9 q% M2 `1 d2 g, G
  123.   " ")
    ( H% F- b. b- d$ }( y3 S6 E0 C7 K

  124. + G$ O; M5 ]( r. Y
  125. (defconst *wechat-5x5-black-chess* 96320 m7 V; H0 d+ O7 |
  126.   "■")
    & p; J2 p% X2 W5 S9 L3 I0 P3 u/ C

  127. ( e/ c0 i- D) u9 C! c
  128. (defmacro with-board (&rest body)5 X0 U# V8 W8 M) B
  129.   `(with-temp-buffer8 x9 x8 G0 u1 H' k( p( r# S
  130.      (unwind-protect, t  A) N) r0 X" K, ]
  131.          (progn: H) b" M4 o' y/ \5 n# g
  132.            (if (session "board")
    $ M- W! I$ ^* S  \) w
  133.                (insert (session "board")))
    - r$ b* J( s0 q6 A' I8 ]( [
  134.            ,@body)5 T% h. ^9 x1 ~8 `; p  I4 o' A
  135.        (session "board" (buffer-string)))))) v+ O. L8 D4 T& [, [  q8 m

  136. , N- z9 t/ R% D
  137. (defun board-init (size)
    & X, ~- M  r4 O3 O
  138.   (session "size" size)3 w2 N- H) {* j  w: h
  139.   (session "step" 0)
    ) O, P$ B( ]0 [" [9 F' w
  140.   (erase-buffer)
    ( B* Q8 C/ e* I7 ?
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    * V; ]: t$ F" O- ?
  142.   (dotimes (row size)5 G! [# y# m6 N9 h' F
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    ! ], n3 _5 R* S5 b
  144. " a2 z* J* w& w
  145. (defun board-contains-p (y x)7 L: M) ~% j  q8 c/ X! _
  146.   (let ((size (session "size")))3 p( n/ Y4 f6 p: d3 {& T
  147.     (and (<= 1 y) (<= y size)0 h3 b& H5 K. i7 b+ |
  148.          (<= 1 x) (<= x size))))3 b0 G* o" i3 A! O8 F8 T

  149. 2 R8 K2 C: U8 \% K; U6 }
  150. (defun board-toggle (y x)
    * q# ?3 q" ?% R0 d' s
  151.   (when (board-contains-p y x)0 A# ]. t/ Z  l
  152.     (goto-line (1+ y))9 i& X6 T4 p4 a/ A
  153.     (beginning-of-line)
    : [" L5 _& q* W6 v! i
  154.     (forward-char x)
    1 u- n7 y: ]- L+ s, t5 D
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    ; h3 {6 _! ~/ J
  156.                 *wechat-5x5-black-chess*# ?3 z# J2 f5 @
  157.               *wechat-5x5-white-chess*))' {9 p+ P  r  Z" \5 |1 x# B/ P
  158.     (delete-char 1)))
    2 Q! d7 G/ Y( d7 d7 r% G! }
  159. / S, Z7 B8 m  r$ W
  160. (defun board-put (y x)+ K2 l+ }2 J! V, i8 h
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    $ K& Z0 F: L+ F) T
  162.     (board-toggle (+ y (first dir))) Z2 O. H* H) S; p) V
  163.                   (+ x (second dir)))))6 ?6 f8 }+ a3 ~- X, H

  164. # v3 q) C' P. b5 E6 Z6 r: k0 c
  165. (defun game-over-p ()$ m: E2 s; n4 T; G# F
  166.   (beginning-of-buffer)4 l1 r3 `" y6 |3 k; g+ u& e! x/ Q4 _- I
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    2 b- `6 s. B* }" I4 l
  168. 9 E6 E1 Y8 P# B  z
  169. (defun board-show ()/ v9 O% s. B- g+ r" w6 s# F
  170.   (with-board! T0 \/ r8 ~9 o  i# ?5 w
  171.    (concat (buffer-string)! \  M3 E( v' [# {1 l. Z, a
  172.            (if (game-over-p)
    , H9 ?! y  L  z! o: L4 F
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    : q  g( q+ S0 z+ w" o
  174.             (format "第%d步" (1+ (session "step")))))))
    0 [0 B' U0 x, b+ {! m
  175. 8 ?# q6 h3 r1 o, E' t$ q+ o
  176. (defun board-position-parse (cmd)7 |( Z* n+ I3 R5 l# L
  177.   (if (= (length cmd) 2)+ N; C, n9 M- m6 J; T) e  ^
  178.       (list (string-to-int (substring cmd 0 1))& `! j) D1 K& J
  179.             (string-to-int (substring cmd 1 2)))2 t4 c+ |  E. y* s
  180.     '(0 0)))( o& `0 j  Y) i! H4 _4 E& |- w4 d/ f
  181. 2 V) p7 m/ O, |0 c" t. n) Z9 P! i
  182. ;;; 游戏房间! d. q- o; d8 L5 G3 g
  183. (defun game-room-init (cmd)( o6 P# m! t) ]
  184.   (let* ((middle (string-to-int cmd))# p# E, k$ S. J$ B
  185.          (size (1- (* 2 middle))))
    ( s5 C1 W; Y/ K6 L, Q3 l  x
  186.     (with-board
    8 b0 S0 }5 v+ ^5 o: G
  187.      (board-init size)
    2 x/ g4 P$ M- u
  188.      (board-put middle middle)))2 X$ D" J- Y: ^7 e/ |9 b
  189.   'game-room)
    * p" z/ R/ X/ s( G+ C- t; y

  190. ) T; W( k; x5 R: P# m
  191. (def-room game-room3 o) S) y, S- c! Z' t# s  a( Q
  192.   #'board-show3 E3 z1 p  G: w. Y7 k1 I: S7 ^
  193.   (t (lambda (cmd)1 x: t: O) q$ i0 c
  194.          (with-board
    ) u! c8 X# n1 d- X
  195.           (if (game-over-p)( Z/ h, h: T. {$ g$ O5 Y* o9 k
  196.               'living-room
    + {& I3 r8 @; N6 [9 Y& y/ u0 B$ y# A1 G
  197.             (destructuring-bind (y x) (board-position-parse cmd)2 o  y+ U. N& x4 y. j
  198.               (when (board-contains-p y x)
    : i/ M- Y; E1 e; A7 R7 J5 B
  199.                 (board-toggle y x)/ u. U) |( e7 a( j
  200.                 (session "step" (1+ (session "step"))))
    * ?. A3 p* m" {- a7 ]
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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