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

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

  1. 4 p9 Y! r6 I4 D5 F) |2 y( u: E
  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;">;; 定义新的游戏地图
    7 f4 C1 D' m: Y5 `# A  Y, K7 j
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL* j! ^8 V1 m# Z& @2 B6 V5 U
  4.   'tutorial-room-0)                     ; 默认的入口
    1 F1 i7 j- @& S) h6 V

  5. ; t8 S! {; ^* d8 t: p$ ?0 m0 y' m
  6. ;;; 游戏大厅
    5 H% @2 v# Y7 l$ R+ [7 [
  7. (def-room living-room. y6 @+ T6 e1 _0 ?) m. y! Q
  8.   ;; 进入该房间后的提示语' g* s8 g% T& J+ ^& f2 z; f3 z
  9.   "1. 教程! ~) I! ~) u) F4 J6 j5 z' A
  10. 2. 入门(3x3)4 G' g% @- l8 g3 g0 ?, Q$ M8 V
  11. 3. 初级(5x5)) i2 G+ \) s1 H( K
  12. 4. 中级(7x7)- n( q2 a  g+ ~8 J' X* ?! ]
  13. 5. 高级(9x9)
    , p1 A% ^+ j7 o
  14. 0. 关于作者/ z7 B* q1 E& w; v' S9 _: u" Y
  15. 请选择1-5开始新游戏:"
    7 B& Q( Q. N4 _) V
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    ' M/ Y% `& y6 t- {$ z+ d/ d
  17.   ("1" tutorial-room-0)
      r5 M  W  }; L9 u$ a
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    ' W* g# \: ^% h% l8 `
  19.                                         ; 相应的返回也可以为函数,动态返回房间名2 L  v0 K( @( k
  20.   (t living-room))                      ; 如果条件为t,为永真
    ! X) n/ O2 b2 B7 b& h# r

  21. ( t: l3 I3 ~" u( C3 Y, W3 |
  22. ;;; 作者信息( _" a  v9 f( N" @- u8 Q
  23. (def-room about-room8 D+ h( a# ]5 D' ], \: R1 h' W, p
  24.   "作者:redraiment  ]% R3 W( |5 m: T( E- m
  25. 微博:http://weibo.com/redraiment
    3 u4 E, [: E" v$ Q4 w0 H$ d
  26. 有任何建议,欢迎在微博或微信上联系redraiment。4 M' q) x" ^& p
  27. 请输入任意数字返回游戏大厅。"9 c+ G0 O- t4 v) m3 J
  28.   (t living-room))
    . d; P: V! f+ q% N
  29. $ s8 s4 U5 D: ?. z; q
  30. ;;; 教程0 I1 i( W3 {; ], F( ^: I) R
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    7 ?4 L' G# i. X
  32.   "The number of tutorial rooms")' ^; J! z* b6 t7 ?9 q
  33. : @) K( N3 E; T
  34. ;;; 简化教程的定义& J) T: [) v2 w( _' m" z9 ]
  35. (defun string-last-line (content)# P2 R% i5 x2 ]( Z4 e
  36.   "多行内容组成的字符串中的最后一行"
    ' {- w/ @! f! V* W4 {  m' {
  37.   (with-temp-buffer! J$ f" x- n+ ~5 {! b$ w
  38.     (insert content)9 y5 W) a+ E4 n6 L% T5 }( i" M
  39.     (buffer-substring (line-beginning-position)9 D! s0 q8 W2 p
  40.                       (point-max))))
    2 }& [& |% y% W. C, ^2 `( K
  41. ; ]8 N3 D/ `% V( M. c% r1 @* D. T
  42. (defun def-tutorial-room (prompt)
    7 m; D5 V& r0 q- {3 Z8 a6 [
  43.   "根据提示语自动生成教程房间。: @2 [* }; H+ l: |2 S) ?% n( S' Y
  44. 2 X6 r8 t, p3 x! L1 Z
  45. 1. 提取最后一行作为问题;9 b5 _7 v( S1 t- H  v) l
  46. 2. 分析问题,获取期望用户输入的内容;
    $ |0 d! m7 S1 Q- j9 B
  47. 3. 定义教程房间和重复提问房间。"! i7 T) I2 O& F: h9 ~. b4 W
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))$ R* s9 N3 V# @
  49.          (repeat-room (concat room-name "-repeat"))
    . F6 @% i$ E& E2 n" F" m- y; Q
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))8 Q' H1 m+ K# a* n1 Q
  51.          (question (string-last-line prompt))
    7 a" p9 c# c9 N  {3 k. C6 ]
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    $ [3 b8 c0 f3 n2 N- W( V( s5 A7 h
  53.                      (match-string 1 question)))1 x* K, L% o( u
  54.          (doors (if except
    # ]( Q/ o) }  f/ Z1 U4 J
  55.                     `((,except ,(intern next-room))
    : L; @$ f3 a8 T% Z; W, c' S( B
  56.                       ("q" living-room). a5 ]/ z' ~* T# c  U* J4 X
  57.                       ("Q" living-room)
    0 p# g5 L+ x) G, v7 `% E" _7 u1 s
  58.                       (t ,(intern repeat-room)))
    6 j! V/ v0 J& O$ ~9 k& l
  59.                   '((t living-room)))))
    6 T) p1 H* Z: W9 }  Q
  60.     (def-room-raw (intern room-name) prompt doors): o" n0 M' X# ~1 Q/ e
  61.     (def-room-raw (intern repeat-room) question doors)))
    . @: l$ z. o1 s, a  C' R( O$ B

  62. # T' a# ~6 R/ [5 g4 Y/ E- W/ T, S
  63. (defun def-tutorial (&rest prompts)! a5 y- H5 B# T" `
  64.   "批量生成教程房间。"3 [# s4 Y& ]% x
  65.   (dolist (prompt prompts)
    " e3 m" G7 G6 b  [) ]9 v& L* n5 D
  66.     (def-tutorial-room prompt)))) V/ ?, ?- v% }6 S; u
  67. 5 I  ~' A& f3 N2 \: x7 L7 T) [
  68. (def-tutorial
    ' I3 C- \/ k6 k+ a- v- B% a
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    2 t1 K( B5 R2 r: I9 M9 k+ Q
  70. 1. 教程4 B% U- s- P4 y" o  ]
  71. 2. 入门(3x3)* K$ C% ~% s+ }" H
  72. 3. 初级(5x5)
    7 i" i. {& b4 f0 v0 M
  73. 4. 中级(7x7)7 H3 B* s0 n* Z& V& s: S
  74. 5. 高级(9x9)) k1 I: z" T. v+ g  G
  75. 0. 关于作者* f$ g; {' R$ W0 N: N# C
  76. 请选择1-5开始新游戏:* s( D/ m& d- K8 b7 u: T& s
  77. 您现在正在游戏大厅里。
    * p& ^( O6 A. }1 @1 Z( Z# c
  78. 请输入“2”进入入门级房间"
    8 v, u0 @1 K0 Q* L1 J
  79.   "  ①②③
    , C! z2 W6 G, c! e. {  G* }* ~0 Q
  80. 1 ■ 8 i; B! n) \: J
  81. 2■■■
    + {( `, x% d9 @3 X: T8 ~. Z
  82. 3 ■ $ C, d) z5 t% u5 S, U; s% `$ x
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!' y+ w* Q# l, ^
  84. 请输入“22”来关闭第2行第2列的窗户。"4 o  h1 O, ^2 v, E
  85.   "  ①②③7 y& d% l: _/ x5 i1 f5 t7 c  q
  86. 1   
    ( w+ m. S1 p7 `$ r
  87. 2   $ B5 T/ c8 S( w: A5 I- E# Z
  88. 3   ( M% K5 V' @; k0 ]
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    " t( d( f2 O9 I# ]: ~6 D$ r9 C
  90. 请输入“11”,试着开启左上角的窗户。"  C6 k* |6 {  a  Y& _
  91.   "  ①②③
    ( J+ a8 m; a$ m" C; ~
  92. 1■■ $ `7 `, [+ S1 M
  93. 2■  + [7 t6 i/ i9 w: R# {" ~
  94. 3   
    2 J! z% U1 k  N3 m7 A  Z. T% |
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。* n' S% H8 F0 n
  96. 请输入“13”开启右上角的窗户。"
    ' r5 b4 B: o$ p, m1 l+ A
  97.   "  ①②③
    ; L9 Y5 o# k4 J' k# V7 M: p
  98. 1■ ■
    9 T( T) O3 s% Z. g. e  x3 t9 p: Y
  99. 2■ ■# ^  d! ?3 W( O4 ~0 a
  100. 3   
    * B  V! ^! D2 ?5 R/ K
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。0 s& Q: e# a7 \9 [$ A& q
  102. 请输入“31”开启左下角的窗户。"
    ( A, J. y" ?4 z( {0 z
  103.   "  ①②③( ]' x% q2 |+ ~/ p4 |/ J
  104. 1■ ■8 q! j$ _& f+ K: c
  105. 2  ■) _/ N5 {/ k" X8 ~1 e: G' y
  106. 3■■ 
      \" {9 V* H4 d* `. |8 |' L
  107. 此时,总共有5扇窗户被开启了。/ W/ `  |, Q; Z3 V
  108. 请输入“33”开启右下角的窗户。"
    3 J5 p9 }$ V; R$ y
  109.   "  ①②③6 q! x( b; a; C7 E. b
  110. 1■ ■* g. [& C  P+ ?9 D) S
  111. 2   
    . H4 W4 s( G8 \# P* O: l) f! t
  112. 3■ ■! q3 h; E; m, p% d3 ^
  113. 现在,只有四个角落的窗户被打开。
    1 l, b+ n( I" G
  114. 请输入“22”完成最后一击!"
    ) o& g  m. v+ K+ D* s
  115.   "  ①②③0 n$ l6 Y4 ?' B2 c% t
  116. 1■■■
    - b' a3 s+ F2 Y9 t& a: V$ k: ^% G
  117. 2■■■' t& e/ T" r8 E2 b3 }( B
  118. 3■■■
    0 d) l  p6 L1 X4 n3 F$ U/ m$ T
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    ; _, r! i( f0 F! I6 e

  120. / ]6 f- m3 g! x- M2 h
  121. ;;; 棋盘
    " G5 R6 ~9 h1 q; \7 J* j1 q2 L
  122. (defconst *wechat-5x5-white-chess* 12288
    # N# N0 R  s$ r; c: O7 X  A4 i
  123.   " ")
    # x3 m6 o0 R; z: h1 p1 i7 r

  124. . O5 w7 b) c4 d2 w( n
  125. (defconst *wechat-5x5-black-chess* 9632) _- L, w0 S! z9 A% [& x. k/ C; c
  126.   "■")
    9 [; a' h5 G: J# I5 {" u

  127. " U. H$ I5 Q4 e" W1 {
  128. (defmacro with-board (&rest body)
    ' ^# O# D( h; J, x0 D
  129.   `(with-temp-buffer* S/ D" K# S& V
  130.      (unwind-protect, W' S) Z3 L/ q& J- ~
  131.          (progn
    ! `" b/ g  _' K! K4 C! ]$ Y0 Y
  132.            (if (session "board")
    & h3 O3 J; d( U
  133.                (insert (session "board")))1 v$ B+ l, ~: o/ P8 v7 |7 W
  134.            ,@body)% i/ l' h0 E  ?" b* \  O% E! b' p
  135.        (session "board" (buffer-string)))))
    5 k' [3 }6 ~- u! U. M

  136. : e/ S$ p6 e2 a. p3 k6 W5 s
  137. (defun board-init (size)
    4 ?& w% F1 M7 x+ b1 D5 u1 E
  138.   (session "size" size)) k1 e; h, i! Y8 [8 e% }. T4 e. U
  139.   (session "step" 0)" K  y- V  k" L9 P. `9 t- b2 {
  140.   (erase-buffer)
    . E  e8 L# K% D" G2 g: s
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    : Y  f0 u0 D0 \) g. m4 |& V4 L
  142.   (dotimes (row size)  T* e, N8 s5 C/ b2 q) _# X7 O% u
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    " P! J1 c6 O  ?' @
  144. , L$ L7 ^' ~% \& a1 _2 s( P8 r0 N
  145. (defun board-contains-p (y x)
    , `. ^; S- R+ S$ d6 h  D. f
  146.   (let ((size (session "size")))
    ) |+ u6 ?. s; Y
  147.     (and (<= 1 y) (<= y size)6 l: O0 C. z1 @1 x4 C* d) P+ j6 G
  148.          (<= 1 x) (<= x size))))  ^7 }7 ^! p# F6 c" F% O; D
  149. 2 P! |( }$ @& l
  150. (defun board-toggle (y x)
    % ~2 j6 W; t# X+ c1 |  ^
  151.   (when (board-contains-p y x): t/ x7 ^( r$ J9 k% Z
  152.     (goto-line (1+ y)): @( Z; P4 X- v7 ~
  153.     (beginning-of-line)# Q8 c* Z, |; t2 s! ~
  154.     (forward-char x)6 r0 f, W/ f3 w& N: n7 |
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))+ }) V* {2 i0 @0 N2 D( w) J( y' |
  156.                 *wechat-5x5-black-chess*
    3 _. o0 h) n" X& E6 j7 n, Z
  157.               *wechat-5x5-white-chess*))
    3 l0 l6 ]4 ^9 H. @# J$ h
  158.     (delete-char 1)))" Y$ R5 H: |  C* I7 w! |
  159. & X1 `' |* q4 R' W
  160. (defun board-put (y x)! h3 Z% Q3 X, t( A2 G
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    $ E5 S% f* \( D( ]% H, t6 C
  162.     (board-toggle (+ y (first dir))
    + n7 j' \3 [) Q. v; I# Q" Z, y
  163.                   (+ x (second dir))))): U/ \  L: w1 l, d2 U5 ~
  164. , `- C' |; L1 X% F* V3 @: o
  165. (defun game-over-p ()4 Y+ d% @, d$ G" X: m  m
  166.   (beginning-of-buffer)
    . n2 `, {) u2 U2 J4 `
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))# h/ f" q; [4 x: a& ^. M5 r
  168. % `: Z( X0 t, m4 z3 |
  169. (defun board-show ()5 ~; z: y. l7 O
  170.   (with-board7 u$ R) y' t6 q$ p# h
  171.    (concat (buffer-string)
    , W/ D5 c6 j5 A% ]# ?% ?
  172.            (if (game-over-p)/ X6 L7 \, P; h5 c# I
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))6 I/ R! d0 \! }9 Y# v  {
  174.             (format "第%d步" (1+ (session "step")))))))! z* c' z4 |( @9 [) d5 `9 [
  175. % q- _  E- ^" T9 f
  176. (defun board-position-parse (cmd)+ ?& E1 D# u0 y% u" R
  177.   (if (= (length cmd) 2)
    : o; o: o; C+ G) ~1 |
  178.       (list (string-to-int (substring cmd 0 1))
    * W# _) V' O1 u. ]
  179.             (string-to-int (substring cmd 1 2)))
      G- c% [& N6 V8 d( N
  180.     '(0 0)))
    3 C* f5 K3 d7 I9 L
  181. : R# f  d" x) f$ h6 _: t/ W
  182. ;;; 游戏房间
    3 T1 w4 [+ v5 u8 U' f% Y
  183. (defun game-room-init (cmd)- s% z8 F" r% C5 b. v
  184.   (let* ((middle (string-to-int cmd))0 M' U2 i( w$ e+ n( r
  185.          (size (1- (* 2 middle))))( C' F; @7 \; i6 a" U8 k
  186.     (with-board8 d, H) N5 I7 N2 z
  187.      (board-init size)
    . _" H+ g' V0 Q+ C/ Z$ S' P
  188.      (board-put middle middle)))
    5 ?7 C& J& k! f2 F' A2 @: o6 e5 A8 J1 g
  189.   'game-room)2 S) \# _2 A! w: o5 n
  190. 5 L8 F% {; u) [# F- G+ P# M+ t
  191. (def-room game-room1 w, {5 ?; M1 z2 j4 o
  192.   #'board-show
    9 \6 s/ P( }$ M% k( G
  193.   (t (lambda (cmd)
    : c: A! N2 T' w& s5 J4 B: G& p
  194.          (with-board
    3 g3 C  M/ ?' k. q. J0 H/ n
  195.           (if (game-over-p)( V) y' Z6 q# q& D
  196.               'living-room  y0 W2 a6 a  n! U2 B/ \- K
  197.             (destructuring-bind (y x) (board-position-parse cmd)9 g) R1 D" \: \& ?2 B" b  ?
  198.               (when (board-contains-p y x)
    ; {( J; y4 u8 b5 \4 @5 T/ B
  199.                 (board-toggle y x)7 s+ V3 x7 `9 ~4 a  C7 i5 b: g
  200.                 (session "step" (1+ (session "step"))))
    5 V! p! p( d8 w* I  S- F* j
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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