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

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

  1. : H9 D" t# v* k3 h8 W8 g
  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;">;; 定义新的游戏地图
    : D9 V; y: I4 m( {" [. e& c5 R0 w. R
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
      {- `- g( n* g8 T) q6 F/ u
  4.   'tutorial-room-0)                     ; 默认的入口- I" I8 z" w1 M# Z9 I2 T% D6 i8 T  c
  5. 3 x- r/ e. F. I( g! A# s
  6. ;;; 游戏大厅
    ; B, H2 E( M+ I$ \, D2 {" {2 b# G
  7. (def-room living-room* B- S6 L  M, u9 \
  8.   ;; 进入该房间后的提示语, w6 A$ e- T) [
  9.   "1. 教程
    $ k/ D2 }3 y# E5 j2 g7 _
  10. 2. 入门(3x3)
    * @; n; G1 _$ R& T7 H% L9 m
  11. 3. 初级(5x5)
    / H/ P6 R, l" c% l* d* T
  12. 4. 中级(7x7)1 J+ J9 [2 \' n4 L1 ]
  13. 5. 高级(9x9)
    " g- M& d) `+ _3 ~& `3 d9 K
  14. 0. 关于作者% l& Z1 ~. J7 }2 n$ N; U8 E3 I
  15. 请选择1-5开始新游戏:"
    + d( M: P8 A1 y  }8 b, g0 `) N
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    7 `  [. t1 S$ C3 g+ l
  17.   ("1" tutorial-room-0)  _5 U( S- j2 Z3 y1 J8 y0 l! v
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配; y5 k2 z# O; s$ P8 k+ H
  19.                                         ; 相应的返回也可以为函数,动态返回房间名, _1 @  C* @7 C5 G
  20.   (t living-room))                      ; 如果条件为t,为永真  _. X! V/ J$ {! r

  21. - D" B3 L4 ^) m# f5 [7 t
  22. ;;; 作者信息
    & Z# t1 x8 a" j6 ?4 G3 q5 c
  23. (def-room about-room
    # T  }8 ]9 ?: S1 c) E; G
  24.   "作者:redraiment; E2 Q- S' p$ ~' I# ?% e
  25. 微博:http://weibo.com/redraiment0 }, a. {  ?& h! P  k0 Q9 u% y
  26. 有任何建议,欢迎在微博或微信上联系redraiment。! v* h! \! x9 o8 M" {
  27. 请输入任意数字返回游戏大厅。"6 r* e% D, T" {" A/ \
  28.   (t living-room))
    / B& [' v9 E6 E4 ~* t

  29. 3 A& W- Z( O; N' j8 T4 p" v
  30. ;;; 教程
    ! T; z5 b  r& W1 a4 k% R1 c# G
  31. (defvar *wechat-5x5-tutorial-rooms* 0& R6 h$ q7 i) k, [' t
  32.   "The number of tutorial rooms")! @) J, h" s8 N$ K& C" L9 t# p
  33. ' ]8 O: w" s4 j0 _# f! x
  34. ;;; 简化教程的定义
    ) j! a. h% P  ?4 o
  35. (defun string-last-line (content). `  M$ |* |- ^- n# F
  36.   "多行内容组成的字符串中的最后一行"1 \* b, q8 ~, s  N" j) U" J! p, j
  37.   (with-temp-buffer
    - D0 {! J* ~" c# v7 e! Y
  38.     (insert content)
    / B/ |  E" |- t3 S
  39.     (buffer-substring (line-beginning-position)
    9 n/ M( @( b% K- w' v0 V& K7 c
  40.                       (point-max))))
    8 A$ a' D5 y6 `9 A: q! Y
  41. + `6 X& J% u2 o  u/ _2 s3 T
  42. (defun def-tutorial-room (prompt)
    " H$ b2 ~' W& u" @! m( v% r( ?
  43.   "根据提示语自动生成教程房间。) R* G5 i0 V4 y$ R( ?; E* Y6 A

  44. # ]7 S  i2 E0 }0 p( |3 C
  45. 1. 提取最后一行作为问题;
    2 C4 J7 A6 l; U/ c6 Q( V
  46. 2. 分析问题,获取期望用户输入的内容;
    6 S' {2 P8 T# p+ \3 M! k# B
  47. 3. 定义教程房间和重复提问房间。"# x' H1 Z+ S; _
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))# M' V7 k2 h* M  N- c  p
  49.          (repeat-room (concat room-name "-repeat"))
    9 A6 h0 O% T8 x* r
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    3 V. h( \2 A: f2 h2 ~1 S$ J
  51.          (question (string-last-line prompt))
    & z. R; y1 r/ k' }1 U
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)" {; D3 S# Q/ n4 E9 k
  53.                      (match-string 1 question)))
    / l7 C; c) F; w* a) Q) T
  54.          (doors (if except$ v& [" y# A$ \# p% k
  55.                     `((,except ,(intern next-room))
    7 S0 m- ]0 Q& f3 y9 M
  56.                       ("q" living-room)8 i$ a; P6 [# P1 G3 ~5 ^& D
  57.                       ("Q" living-room)+ j7 F3 h$ F. W! T
  58.                       (t ,(intern repeat-room)))
    ' D# h8 X7 _5 o2 r3 Z
  59.                   '((t living-room)))))3 }1 W) o% y2 V3 C! r0 O, t
  60.     (def-room-raw (intern room-name) prompt doors)! n" z( f1 [4 |9 E" z2 D
  61.     (def-room-raw (intern repeat-room) question doors)))
    6 N/ u0 K* B0 n3 Q) }$ L

  62. % g% M9 [* G/ c% y6 D5 [$ N( O2 v
  63. (defun def-tutorial (&rest prompts)% @2 }+ G) e  ?* O% i' y1 M1 g
  64.   "批量生成教程房间。"& f* ~3 w! m7 W4 s# j
  65.   (dolist (prompt prompts)" R, D+ t# C: ^( U
  66.     (def-tutorial-room prompt)))  n7 m2 x: R0 ^. @: C) ^: \+ m' R: O

  67. 1 i1 R$ Y9 X7 W
  68. (def-tutorial6 }- x0 k0 C# f
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    2 }! h9 d( `9 i1 Q, q6 \
  70. 1. 教程" p& l$ [7 @- H5 a* _9 r" F& Y
  71. 2. 入门(3x3)
    & K8 ]9 {4 `1 o$ }# J" G
  72. 3. 初级(5x5)
    % l2 K/ h& L/ e0 e" i7 x( o
  73. 4. 中级(7x7)7 k  p; U" M: a, x
  74. 5. 高级(9x9). U" ~3 G  B6 b! e; @; m
  75. 0. 关于作者
    : D+ `; L* L$ i+ O% m/ b
  76. 请选择1-5开始新游戏:
    ; J# y9 o) N* {5 ]
  77. 您现在正在游戏大厅里。' |4 v8 ^% w: z3 _- S2 d
  78. 请输入“2”进入入门级房间"9 X0 E- O3 l( s
  79.   "  ①②③
    & v5 W* M' W- e9 o" V! b/ l
  80. 1 ■   s- m6 A4 V% G3 u, a
  81. 2■■■
    & B9 C/ j, W7 Z2 r) ^9 f; h0 z
  82. 3 ■ 
    , _& V& v5 A& e* @  E
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!4 |2 t: H" X2 C: @8 G4 ]
  84. 请输入“22”来关闭第2行第2列的窗户。"$ M9 ?- E( v, j* g# c
  85.   "  ①②③8 ?$ U% @$ u6 Y
  86. 1   ( _7 y8 b. t9 q8 j3 h
  87. 2   . _& {1 A0 y# d$ ~
  88. 3   $ g) T" \" d/ c+ Y
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    : `; X" v; G+ v9 s( L  i0 D0 R6 M
  90. 请输入“11”,试着开启左上角的窗户。"0 B# I9 W( j5 h4 u9 K
  91.   "  ①②③
    + i' w2 j$ d' l# W" H4 O" ?( u  \
  92. 1■■ " N; r% u, V4 c7 _; V- b* H
  93. 2■  
    % X- X& ?! e6 i1 w! g) }& P
  94. 3   
    2 Z1 \: C* y: Q- p7 G8 x" G
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。  v" O  n+ s! m7 \
  96. 请输入“13”开启右上角的窗户。"
    , c5 L9 ~3 X5 g2 J) i" M$ T
  97.   "  ①②③
    4 k* v# B& a6 J# N
  98. 1■ ■
    & Y$ n% H3 p3 \; Q  X
  99. 2■ ■
    0 E2 Q0 m+ e+ n& H# W
  100. 3   
    ( V& i. D# C0 M$ p( G
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。1 R5 i* u. Z; w, |
  102. 请输入“31”开启左下角的窗户。"
    & O, z5 [6 d' q
  103.   "  ①②③: H  J. C, D" Y" B) E( {' ^  j& |
  104. 1■ ■1 N/ R- Z1 c( r7 R2 b! ?
  105. 2  ■
    7 W) M0 \" r: U+ w6 D( _1 R! ]& y( z& P
  106. 3■■ 
    3 ~0 ^3 X7 a! p+ E8 T+ h
  107. 此时,总共有5扇窗户被开启了。* Y( u4 s* e  P/ @; r
  108. 请输入“33”开启右下角的窗户。"
    ! o* t4 l+ D7 [- W
  109.   "  ①②③
    5 p+ e8 i6 z. E! A
  110. 1■ ■7 l: }% c  l! \
  111. 2   
    5 a* y2 }/ E* N. {; L
  112. 3■ ■7 V' [6 b9 b3 j( H) v& v) P
  113. 现在,只有四个角落的窗户被打开。
    3 e7 W6 K# L+ G* a! }' ?) J
  114. 请输入“22”完成最后一击!"
    , ]0 M: k/ L  c% p! c% V% a6 R
  115.   "  ①②③
    6 N* E' U( Y& g. X9 [; k
  116. 1■■■' I  t+ d9 h5 j' }" E. w0 |# |
  117. 2■■■; z5 H2 ]- x# T) R2 E2 S
  118. 3■■■+ Z( q7 d' u. {, O7 E: G& P
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")7 u9 `; [8 G5 C0 d, {8 C
  120. 2 A4 x6 J2 l) |7 ^
  121. ;;; 棋盘; r% }% T& X4 R) D
  122. (defconst *wechat-5x5-white-chess* 12288
    ' Q% L8 ?  P1 e
  123.   " ")8 z" B  N% o4 F8 O
  124. 7 {  V' l1 s* Q, S+ a  c  t- [
  125. (defconst *wechat-5x5-black-chess* 9632. z) j5 F. @5 V0 s9 f- N
  126.   "■")
    ' Y' l* {  j3 V4 z& ?
  127. 7 Z  U4 n) x( ]' a2 Y
  128. (defmacro with-board (&rest body)* X2 g  V+ Q" N7 f, v
  129.   `(with-temp-buffer. f+ k3 }% e$ S0 o. b- s: y+ j
  130.      (unwind-protect# X5 B& h% q9 L" I6 P( K7 L. m
  131.          (progn9 e5 t& J2 |& v+ u
  132.            (if (session "board")
    : ~9 q" W# ?3 H) \8 ?) a2 e
  133.                (insert (session "board")))  b, n4 d3 E" n9 Y
  134.            ,@body)
    ; s9 s8 ]9 S4 `* J6 a1 _! U
  135.        (session "board" (buffer-string)))))
    ) k* _% J  O+ z& ~
  136. 9 Y# [7 E5 L. `* D& \8 n, u  c" w
  137. (defun board-init (size)
    3 X& T/ S' y! Y6 T3 \9 ]. l  a
  138.   (session "size" size)2 Y6 Q$ S* A5 a. ^0 s/ x
  139.   (session "step" 0)
    + c. ^# |6 F/ Q' Z& n! a' D4 h
  140.   (erase-buffer)6 G$ w( ?3 S! u" i: Y
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    " y6 E) `: l) a% R
  142.   (dotimes (row size)
    6 X3 A, k, r. C( ]% j
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    3 m# K8 \$ j& k5 |: W& W  |' w* V$ e
  144. 0 }  ^+ \2 [- F3 L
  145. (defun board-contains-p (y x)% q4 z4 U' r3 d% H. r$ U7 L
  146.   (let ((size (session "size")))) D6 |+ D0 V6 h3 ^
  147.     (and (<= 1 y) (<= y size). D" B7 V8 h% B" _7 K
  148.          (<= 1 x) (<= x size))))
    : H$ n! @' G% |2 f+ g

  149. ) U' C4 j6 \2 E2 ^, T8 w
  150. (defun board-toggle (y x)
    3 h4 Z) s$ p2 e- ^
  151.   (when (board-contains-p y x)4 @. Z. ^# z& [3 X- y6 n" j$ v
  152.     (goto-line (1+ y))
    ' c; o( K5 k9 N
  153.     (beginning-of-line)  P# r6 u/ C3 [# K
  154.     (forward-char x)5 n$ _/ {2 E8 Y  T% k! r0 X
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))$ ^6 k, U( B$ S! O- s
  156.                 *wechat-5x5-black-chess*4 ]  j6 d6 s' l) H
  157.               *wechat-5x5-white-chess*))
    . B$ @5 r" d, P* p6 ]$ g
  158.     (delete-char 1)))
    ! p# w# R! c, a( B8 ^& q0 m# u

  159. 2 i4 g4 a! J4 G
  160. (defun board-put (y x)
    . p3 @) L& A: M. S
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
      Q" c) l. ?4 s* [; w
  162.     (board-toggle (+ y (first dir))
    " s/ ^+ N: @7 e- l2 Z# f
  163.                   (+ x (second dir)))))
    - {4 _6 C' Y  Z

  164. 9 Z* v( [6 s7 [# z
  165. (defun game-over-p ()
    " F# h" }0 s) Z
  166.   (beginning-of-buffer)3 f- k' `0 ~; y1 X' Y) W6 o
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))$ R+ D& P% y8 q1 N- l( t* P* N; \
  168. 4 Q$ G+ K2 o. P& c
  169. (defun board-show ()
    . T1 q( X; T. h& o( B0 a1 @
  170.   (with-board% A& t5 Q$ S# O2 ~/ ]) e
  171.    (concat (buffer-string)
    6 q0 S8 D; d2 v
  172.            (if (game-over-p)
    3 J" L1 b. [6 s  r7 A7 U
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    ; w8 x, Q( C2 k+ ^8 i" q
  174.             (format "第%d步" (1+ (session "step")))))))
    4 l4 |! ~# S# o5 p% f
  175. 7 a) B$ `- e6 r
  176. (defun board-position-parse (cmd); ]2 U5 l' a4 C% }/ {
  177.   (if (= (length cmd) 2)0 q) D4 L1 e% N" d" |' s
  178.       (list (string-to-int (substring cmd 0 1))
    - X9 e# a7 a/ K$ H  ]5 \1 \
  179.             (string-to-int (substring cmd 1 2))); _- V( m$ n6 b. `  u6 x( {, f
  180.     '(0 0)))" H) R  D! U3 B& |2 `
  181. ! U! X$ E7 g" {& _$ m' v
  182. ;;; 游戏房间4 i% Q# Z6 A; |5 b
  183. (defun game-room-init (cmd)
    " @# j8 }0 I, {, z) A+ @1 f
  184.   (let* ((middle (string-to-int cmd))
    9 l! f) S1 F# ?/ T1 ?
  185.          (size (1- (* 2 middle))))
    1 w) I1 q1 l8 i0 t/ d: k, O7 S! i$ X
  186.     (with-board
    - g/ R$ N, k% u* G) j) E& A0 ?, g
  187.      (board-init size)! m* A& Q8 Y; e$ E
  188.      (board-put middle middle)))+ B$ r" \8 S( R% }4 U
  189.   'game-room)1 a* F; z# n7 V4 D! [( A
  190. % Z! L$ g- J1 v* d4 X" p/ l
  191. (def-room game-room& }& Q; K# Q" m6 H  P3 w6 Q  t
  192.   #'board-show. h& Z# Y" R" U* `) ~* e  J
  193.   (t (lambda (cmd)
    4 b- P, I9 V5 u& p' N
  194.          (with-board0 k5 t% z& |+ Q1 P& v7 O) y
  195.           (if (game-over-p)0 f9 f- x# t2 b, h
  196.               'living-room& T* v) ]8 s3 n1 l4 B0 l2 A7 ]' }: i- s6 |
  197.             (destructuring-bind (y x) (board-position-parse cmd)* \- f& C0 P0 w! n& H
  198.               (when (board-contains-p y x)# O/ p+ \  ^0 a/ J( O
  199.                 (board-toggle y x)9 m* N7 u; a5 R! B
  200.                 (session "step" (1+ (session "step"))))0 n0 Q7 P4 P) W1 d& S6 z+ K+ I8 k
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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