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

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

  1. ' Y/ Z  {# o4 v- }, A0 B
  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;">;; 定义新的游戏地图, s; y1 ~% k5 A1 o) c7 z
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL( J, K" a! y: |; n5 D1 c2 `
  4.   'tutorial-room-0)                     ; 默认的入口
    ) `; |/ F" n) k% R6 r& m! V

  5. # v, L- d0 {9 O
  6. ;;; 游戏大厅# ^+ r/ m. ]+ `
  7. (def-room living-room2 m. S1 L+ N1 X  ?0 {
  8.   ;; 进入该房间后的提示语
    . ]" k. |! p0 g0 i/ A
  9.   "1. 教程/ ?  Z! b. u7 e$ \" ~* Y) Y
  10. 2. 入门(3x3)7 e3 p+ K* Y8 n) @4 P
  11. 3. 初级(5x5)# H3 j4 R" _- E6 n( b5 C9 {
  12. 4. 中级(7x7)" g3 c! i/ p3 [
  13. 5. 高级(9x9)3 M) ~" \5 i9 h- R. F4 d8 s( `
  14. 0. 关于作者
    1 S( Z% Q( `8 a  T" ?& @% X( }3 e
  15. 请选择1-5开始新游戏:"
    0 ]4 A9 ~- a2 U6 a+ g1 D/ e2 z7 S1 E% D
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名; L, B0 n+ P8 E* `. a1 b5 j
  17.   ("1" tutorial-room-0)
    ) O4 a: U7 p# L0 ]7 }9 R4 x$ U
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配7 Z8 ?( _$ z* E" l
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    : Z4 d) ?/ ^0 h
  20.   (t living-room))                      ; 如果条件为t,为永真
    # ?( v3 \; j* D

  21. 1 p2 c; L' H& i1 y5 J
  22. ;;; 作者信息
    , a* u1 C8 H3 o0 c
  23. (def-room about-room
    3 {, u% Q* J5 k8 S
  24.   "作者:redraiment" J8 \& B, [  \- _; a( e
  25. 微博:http://weibo.com/redraiment8 P+ K3 {4 j2 s* Y& t3 c
  26. 有任何建议,欢迎在微博或微信上联系redraiment。" X7 l8 Q; |3 _; O. N9 U( h
  27. 请输入任意数字返回游戏大厅。"% q% H7 x: v6 z( q
  28.   (t living-room)); N4 q. g+ [; \

  29. ; l& B* r( F/ x/ ~! a  k
  30. ;;; 教程
    & @7 k  @# @) }4 ~; ]! a/ S" R
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    4 j# u7 m; b; k9 z, x' T
  32.   "The number of tutorial rooms")
    & j8 K2 w) _  t: z7 H1 ?

  33. " }3 @/ N# ~& J/ \* s: }  ]. R8 }
  34. ;;; 简化教程的定义
    " y* q3 _6 }( S" n+ l% r. ]
  35. (defun string-last-line (content). l) b+ z$ E. ?8 Q* e
  36.   "多行内容组成的字符串中的最后一行") Q% h- K4 Q& x" }" ?/ B1 F/ M
  37.   (with-temp-buffer$ E0 i8 G. W- O, H' K0 y
  38.     (insert content)3 y: i! W1 Z$ \3 D
  39.     (buffer-substring (line-beginning-position)( w, o5 v. u  R9 p4 Q5 k8 U8 _2 \; y
  40.                       (point-max))))
    3 Z0 D5 c& c& ]7 e' b

  41. 4 W. L4 J9 f9 O" }
  42. (defun def-tutorial-room (prompt)1 I( p4 S7 k1 W
  43.   "根据提示语自动生成教程房间。
    & K1 x- D  ^9 f6 H! o! y
  44. 0 h8 p1 a# p; l; {$ S: r/ I
  45. 1. 提取最后一行作为问题;
    ( x( M- B7 Z5 Z5 e  J6 ^
  46. 2. 分析问题,获取期望用户输入的内容;( Q( X$ |, p- ?4 s8 O8 k0 Q
  47. 3. 定义教程房间和重复提问房间。"
    5 i! x+ k+ a5 q$ M) l
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))# K9 Z0 ^1 R, E& h
  49.          (repeat-room (concat room-name "-repeat")), ^$ D7 B3 |; a$ x
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))0 W$ o' ~0 `0 v3 _7 I" W
  51.          (question (string-last-line prompt))
    * D7 S) c7 I. T- [/ b. H2 k
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)5 W! T! O+ q5 |7 v. e
  53.                      (match-string 1 question)))
    ! v3 v; g( ?2 |& o2 \1 V/ S
  54.          (doors (if except' o5 Y; [2 m  F2 t5 J0 @% T# {
  55.                     `((,except ,(intern next-room))
    , h# m# W' V. ]% t; H
  56.                       ("q" living-room)
    . P0 a$ f. r! G+ u7 O3 d. N$ ^) V* N
  57.                       ("Q" living-room)
    % `0 S) J' h" S& O" n% N. x
  58.                       (t ,(intern repeat-room)))+ T! ^# x( J. U4 H
  59.                   '((t living-room)))))
    ) ?) y( f! V# Y& d/ O( J
  60.     (def-room-raw (intern room-name) prompt doors)- }1 U6 S8 @9 ~  ?8 W
  61.     (def-room-raw (intern repeat-room) question doors)))# ]$ H+ \4 }9 o/ ]! ~# ]0 d6 o% }

  62. 4 q! D" V: e. {3 N" y
  63. (defun def-tutorial (&rest prompts)
      B3 L  x7 u! ^. r, T9 M6 y
  64.   "批量生成教程房间。"  ~; v, k# o' A9 W/ a
  65.   (dolist (prompt prompts)
    9 i9 a; a9 y9 o
  66.     (def-tutorial-room prompt)))4 b* E" E  K+ k+ |$ K9 X* x
  67. 0 [( J' K) g, }: {
  68. (def-tutorial
    / Z+ @& ?4 u" b( ^) X
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。3 Z  p2 E8 }/ h" o
  70. 1. 教程
    : H3 [5 {* Y( E+ \+ C4 z: J
  71. 2. 入门(3x3)3 @0 L- l, Q% O1 m8 A# E4 m
  72. 3. 初级(5x5)
    : U8 w) `9 c% p0 n4 Z: Z
  73. 4. 中级(7x7); F7 g" ?+ L" D
  74. 5. 高级(9x9)
    ( `) f/ L; E$ r" o* q0 R; k/ ?2 ^8 ^
  75. 0. 关于作者, \' u6 E! i5 J# e
  76. 请选择1-5开始新游戏:
    ) i( Y! L3 l: `; w
  77. 您现在正在游戏大厅里。
      y; I# i0 W) ?/ U% @$ |! k( |
  78. 请输入“2”进入入门级房间"& X) p7 f  {$ P& D7 R9 W% D! y! S* s
  79.   "  ①②③2 V% r3 n4 z& k' J6 ], A
  80. 1 ■ . f& X- Q" T' v) e# }$ U2 t5 a) a( Q
  81. 2■■■6 m: S& j/ K. M0 D
  82. 3 ■ ) v% D* q# E! U: C
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!, a7 C6 `+ n$ V7 J, P
  84. 请输入“22”来关闭第2行第2列的窗户。"
    * D. `, _4 @/ f5 y1 ~
  85.   "  ①②③+ b: `  z# R0 r6 @1 ?% O
  86. 1   
    : t: B/ H3 N' y; B8 }2 }8 ~0 b
  87. 2   
    : V2 z; v0 `3 [$ d
  88. 3   
    . `8 D! q$ B: z9 Y, H
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。. j) G$ p( H6 W. C4 W
  90. 请输入“11”,试着开启左上角的窗户。"9 O6 \* A2 |# w. s8 L' A7 H/ o
  91.   "  ①②③
    ' x# C. T8 r% Q3 K
  92. 1■■ 
    : P+ R5 w& o2 E' L  Q" {
  93. 2■  0 x1 f2 |, I5 m, M; Q
  94. 3   ' ~" c0 N: S& G% Z  `$ ]- y
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。- H' x2 Q7 ^/ @6 Y& |
  96. 请输入“13”开启右上角的窗户。"5 I, P5 y; O8 c
  97.   "  ①②③/ r. M3 f3 D/ }* M7 R' S: |) b
  98. 1■ ■. X) A( N, }. A3 f
  99. 2■ ■; B  w$ Z4 i9 D" W" s' q
  100. 3   
      E6 ?- |! C% X$ m. d0 N4 H
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    0 j+ y* _6 }3 Z, [( l
  102. 请输入“31”开启左下角的窗户。"
    - ~; X9 Y" T' z4 r$ ?* q4 `$ n
  103.   "  ①②③
    $ w2 g4 K$ i' t0 H% t( c
  104. 1■ ■5 |" f2 J9 f4 S( E+ N
  105. 2  ■
    # ]  [9 A5 |) r" D& V& P6 _
  106. 3■■ 
    / G/ _, Y& A2 E
  107. 此时,总共有5扇窗户被开启了。3 V" n; c2 R& ^5 A2 P+ O; C1 P
  108. 请输入“33”开启右下角的窗户。"% r' [4 P4 b; Y+ [
  109.   "  ①②③* `: r; t. ?- y- l2 `$ p
  110. 1■ ■
    4 v, f1 f( K8 |) j4 V. l
  111. 2   $ P7 Q" |1 m4 `7 y& L! J; u
  112. 3■ ■. V+ B. P9 O6 b6 o8 B9 W
  113. 现在,只有四个角落的窗户被打开。8 k; a0 h9 l1 U4 z3 `
  114. 请输入“22”完成最后一击!"
    6 Y3 ~8 _# j* L8 ~5 z$ }
  115.   "  ①②③+ ]" i: n" p, X; R) r/ T
  116. 1■■■) P4 {" I% E8 P3 ^/ M2 l
  117. 2■■■
    & w- x( _/ w3 \" @) r  w
  118. 3■■■
    $ ?) z' ^  p7 W6 @# V0 L2 i
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    $ P% _2 y6 h, P9 w$ ?& C

  120. - j8 s5 \( ~9 I! }) T4 N6 }
  121. ;;; 棋盘
    & E. T% c9 `4 {  V8 Y
  122. (defconst *wechat-5x5-white-chess* 12288: I- X  E9 `8 I! }% {/ [6 C
  123.   " ")
    6 s; x9 d$ |) p$ m
  124. 7 N) O) g- Y& \. Q: a5 P
  125. (defconst *wechat-5x5-black-chess* 96324 v" O+ C1 W  Z' _- e8 Z6 |
  126.   "■")4 j' N4 m. y1 t+ a& H5 x, O
  127.   q6 k" A- l/ C
  128. (defmacro with-board (&rest body)
    5 X8 ^" T- U& T! w& I6 p
  129.   `(with-temp-buffer
    * T9 D/ u7 e8 O* X! ]
  130.      (unwind-protect
    1 a7 C2 M/ I5 n0 f
  131.          (progn3 N& \  ^. q) v) b
  132.            (if (session "board")
    " b1 Z: J: k! B$ I# C+ E
  133.                (insert (session "board")))
    8 e; p. x' b) B
  134.            ,@body)" v  H* m4 [, |( m* k" o+ C
  135.        (session "board" (buffer-string)))))
    ' R0 T( t( U; ]$ f0 a; R
  136. * t8 Q5 i4 m% a4 P# K3 I  E
  137. (defun board-init (size)
    , k9 n3 e& R$ Q- K
  138.   (session "size" size)
    ; @( U1 [" J' N' z
  139.   (session "step" 0)
      _  b# R  l+ f# c& ?) K. ]
  140.   (erase-buffer)
    # {. T( p4 N7 B8 n  R1 {( x% P
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨")): o' F" V$ k+ x' S
  142.   (dotimes (row size)" `( {9 Y1 [- T% d- f; T% L+ P
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))) w  `( G6 ?* |) G) u
  144. 2 k$ ^- g3 X5 H. G. v" W% ]: X" T
  145. (defun board-contains-p (y x)0 \. X% x  w, M1 v1 [* n
  146.   (let ((size (session "size")))
    7 O* v$ T+ x0 c3 g& |- y6 `
  147.     (and (<= 1 y) (<= y size)' \# k8 f5 D: k# n
  148.          (<= 1 x) (<= x size))))- X7 e3 n5 h! U6 ?: i, U. n

  149. 8 n) U& v; H# D3 f
  150. (defun board-toggle (y x); z0 Q( m$ U  O1 y- j# x3 G. V
  151.   (when (board-contains-p y x)  i2 y+ ~% h6 n, b, M9 |) J0 q
  152.     (goto-line (1+ y))0 v+ p) H3 o6 Q; _# T0 C2 `
  153.     (beginning-of-line)% y4 _8 X! ]- @) S
  154.     (forward-char x)
    4 I. V7 f( N+ {7 Y2 E/ X/ ~
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))9 @3 ?2 W$ l5 j9 m+ N( M) `
  156.                 *wechat-5x5-black-chess*: M( b) [0 x+ b7 N, B7 f
  157.               *wechat-5x5-white-chess*))+ A7 A% u! }3 B/ x/ H8 S0 u8 g
  158.     (delete-char 1)))
    6 v& T5 Y6 z9 [  Z8 f0 S
  159. 7 O6 F4 W$ C9 [' U, X# {
  160. (defun board-put (y x)7 \% Z1 a  D/ U- \3 g9 V% f3 \
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))5 F5 ], C) |8 z7 y, D( T
  162.     (board-toggle (+ y (first dir))
    ; A- [8 p  a! u
  163.                   (+ x (second dir)))))) \5 H+ ]% T4 N0 B3 t% x
  164.   t) g: J5 o2 P/ v1 r
  165. (defun game-over-p (), i! t. C, V; U6 B, }
  166.   (beginning-of-buffer)
    6 @* d- O) H2 d& P3 F
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    % a: i  i+ Y" s- [1 m6 h4 Z" s  w
  168. $ ~; _- N6 A( @" `7 ]: t
  169. (defun board-show ()
    3 V9 D3 H0 y, Y( F& f
  170.   (with-board
    ( ]+ T5 n$ n: F. V: o$ R
  171.    (concat (buffer-string)
    + a% i: F  X# ~% \9 L) ?: D* T
  172.            (if (game-over-p)/ l  B/ ^) `! K/ h; ?$ ?
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))0 Q+ V- U; c. U" U
  174.             (format "第%d步" (1+ (session "step")))))))! E/ A& P, O4 k) @

  175. ) ~  o; H: E" N. |
  176. (defun board-position-parse (cmd)
    + p# E% t* c- ]2 w% p  C% u
  177.   (if (= (length cmd) 2)
    : Y1 d- T7 N5 q& Y! T( p0 P
  178.       (list (string-to-int (substring cmd 0 1))
    4 N9 Y. x$ ~8 x- t
  179.             (string-to-int (substring cmd 1 2)))& M% e7 C/ e- r( E: n
  180.     '(0 0)))
    6 C% D8 R0 D: {& @/ x* ?
  181. - P0 {; {# D6 a, r" C
  182. ;;; 游戏房间& A, o* e6 l$ q1 @
  183. (defun game-room-init (cmd)
      V8 W, I2 u1 k! w  o
  184.   (let* ((middle (string-to-int cmd))
    : s" |; I! G% j' B
  185.          (size (1- (* 2 middle))))
    % u+ M  }# X; U/ h3 X
  186.     (with-board* z* F5 r4 N4 `9 D8 F! `/ o
  187.      (board-init size)
    3 i8 N' S& E* [# ]! v3 J5 D
  188.      (board-put middle middle)))2 B9 m$ e% R9 _) r3 {& D4 Q
  189.   'game-room)- s7 d0 I' d; |1 |8 ]* N" j
  190. # L2 U- t% H  j' p6 `. V
  191. (def-room game-room
    1 D5 V1 m" a4 s4 W0 p" e8 z
  192.   #'board-show" J6 l- {' D- I
  193.   (t (lambda (cmd)( ]. O6 r- f5 p1 a! N: Z* f6 M
  194.          (with-board) k. V* b4 `* n5 c, y! o/ _
  195.           (if (game-over-p)8 j/ X% _- E1 @% q
  196.               'living-room
    ) Q) V7 f* f% u# f2 L. y& V) _+ ?
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    " ^* J& B4 P! ?7 A9 i" {+ y6 O* p& \
  198.               (when (board-contains-p y x)
    + w: W3 v  y9 S6 u. K$ W1 @
  199.                 (board-toggle y x)3 f* G. v* ^8 D# K
  200.                 (session "step" (1+ (session "step")))): }* f0 p" D6 ]* e& n
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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