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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
# d" z8 v; O  p, q) [( x借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. # a8 O" o8 n& K7 ~
  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;">;; 定义新的游戏地图5 x  W2 N) n; a7 i5 N1 u
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL+ |5 `4 m( S' L3 c5 X) D9 o
  4.   'tutorial-room-0)                     ; 默认的入口
    9 m3 C: u* H: Y* b0 w

  5. . }! c2 u1 a1 r9 ~1 p
  6. ;;; 游戏大厅( d" P. q0 `1 Q. l
  7. (def-room living-room
    1 |3 P. e3 l8 Y; d. K
  8.   ;; 进入该房间后的提示语  w" x0 o2 y6 a7 @; K5 u8 W
  9.   "1. 教程
    1 N5 n6 [4 }2 A7 E) D+ s* ^6 G
  10. 2. 入门(3x3)
    - J) _6 u0 F3 k" I
  11. 3. 初级(5x5)
    3 m* O. h( e9 \3 w8 _  L0 S% Z& O
  12. 4. 中级(7x7)
    8 d! G' c( S* i6 \1 u+ }0 Z" ?
  13. 5. 高级(9x9)
    # N  o3 \3 G0 T3 \2 Q: ^, P
  14. 0. 关于作者
      {9 w. D1 f6 ~
  15. 请选择1-5开始新游戏:"
    + n, H3 v& P* T
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名9 `4 n- w( H, o
  17.   ("1" tutorial-room-0)* ]: o: e8 s4 Z. Y5 m
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配( F. e% E) Z! R
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    / L8 }. Q4 G) G& c4 z6 a. w! Q
  20.   (t living-room))                      ; 如果条件为t,为永真
    . J. K* ]7 A' B1 j6 ^

  21. 7 I* j# U, X* L+ i
  22. ;;; 作者信息% K' O" \, _& V0 f4 h
  23. (def-room about-room
    6 ~- M4 U) f" `% x* i8 U/ L1 D
  24.   "作者:redraiment
    9 I, {9 K% N0 P  L) f' Y1 z0 j
  25. 微博:http://weibo.com/redraiment! t# S3 {, J1 j6 N; s% M
  26. 有任何建议,欢迎在微博或微信上联系redraiment。& U; D! c3 o! l0 g! ^( ?* H, d6 b
  27. 请输入任意数字返回游戏大厅。"+ n! A. \/ m' @, e- t
  28.   (t living-room))- H9 _6 L& `0 k

  29. ) @# ]8 T8 W7 z% H) P
  30. ;;; 教程3 {$ l0 Q6 r: a# V- p- C- J- M! k% \
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    & h$ b# [3 ~+ L. q$ t3 M
  32.   "The number of tutorial rooms")( g3 i9 A% a: f  U( f

  33. 7 E4 |% x, Z$ C
  34. ;;; 简化教程的定义, N- L3 t! V9 O# [& m
  35. (defun string-last-line (content)
    9 b' j+ Z7 x8 T, T* o% ]" b! [
  36.   "多行内容组成的字符串中的最后一行"3 j$ C! @# R3 e2 v
  37.   (with-temp-buffer
    6 |! D1 c/ e" j9 S- U! b) e( {
  38.     (insert content), @' K& e% H! L. B4 w
  39.     (buffer-substring (line-beginning-position), |+ h1 g5 E2 ]7 J: l
  40.                       (point-max))))/ |0 @* z* M8 [) t* _$ ~, p2 X
  41. " \7 N0 |2 M/ ?
  42. (defun def-tutorial-room (prompt)* _9 L0 b& ^8 Y! D' K( h. D
  43.   "根据提示语自动生成教程房间。
    4 A( F2 Y" C" W! D  z9 u1 s

  44. ( R* ?; |' c9 ~
  45. 1. 提取最后一行作为问题;' V  O2 v2 z0 Y: A
  46. 2. 分析问题,获取期望用户输入的内容;7 d7 {. P$ X2 ?3 f# W8 w/ ^( i
  47. 3. 定义教程房间和重复提问房间。"9 f. b! f0 h* d8 j& Z
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    . S- B3 V3 J- z, q- O- B, {9 ^
  49.          (repeat-room (concat room-name "-repeat")): k+ Q/ d* R7 C' q/ c7 s
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    , V: w# C8 q( o3 A3 [) L! ]
  51.          (question (string-last-line prompt))0 P$ H; Z+ A8 C" s9 K( A* E1 G* K
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question); X8 _3 W8 \% n; F
  53.                      (match-string 1 question)))
    6 q" r: g5 {; V1 W2 Y/ l) F3 R
  54.          (doors (if except
    4 Z0 X" Z/ ?$ N
  55.                     `((,except ,(intern next-room))
    * |: J! q# x. A+ d' d; ^, s- `
  56.                       ("q" living-room)
    $ N/ v% _- s: v, y
  57.                       ("Q" living-room), E4 G+ l: n6 V
  58.                       (t ,(intern repeat-room)))" P9 ^8 F& R4 M3 B/ d9 f3 e; k
  59.                   '((t living-room)))))& d/ L$ v  h! \/ a3 f  g
  60.     (def-room-raw (intern room-name) prompt doors)
    " ]# ?9 }& Y/ P7 {( ?! [2 u
  61.     (def-room-raw (intern repeat-room) question doors)))
    3 v8 a/ a) ], c: l3 _, m& M
  62. : U4 r  D9 E6 I5 Q0 S& @
  63. (defun def-tutorial (&rest prompts)
    4 d, w" D3 D, l5 N: U& A9 k8 b
  64.   "批量生成教程房间。"/ L, q7 T3 ?/ w, e: O3 o# |
  65.   (dolist (prompt prompts)
    % a- b2 G! _0 _( {
  66.     (def-tutorial-room prompt)))
    1 l# i# J# Y0 M9 n2 A& P

  67. 6 W6 h4 M' Q* ~* G& ^7 `& [. s' D. e
  68. (def-tutorial
    # J- u" ^. W2 u1 j
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。0 V: u1 p, E6 L
  70. 1. 教程6 y* H' z% s. d1 k
  71. 2. 入门(3x3)
    # M9 O& X3 P0 {8 Z% z9 H
  72. 3. 初级(5x5)
    1 J0 ?6 V, Q3 U# t1 @
  73. 4. 中级(7x7)
    7 Q- j; z4 C- I
  74. 5. 高级(9x9)' Q, W8 L$ m# N1 T5 C! |- @; |$ d% ]8 s
  75. 0. 关于作者* x" g8 x. f: R7 t' i& e/ P
  76. 请选择1-5开始新游戏:
    " B9 k. V5 K% ?3 ~/ Q' K
  77. 您现在正在游戏大厅里。
    , S1 W* F5 r0 o9 C- J, ^
  78. 请输入“2”进入入门级房间"
    ! f, n1 a; |" r2 n$ t
  79.   "  ①②③% @: R; [& N9 o& i
  80. 1 ■ 3 h6 ?* O! c; g1 ?* @
  81. 2■■■
    . e* c4 Z9 I. I' k1 @& Q
  82. 3 ■ * b/ I- K0 E& s4 W  z
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    ( ~$ G" |1 B5 K. r
  84. 请输入“22”来关闭第2行第2列的窗户。"
    5 V: M* ^0 e# K! e# h7 r
  85.   "  ①②③, N$ X2 r' Q* X  x0 Z2 u
  86. 1   
    0 K+ b. C% F% k4 ?. q" n
  87. 2   
    8 O6 `6 n0 O% B/ W' q- O
  88. 3   3 x; g* A* w0 F) O4 g1 @& h
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    + g) i/ a2 Y& t' V3 {( P
  90. 请输入“11”,试着开启左上角的窗户。"$ N( c* g8 h! z4 I0 o% t
  91.   "  ①②③
    , V2 l& M& ~; ~2 J3 i5 d
  92. 1■■ , q! B5 h, [7 Q* M' d
  93. 2■  , E9 p2 I  ?& S0 _/ ~
  94. 3   & Y) C5 C9 ?5 o- ^2 l
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    ! |- s, o' U6 w9 R) l9 z2 x7 e
  96. 请输入“13”开启右上角的窗户。"
    % |( r; x' A( x4 n. Q/ m/ f
  97.   "  ①②③
    & R/ u$ w$ m5 F* ^9 q  S1 j2 K( U
  98. 1■ ■
    + @7 w4 b7 _, ]3 Y" f3 m2 ]7 I
  99. 2■ ■0 Q3 E# S3 `( a% e" f
  100. 3   " [- Q3 v0 e5 k' J! }# n1 k& n
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
      j$ x' M/ A& c0 L8 u
  102. 请输入“31”开启左下角的窗户。"' U* v2 b+ h9 ?, R* P; U* u
  103.   "  ①②③
    # u1 \5 G& q8 I+ G
  104. 1■ ■' I2 R2 |% S- l
  105. 2  ■
    , Y7 T' R* u5 H; T2 F
  106. 3■■ 9 G2 ?4 y! W9 w0 Q# q
  107. 此时,总共有5扇窗户被开启了。% x- G1 ?+ Z6 j, @
  108. 请输入“33”开启右下角的窗户。", V4 U& j( K& N) o+ I/ l+ q- v! Q
  109.   "  ①②③2 _, x% [& }* ^4 X$ l+ @
  110. 1■ ■
    5 v1 v6 m/ g7 }* p4 y: l# e( ^4 G
  111. 2   
    4 e' T) C) b  X( m
  112. 3■ ■
    + i; V3 H6 K7 W$ |2 f( j. a
  113. 现在,只有四个角落的窗户被打开。9 Y- B0 S% K% }, O
  114. 请输入“22”完成最后一击!"
    7 o5 s- {6 s4 n  ]8 Z$ i# {; ^
  115.   "  ①②③
    " B/ Y0 {3 J8 B7 f, J
  116. 1■■■
    + Z8 U  G$ @: n- `; v: C
  117. 2■■■7 X2 F& ^! M% A! i
  118. 3■■■) r6 }+ Y+ X- A: r3 z- F0 E7 P9 w
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    & p- W$ i8 Z8 U) f5 c1 C) Z2 X
  120. ( h2 r4 O. ~) Q2 v+ L9 c
  121. ;;; 棋盘
    , e& {- Z: N, V# e- d- g2 c
  122. (defconst *wechat-5x5-white-chess* 122888 f: @* @9 }8 w) ~1 t
  123.   " ")6 a' K: W( ~* o$ T8 C* j& j

  124. : Z% o, o$ L4 |' I
  125. (defconst *wechat-5x5-black-chess* 9632) ?9 v& |" L" ?9 Z5 W
  126.   "■")+ z% o0 r/ y" ~! W  Q' t
  127. ! h1 I7 l2 ^* R7 ^3 H
  128. (defmacro with-board (&rest body)0 `& Q6 ?6 X( n  Y  s. a
  129.   `(with-temp-buffer3 ]4 x6 C& a' C( Z0 m  Y1 }
  130.      (unwind-protect2 M4 W, i$ t2 X5 c, G+ F, F0 a
  131.          (progn+ k: K0 ?9 q6 d. S( U. J% S
  132.            (if (session "board")9 k" b- U' f) e
  133.                (insert (session "board")))5 X5 w4 m0 I0 n) O8 r4 N
  134.            ,@body)
    5 v$ E8 x! ~7 _9 k! B
  135.        (session "board" (buffer-string)))))  [7 L5 q1 q. H
  136. 7 n* x$ l: C+ J3 |* |
  137. (defun board-init (size)
    : `( u6 J& I( U# m, v
  138.   (session "size" size)" c" d; ^  Q* \% K4 `& w
  139.   (session "step" 0)
    9 |- q% `3 Y; c7 g5 u
  140.   (erase-buffer)
    8 K/ J8 S+ u4 j3 ^& F5 A5 Z  |
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))3 M3 d, L$ s% P- Q5 v# }
  142.   (dotimes (row size)
    8 Q/ M& }6 V3 [$ q8 j9 ]
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    4 J& D" Q* S, P" ?

  144. " D- x7 e# x% N7 b2 }6 d* a; w2 x
  145. (defun board-contains-p (y x)  C- n0 }6 |* q7 T; n3 G
  146.   (let ((size (session "size")))
    7 u5 J& a6 m/ h
  147.     (and (<= 1 y) (<= y size)
    6 g& {, j! T% Y1 @8 x  D# ^
  148.          (<= 1 x) (<= x size))))
    + ]- T8 D5 p; Z
  149. $ i' r  W- A& A
  150. (defun board-toggle (y x)- ]" P- d4 m9 h# t* }* R
  151.   (when (board-contains-p y x)
    3 @) h% C& x- J& Y# @! \, G
  152.     (goto-line (1+ y))
    7 u: U: H% d7 f8 q% W# u$ Y+ U2 [
  153.     (beginning-of-line)/ E4 ~8 x( s4 x
  154.     (forward-char x); N8 q: ~8 S0 e2 c$ U
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    - l% i0 y5 \' q; z3 V
  156.                 *wechat-5x5-black-chess*
    # e% g" A0 d! U5 B8 c* @
  157.               *wechat-5x5-white-chess*))$ u) }. R5 c' ?0 d
  158.     (delete-char 1)))
    , \, }$ d/ g+ {# L, W

  159. 1 d; s' `7 c8 I, [) Z* e1 L
  160. (defun board-put (y x)
    $ j' F- E; o; L/ u3 r& _
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0))). l$ a+ A( {; O7 u
  162.     (board-toggle (+ y (first dir))
    4 K2 ~7 Y( ^! Y, [' G5 N2 ?7 v
  163.                   (+ x (second dir)))))
    6 G  S8 g! H4 \4 q

  164. : R0 i2 o9 z  ]0 t/ D
  165. (defun game-over-p ()4 r) Z/ d7 X% W& o" H2 L) H
  166.   (beginning-of-buffer)8 G, }6 S! F  t0 w$ }6 Y* V
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    4 W! o3 m$ E" ?( x' ]- q6 F: C
  168. ! p! @  j& P3 |1 t
  169. (defun board-show ()+ o( V# L" t9 u9 J; Y; F' j
  170.   (with-board# o/ I% o3 I5 h0 Y) c8 s$ D
  171.    (concat (buffer-string)2 l0 k/ u% k) ]& L
  172.            (if (game-over-p)
    ) \% Z( J) O% X# m& R  y' n. {/ [: k0 z
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    3 x* V% s- i# R6 x+ a* r
  174.             (format "第%d步" (1+ (session "step"))))))). s3 Z0 W8 @4 k. `1 |( g9 c; l

  175. ; _1 o8 W$ j5 L+ ]! s$ I. `
  176. (defun board-position-parse (cmd)
    * D3 |& {6 L# a. z) r9 ]# U
  177.   (if (= (length cmd) 2)
    3 H3 G3 f8 ~/ _6 o2 L
  178.       (list (string-to-int (substring cmd 0 1))% H- \* I( o6 G# d- g; O* l# U
  179.             (string-to-int (substring cmd 1 2))). ^. N; m0 f- ~9 y
  180.     '(0 0)))
    # N) |/ p! }+ q* M$ U
  181. - `$ j4 Z2 [4 \8 u5 K# H0 H
  182. ;;; 游戏房间9 P& K. {8 g: y9 U3 Z
  183. (defun game-room-init (cmd)
    5 d8 E% }4 T; C5 }" C
  184.   (let* ((middle (string-to-int cmd))3 Q) Q( H# P) t( \. y: }
  185.          (size (1- (* 2 middle))))# H. P$ E9 g2 `( w6 G' V
  186.     (with-board7 }8 F) ~7 s; C4 w
  187.      (board-init size)2 T9 L$ P4 J. J' a, b
  188.      (board-put middle middle)))
    * L5 w# o$ B) _5 x  v' m0 ~  y- L
  189.   'game-room)
    ! w6 ^' [: f! H, k1 p3 D' j

  190.   `* ^- a  W. |0 y% x0 |' ?& a
  191. (def-room game-room. K, s! M, }& B# p* d
  192.   #'board-show( m  _% N2 Q3 a7 A
  193.   (t (lambda (cmd)0 n( e3 S6 d$ \8 U/ V6 R
  194.          (with-board) S, Y# B8 p7 M& \& s$ |5 o# I3 }
  195.           (if (game-over-p)
    / f' m" S+ R  z
  196.               'living-room' X6 ^$ o- ~3 B5 [2 C) e  H7 }9 n7 {6 L
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    ( ?/ d: c0 C) g. L
  198.               (when (board-contains-p y x)
    ; }# U4 G) h2 Q
  199.                 (board-toggle y x); |) Q7 I1 Y7 a, |: o2 F
  200.                 (session "step" (1+ (session "step"))))
    ! ?; U* r  Z9 g8 c
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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