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

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

  1. - g* A# `2 E6 `5 |' B, L
  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;">;; 定义新的游戏地图) V' K; W5 T6 p6 n, \; ^6 O
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL. X2 o+ D% T- ^, x$ o
  4.   'tutorial-room-0)                     ; 默认的入口6 G! F( h1 K9 @
  5. + ~/ l  x( i. J6 i4 Y9 r+ H
  6. ;;; 游戏大厅/ x% U- @/ f$ s3 M7 L( `8 @
  7. (def-room living-room
    + |8 L8 f$ Z, M7 _. z1 @
  8.   ;; 进入该房间后的提示语
    ; X" p% H4 G5 h) e! e/ f$ l- Y/ `
  9.   "1. 教程$ M5 r, x- D) {" G- F) V' a
  10. 2. 入门(3x3)
    $ z9 W- {' ^5 Y# j" u
  11. 3. 初级(5x5)
    5 D/ z0 B% b5 }
  12. 4. 中级(7x7)' s# O% o' d  q, ]0 U" Q3 G7 l
  13. 5. 高级(9x9)
    - [5 S4 ^' W/ n8 `: [
  14. 0. 关于作者
    $ R+ A% ?: y. y- \0 {
  15. 请选择1-5开始新游戏:"$ L. M5 K9 i% M  m) h
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名/ E6 j8 l0 C* T! h: a
  17.   ("1" tutorial-room-0)
    , Q* ]* ]) l; d! \
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    # d2 h$ j9 c/ Y& y# m
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    7 X' G9 y' P8 r+ e0 v5 o; i. i+ @
  20.   (t living-room))                      ; 如果条件为t,为永真6 M2 G5 F: n3 C

  21. $ m7 `6 I  b9 H5 Y; z
  22. ;;; 作者信息
    6 g8 O& {, P5 E+ Y; R
  23. (def-room about-room+ N- f0 x/ y% C- ^$ `3 F" {: A+ u0 _
  24.   "作者:redraiment5 V$ f; R  v5 ^1 e: a
  25. 微博:http://weibo.com/redraiment: m( R1 V/ {  u: m- O  T! a
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    ' z/ N0 L2 ?; W# p8 S5 w+ n/ q
  27. 请输入任意数字返回游戏大厅。"0 \/ ~) S; v7 e. r
  28.   (t living-room))6 A0 J/ h& }* v  j: P
  29. 1 k/ J' T0 \& ^. C
  30. ;;; 教程
    ; K1 K1 G8 A7 N5 S. ^+ z/ s4 R
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    : u) f$ H. G: U% I+ }. D7 e% T
  32.   "The number of tutorial rooms")1 {; `0 E+ W% }
  33. & c4 T+ a# _) N( _6 ]7 l
  34. ;;; 简化教程的定义
    ( F; g% S1 d% `4 x+ e
  35. (defun string-last-line (content)
    3 k& ]7 Q7 W3 ]0 ~+ l+ a
  36.   "多行内容组成的字符串中的最后一行"
    ; p* N9 {5 L% h( u/ A
  37.   (with-temp-buffer
    " ?$ }5 P6 E! b" a& ]
  38.     (insert content)2 s! Y6 h( S$ m+ x. X9 t: J* e
  39.     (buffer-substring (line-beginning-position)& r* }) D0 g! B
  40.                       (point-max))))1 H" D& {: T( s' e) S! M  Q4 }
  41. 8 y; ]" Z* K. a
  42. (defun def-tutorial-room (prompt)# i) L; g/ `* F- {* G6 |5 F) W
  43.   "根据提示语自动生成教程房间。
    0 e9 J( r+ @9 b) ?$ v! y. H. B
  44. ; C1 z: C& ?' q& H1 G1 @
  45. 1. 提取最后一行作为问题;
    7 [2 A1 h, s4 z7 k
  46. 2. 分析问题,获取期望用户输入的内容;' I, O! x0 u8 S; }. W% O
  47. 3. 定义教程房间和重复提问房间。"
    # j; R* `: i4 V- u3 d$ Z) l
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    # [0 C7 N3 ]3 a- u
  49.          (repeat-room (concat room-name "-repeat"))3 W* m: j! v6 m+ X, d
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))) n7 ]1 m% d$ G6 |
  51.          (question (string-last-line prompt))
    ' _& K# t% b5 R5 ^/ R
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)0 h. j; l3 v& P+ M
  53.                      (match-string 1 question)))( I4 Y6 h. ~/ o5 E/ x6 |
  54.          (doors (if except
    + N6 h7 E6 n' Z7 k+ E! Z) T# J
  55.                     `((,except ,(intern next-room)): g0 q/ ^1 H- a" W
  56.                       ("q" living-room)
    # E, r/ @' R3 e1 r1 j
  57.                       ("Q" living-room), I" n# u5 E% Q' Q3 |9 c
  58.                       (t ,(intern repeat-room)))
    / I2 U: W+ ]; ^9 j$ F4 ?2 c7 L; a( J
  59.                   '((t living-room)))))" t0 P& }9 c# W: e
  60.     (def-room-raw (intern room-name) prompt doors)* f1 @! Z- Q) }" N3 H
  61.     (def-room-raw (intern repeat-room) question doors)))
    ' U, u/ o. B! ?2 y9 m* D
  62. ( m$ j8 |# \5 w8 Z7 d
  63. (defun def-tutorial (&rest prompts)
    - @5 x: I9 p7 |
  64.   "批量生成教程房间。") M4 ], X7 V  d9 {, S7 r4 I
  65.   (dolist (prompt prompts)1 q( s% t$ L7 e
  66.     (def-tutorial-room prompt)))5 x4 e$ k. ?) |& b$ o$ f4 _& Q; m
  67. * {' w( _8 c3 h% C
  68. (def-tutorial
    % ]0 d! p' m7 V2 k& S) c) T
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。) L& D9 g  k3 Z; r' g
  70. 1. 教程' n1 u; f1 s3 O. ?* ~
  71. 2. 入门(3x3)3 L8 m# ~% b" J' |( L
  72. 3. 初级(5x5)+ }; {7 o- Y2 [. F
  73. 4. 中级(7x7)
    6 L. m) \! h/ a7 h# R
  74. 5. 高级(9x9)
    % ?! S$ |! w1 u
  75. 0. 关于作者" u6 H) A1 \. I4 ?, d
  76. 请选择1-5开始新游戏:
    0 U/ {6 y5 G# f, s
  77. 您现在正在游戏大厅里。3 v* y2 `$ Q# I, ^3 {1 V
  78. 请输入“2”进入入门级房间"! h# ~$ V6 p3 b3 I+ z/ M
  79.   "  ①②③
    + m' U& L+ Y  S+ ?* Q# k; I! P2 g
  80. 1 ■ * Y/ w. r9 N! }1 x$ y
  81. 2■■■+ W" U3 I# p/ U7 z) G
  82. 3 ■ + z1 Q- B* `7 o7 S9 @; p. t
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!4 ~8 }: H( N  `: u8 h
  84. 请输入“22”来关闭第2行第2列的窗户。"4 ?: S# x( J; d$ A6 _% I
  85.   "  ①②③
    # q2 w- Q: G. e  W0 k+ G
  86. 1   
    * r. W8 i3 @! u, n
  87. 2   
    ( ~- h( @( m& m
  88. 3   
    ; c( Z: J' B; A4 U- p7 n! |: ~: i. g
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。; j$ ~8 C' b. h9 W0 l
  90. 请输入“11”,试着开启左上角的窗户。"* p% }9 ^; Z$ t/ `) r. t# d3 x
  91.   "  ①②③9 x, s8 P: p/ h7 \6 y
  92. 1■■ 1 G) U' _" @5 ]. F6 |6 Y' L/ t
  93. 2■  
    : L* x0 y# b6 A  Z
  94. 3   
    % {- b' J# ^8 k- T  g4 I
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    : j, Y( I: h9 `3 h
  96. 请输入“13”开启右上角的窗户。"4 l) L1 @# S6 a, }' d
  97.   "  ①②③
    ; \9 D* \" P1 M% u7 u5 l# I
  98. 1■ ■+ C& |$ X# Y( m" d( i7 P/ ~+ G
  99. 2■ ■" s+ l  E# q& }$ h
  100. 3   
    # c; @. S' f+ v7 F% D7 m* L9 U1 y! w1 x
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。1 U# R8 C: B8 V: L& e
  102. 请输入“31”开启左下角的窗户。"; x! ~0 X1 e& Y$ D. F* `3 f: g* Q
  103.   "  ①②③
    7 U8 _" E# f2 C0 r% E  u, s, y) j
  104. 1■ ■
      o+ }2 E( X7 |6 w
  105. 2  ■4 ^6 A5 R' \; X; g* l5 v
  106. 3■■ " Z/ E! `# F* V2 c8 s& u% [2 u2 ?
  107. 此时,总共有5扇窗户被开启了。
    & ^0 \' H! A5 g% M$ E3 j) y8 T& V
  108. 请输入“33”开启右下角的窗户。"
    0 ?* O1 p) t, S# u; h8 H/ ~
  109.   "  ①②③% E% C% _& v" U  G1 d
  110. 1■ ■0 [" _6 x- b- K+ x/ p2 u8 h6 w
  111. 2   ( C/ v3 n# |/ F9 K
  112. 3■ ■9 b+ Z0 G4 s/ J# _( z" R+ m
  113. 现在,只有四个角落的窗户被打开。
      V  P  z' J9 m& {8 m8 k
  114. 请输入“22”完成最后一击!"
    9 m: d- a& z9 o8 y, Y- x& Q$ w
  115.   "  ①②③
    $ v/ X3 {( T3 \- T! I4 y
  116. 1■■■
    7 Z! _& q) }- B
  117. 2■■■
    3 L+ ~( A1 a. R( X- U4 |, j" O1 w
  118. 3■■■
    / M+ u0 u$ F  Y
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!"). \( F3 z, ?" b% ?: |! Y% D8 f

  120. 5 V$ g: t8 h$ ~/ \
  121. ;;; 棋盘1 W4 x. y( p% g" m9 f- j3 N* {9 I* `
  122. (defconst *wechat-5x5-white-chess* 12288
    1 }2 e0 D0 H' L& I$ u4 N  {' l# Y* [$ l
  123.   " ")
    ; a9 J1 X( b: o* ]( a

  124. ; O6 Z$ ?; f* I  D& S4 ?5 ^! u
  125. (defconst *wechat-5x5-black-chess* 9632* ]2 N: [* L0 D+ v( v: t8 O! H
  126.   "■"), C, c# w- R+ k4 v2 V# c

  127. 1 H8 w: t3 Q- o8 d
  128. (defmacro with-board (&rest body)
    # F1 C" q& R& o  L/ X- Y  _
  129.   `(with-temp-buffer
    7 K, L9 D7 G+ H, t% N
  130.      (unwind-protect  [* }$ X8 T( R! Y. j
  131.          (progn
    9 p6 Z' i2 R# O7 n2 s" H) d
  132.            (if (session "board")
    ! P5 M' {/ B, ?
  133.                (insert (session "board")))) @7 t( L6 }" U+ y$ M/ m% ^; i/ L
  134.            ,@body)5 [: |  p& E  g( g1 N
  135.        (session "board" (buffer-string)))))' c% H; q, {1 F6 F# Q' n
  136. * c- Q7 W( q' R1 p1 M. h" _' z
  137. (defun board-init (size)1 i' S. o4 x+ C7 ?8 m1 W+ l
  138.   (session "size" size)/ q/ k& x1 P6 Z" z. U
  139.   (session "step" 0)! d% P& @2 B$ F  u
  140.   (erase-buffer)
    , s9 _0 q, {% U+ Q- _
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    * x' a2 u! ^& |0 E! ~
  142.   (dotimes (row size)
    + ^3 W+ i& ^6 ^8 u1 l9 V; a
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    3 v6 k3 f" H5 e) q- s. Z
  144. 8 V. r& O2 w7 G! a
  145. (defun board-contains-p (y x)
    , k7 a# K; L- ]/ H* B4 G! @2 t4 j- U
  146.   (let ((size (session "size")))
    ' N7 X+ R3 A) V9 O& e/ x
  147.     (and (<= 1 y) (<= y size)
    ) u' Y( d, i7 ^* m: E! [4 q
  148.          (<= 1 x) (<= x size)))); q: o7 T8 ?$ _+ ~
  149. + V8 n- c  J3 Z5 h- n+ v
  150. (defun board-toggle (y x)9 W0 u6 m" w  y7 U7 S% X
  151.   (when (board-contains-p y x)6 Y& ], l2 z. f1 T5 H
  152.     (goto-line (1+ y))
    . c" Y) u% C) B
  153.     (beginning-of-line)
    & C. I" F. D) T& R$ V
  154.     (forward-char x)
    3 ]% C7 D4 |3 {9 y' O  }- o3 c
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))8 D9 X- P3 J: Z. H$ y6 y8 S7 W$ r
  156.                 *wechat-5x5-black-chess*# U" [. ^# K! {3 _, s  w% n8 @/ ?
  157.               *wechat-5x5-white-chess*))& J0 m& {$ ~8 D/ l" Z
  158.     (delete-char 1))), I3 u) r, I2 K

  159. + Z5 \0 ?' I9 T- b' a' }4 d4 Z5 s
  160. (defun board-put (y x)
    + C  E: b% y( q4 w, |6 w
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0))). o  V' W+ J% z2 {6 F
  162.     (board-toggle (+ y (first dir))
    . Z1 e' a% b: W5 M- |7 ^6 b
  163.                   (+ x (second dir)))))/ J6 F3 W3 h7 l. ?: o  Z# [) F/ t4 G0 Y

  164. , ^5 ^- A+ i) \/ E8 d, t
  165. (defun game-over-p ()
    7 `; P  p! P6 o9 s4 a2 D; k, u
  166.   (beginning-of-buffer)0 x% y) Z# t! `/ V2 `
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    , p, z' Q# B% R
  168. 1 A  C7 ^! d3 h; M
  169. (defun board-show ()1 P0 N. E5 h7 M! t
  170.   (with-board  o! r% ]( \+ f# n0 P
  171.    (concat (buffer-string)
    . E. k' U0 p. d+ i: m/ E
  172.            (if (game-over-p)
    ) L2 C1 \3 k" {1 N
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    . t1 A' X" C- d
  174.             (format "第%d步" (1+ (session "step")))))))
    " p. v8 \! D0 o2 {9 Z) y/ p

  175. : Z$ a7 A6 K# W
  176. (defun board-position-parse (cmd)
    . t) a+ G, ]$ d. ^
  177.   (if (= (length cmd) 2)
    ) V, m8 T' o! q, G- G
  178.       (list (string-to-int (substring cmd 0 1))
    : P6 i) N- e/ c6 P  Z* |/ x6 q
  179.             (string-to-int (substring cmd 1 2)))4 ~9 K( y' L/ f( F: G9 o- _/ b
  180.     '(0 0)))5 L- ]$ k4 @: _3 a# H

  181. 2 i. m9 V- V# x+ b  Y4 u
  182. ;;; 游戏房间
    7 O7 A3 `( p  o: q7 D3 v! ~
  183. (defun game-room-init (cmd)# t+ ?1 h. H5 C" C: g3 U$ K
  184.   (let* ((middle (string-to-int cmd))
    3 G' H, T+ p6 |0 L/ J
  185.          (size (1- (* 2 middle))))5 ?& Q4 E% ?+ A& |8 K& P
  186.     (with-board/ a# x! W7 ]: ?
  187.      (board-init size)
    & |. S: r  ?: D
  188.      (board-put middle middle)))# k6 T' W9 N; X5 \
  189.   'game-room)) ]" X# ?2 h/ R1 d

  190. " m) Z6 y! D, c5 B, I& ^- u$ Y
  191. (def-room game-room
    ( M7 Q" m# j8 U% f
  192.   #'board-show
    , d) d4 j( N9 ~  V
  193.   (t (lambda (cmd). \6 Y) b* ?- W5 g  O, W* V
  194.          (with-board
    : n$ T5 S1 b4 Z8 G: ]1 }
  195.           (if (game-over-p)6 v; |/ Q& W. p& v! C1 |+ G) j! i
  196.               'living-room4 u4 A, w' l& k/ d
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    2 D, s: a0 H+ D1 W8 }( W
  198.               (when (board-contains-p y x)
    & G5 r5 [, w- r* c4 Z
  199.                 (board-toggle y x)( p( Q9 Z" p" `% T
  200.                 (session "step" (1+ (session "step")))): |# r/ L' \2 i; s: i7 a
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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