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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。: i+ T7 H; q1 k- o
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. * D, G" D$ t9 M$ X6 }* P
  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;">;; 定义新的游戏地图3 }% m  U7 d3 w  o
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL+ Q# Z* ?# ~" O9 C) F' s/ M
  4.   'tutorial-room-0)                     ; 默认的入口) a) L& ^9 @. ^7 R( {
  5. 0 p8 u* h/ |# b8 n' v. @6 q. f. y
  6. ;;; 游戏大厅4 z/ a# @( d7 ]2 ?4 v
  7. (def-room living-room
    , ]& X4 r$ h, {* ]  W. p" h9 r
  8.   ;; 进入该房间后的提示语2 M  H3 _( E' v+ V1 p
  9.   "1. 教程
    & |0 |9 u8 s' g. X9 b
  10. 2. 入门(3x3); L! W5 l' m( a! P6 b
  11. 3. 初级(5x5)
    & ~4 j$ j" w3 b/ X
  12. 4. 中级(7x7)
    ; b9 c$ E- M% M0 H6 _
  13. 5. 高级(9x9)2 v, j8 X) X2 x6 S4 j# u$ U- m
  14. 0. 关于作者
    ' W$ |$ o7 T5 t) V0 \+ M
  15. 请选择1-5开始新游戏:"8 c; B+ c& s% [/ p! t% |
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名; E  F1 w$ d" ?9 Q+ L
  17.   ("1" tutorial-room-0), E& y4 _1 @- \" \4 F5 Y7 Z
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    ) F2 O* ^% i0 |% p: {7 e8 ]
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    % P9 n  k6 k( a
  20.   (t living-room))                      ; 如果条件为t,为永真: K2 ?9 _! X5 u( A

  21. 7 Y: H$ `' U6 k( A
  22. ;;; 作者信息
    7 x# p1 K: s# {/ d1 U, y( P
  23. (def-room about-room) I# [& |+ y' {0 C9 j
  24.   "作者:redraiment) s: d! F1 s1 f$ c& S, ^% \! z5 h
  25. 微博:http://weibo.com/redraiment
    0 e2 F# v( T/ K8 [0 y
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    # V$ H+ T# E6 d6 l* m+ g
  27. 请输入任意数字返回游戏大厅。"7 `5 _9 }3 W( T5 e
  28.   (t living-room))
    7 s' {- Q% i4 E1 W% Z
  29. 3 u8 S2 w& R6 w9 w* a8 d6 {+ P
  30. ;;; 教程
    ( [5 D5 n0 X& O& _# K3 S
  31. (defvar *wechat-5x5-tutorial-rooms* 0. }! B9 H- B& n6 a) h
  32.   "The number of tutorial rooms")
    * b$ I! T% k1 V+ a
  33. ( Q% T( d1 A% `$ N/ s- W; u
  34. ;;; 简化教程的定义
      G8 P- N7 G" p* Y" a
  35. (defun string-last-line (content)9 K/ g2 P6 m" t5 h& T& L
  36.   "多行内容组成的字符串中的最后一行"/ V" R1 N4 U) X
  37.   (with-temp-buffer
    ; a, w% Y* r! I& C0 E
  38.     (insert content)
    6 E) T% S1 L5 a* G- x
  39.     (buffer-substring (line-beginning-position)) b3 ?0 h) O! R6 F# s
  40.                       (point-max)))). o  d6 Z8 `# I9 w
  41. / `' |! G+ t: t2 z& _: [
  42. (defun def-tutorial-room (prompt)5 [  Z' d/ }. c, b4 |$ J
  43.   "根据提示语自动生成教程房间。+ }: G0 \) H0 a8 X
  44. . H/ h  P' Q' u- X2 ^
  45. 1. 提取最后一行作为问题;
    , S$ \5 C; ?7 ?# C) u+ ]! N# t
  46. 2. 分析问题,获取期望用户输入的内容;
    ! g1 |0 Z* R. [9 k( e0 A9 c
  47. 3. 定义教程房间和重复提问房间。"
    4 o8 v; J6 O# R7 q! S
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*))), t; r% K5 s. t! F1 f  H" J, q
  49.          (repeat-room (concat room-name "-repeat"))
    4 F' _# O, Y. k
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    + K. T0 i1 F7 P7 E7 {
  51.          (question (string-last-line prompt))# k/ Z6 Z2 ]4 d: n* I" A
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)1 q4 P% V* a1 r
  53.                      (match-string 1 question)))
    9 P! @7 h+ Q: L& i# H
  54.          (doors (if except' B9 A% W3 m3 @9 B; g( C
  55.                     `((,except ,(intern next-room)); d) T% n5 r* a0 w( W, I
  56.                       ("q" living-room)1 t2 z: w, A! p6 ]0 }
  57.                       ("Q" living-room)7 G- g  S4 [, e7 k6 ]9 `- u( |0 e
  58.                       (t ,(intern repeat-room)))5 H! A5 L" F+ P5 E' K3 i' s
  59.                   '((t living-room)))))* n* _' v% A  T; j& ~
  60.     (def-room-raw (intern room-name) prompt doors)
    2 V1 V3 p/ C, ]: Y8 ?+ @" f) e
  61.     (def-room-raw (intern repeat-room) question doors)))
    ; D' m$ Z& S6 o9 S- i; P

  62. / A" C8 O% r! P8 O5 G
  63. (defun def-tutorial (&rest prompts)+ H+ ]( ~. X+ e/ Y. L7 e6 n6 j( k
  64.   "批量生成教程房间。"
    ; P: g# O# ^4 g+ d1 k/ _
  65.   (dolist (prompt prompts)9 a0 S  _; @  `/ V
  66.     (def-tutorial-room prompt)))
    * L: Q4 P, _8 V2 d0 U& g  g4 R

  67. + l3 R. m& C4 u' e" E1 U& N
  68. (def-tutorial3 ]0 ?; {& X# e  l' F
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    ' m( z; e+ b1 U
  70. 1. 教程
    4 }+ D! ~% j( M2 W: n# y
  71. 2. 入门(3x3)
    ' p! u8 u* f% y4 p  t$ q; t
  72. 3. 初级(5x5)) Q$ E) v% ]5 h( Z( ^
  73. 4. 中级(7x7)
    2 F4 L* z+ {! g6 n# M+ W1 k/ U: z
  74. 5. 高级(9x9)$ s" T9 k+ K4 F9 b  z( [
  75. 0. 关于作者
    - E; F) Y4 F' t% L# H
  76. 请选择1-5开始新游戏:1 M# x0 r- }# M4 [8 I$ v5 h
  77. 您现在正在游戏大厅里。4 F' e. o% ~! q
  78. 请输入“2”进入入门级房间"3 |9 S& v5 G: B2 @
  79.   "  ①②③
    , {* [) H8 J4 Z5 `0 x, L
  80. 1 ■ " h" h$ {4 h% F. ^" R6 \5 |
  81. 2■■■
    5 Y6 w2 z6 @: f$ H
  82. 3 ■ 
    6 c# B; a* B' @# T0 K# N* ?( g
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!+ G: |8 @4 z5 d" q
  84. 请输入“22”来关闭第2行第2列的窗户。"! }/ x9 [# Z, q  j0 ]
  85.   "  ①②③
    : J* u! }% K+ \  N+ Q
  86. 1   , C; s/ `% q6 f  Z( i4 f& e! a
  87. 2   
    " O. m; I" v* }" F1 l
  88. 3   + N; L8 r' P- k% |  t! c) q) R
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    , V- @9 w& }+ C
  90. 请输入“11”,试着开启左上角的窗户。"4 v! s0 D1 [' ]3 ]: {6 }5 F# e7 B
  91.   "  ①②③
    4 ^( f" h# q) }5 R
  92. 1■■ ! _+ n+ n  d+ n% Y' ]/ D1 T
  93. 2■  
    # u* E* p# E; z5 \1 ?
  94. 3   , z; B! `; [, K& n+ n1 T  e
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    / l1 m, Z6 G0 h/ j0 L. c* {
  96. 请输入“13”开启右上角的窗户。"
    & i! H7 H9 v% d5 U" J% m
  97.   "  ①②③9 l5 F$ n$ C' O% L+ v% v8 V3 C
  98. 1■ ■
    ( O- [% N: d& }% N5 _
  99. 2■ ■- v6 f" y# X3 [" ]" t
  100. 3   
    4 R" i5 h) E9 ^0 _7 E( N
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。, z2 n8 v8 D1 v, m# Q
  102. 请输入“31”开启左下角的窗户。"
    4 g5 ~: u! X$ a) ^+ t
  103.   "  ①②③) u! H% y+ p0 c* n! Q
  104. 1■ ■0 a: d% d9 F% u' L. x$ y
  105. 2  ■
    9 H% H' o2 X1 ?2 @$ }! ^0 K( C/ z
  106. 3■■ 
    # E5 ?! {( S  l
  107. 此时,总共有5扇窗户被开启了。0 A2 s8 K. X% b$ l( ]* P% k6 {
  108. 请输入“33”开启右下角的窗户。"! |) Z( Q- Q7 s  D. V5 c' {8 `
  109.   "  ①②③
    # ^# E. m+ D8 C
  110. 1■ ■
    $ @6 k4 d! B' \7 I. z
  111. 2   0 f+ j5 O0 ^# M: y4 c7 H6 V
  112. 3■ ■
    " u) f+ O. n: V$ h6 E# @6 p' e# X
  113. 现在,只有四个角落的窗户被打开。# d! }0 }* O% c0 d3 V/ e
  114. 请输入“22”完成最后一击!"
    1 R7 o: y9 f  i: u8 L
  115.   "  ①②③
    6 Z( y  D' M) N8 q9 s
  116. 1■■■8 t! ?$ l8 I0 c- G7 @1 }
  117. 2■■■! O( V% b& W6 N- q# Q& F) S1 F
  118. 3■■■
    1 ]  [4 j( q7 O" b3 S
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!"). F5 C5 y. U/ E" s8 e% c3 N* @$ j5 A

  120. 3 j1 f+ o, @: _) C) u
  121. ;;; 棋盘
    * |' G1 V$ M! a& N( S
  122. (defconst *wechat-5x5-white-chess* 122881 K% j* |( U# |1 ?9 h9 ]
  123.   " "); x- V& T& [1 d& x0 ]
  124. ; x, u( Z! I6 X6 C9 A- s9 \$ M8 K
  125. (defconst *wechat-5x5-black-chess* 9632
    , B1 F) L5 \4 s% g! _0 i2 U0 t+ ^
  126.   "■")
    1 E% }: M0 V! i' W$ B

  127. " J9 F3 S8 |$ I; H7 q
  128. (defmacro with-board (&rest body)
    2 R* @; r" c' }* X! X5 j0 j
  129.   `(with-temp-buffer
    0 X' u9 c1 E& b* q5 O/ p
  130.      (unwind-protect
    4 L6 t& R; }) [, i" B/ j
  131.          (progn
    $ d: z& R; M1 `/ f; M2 I
  132.            (if (session "board")7 j. s* J! i# D7 F
  133.                (insert (session "board"))); G" c: r: _4 i9 J  c9 c
  134.            ,@body)
    " T5 @, @! U1 Q5 S1 }
  135.        (session "board" (buffer-string)))))
    2 R3 B: b3 t8 `' g$ K, N

  136. : m6 n  M% Q! Y4 F# h
  137. (defun board-init (size)& L1 ]8 {, q* \2 A1 F& \6 h) I! j/ V" `
  138.   (session "size" size)
    . B- Q2 B" s; |2 z
  139.   (session "step" 0)6 s+ \% S' Z/ y4 r3 f! d- Q
  140.   (erase-buffer)
    1 X( p. }6 O4 ~+ M
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    0 k  [# ^% e. ?( t
  142.   (dotimes (row size)
    6 N# D# r! |- ^% d
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))" g" \8 T! P3 k5 z0 O- a4 ^

  144. 9 Z" S7 p6 y' E, R# J
  145. (defun board-contains-p (y x)6 f9 i6 O/ E7 ~5 o, a; C' Z
  146.   (let ((size (session "size")))$ C$ C. I+ Y! ?1 Q6 v8 B) I1 }
  147.     (and (<= 1 y) (<= y size)
    , K  R( X& g5 o! }' w& e
  148.          (<= 1 x) (<= x size))))/ ]' @- K9 {5 z7 H" S

  149. ; D5 H+ L/ W! G3 R
  150. (defun board-toggle (y x)& ^$ O: u8 w3 D
  151.   (when (board-contains-p y x)
    3 I2 Y( d, Y# v" [: M+ s" x  O9 Q. b
  152.     (goto-line (1+ y))* y( n( M! ^; E
  153.     (beginning-of-line)+ X$ ^3 H: P5 D5 |- v; F! U/ J3 `& Y5 ^
  154.     (forward-char x)) ]# D: p; {  y; K9 V- z
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char)); @( l; S( ]# [- \" i
  156.                 *wechat-5x5-black-chess*% x/ H7 t2 T+ N0 H
  157.               *wechat-5x5-white-chess*))* e; W" @9 q. b/ g3 Q" s
  158.     (delete-char 1)))
      [- X- I! }3 q+ m& T+ X0 F
  159. 5 F0 a2 o2 `0 @, {/ i
  160. (defun board-put (y x)4 L" y* G" d( y- M/ y
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    ( L) d, S5 g/ G1 G+ T5 {
  162.     (board-toggle (+ y (first dir))
    ' l9 e8 t% M; A1 z: m
  163.                   (+ x (second dir))))): ^/ L3 C0 u* c: c( G6 `
  164. # p: w$ x3 P' [, {) s
  165. (defun game-over-p ()
    * }6 ]$ ]' H1 g; P% U
  166.   (beginning-of-buffer)
    & Y  u! w4 }) F' I7 F7 P3 s  M# N6 X
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))- I/ R, n: s' k2 y$ C

  168. : {7 L4 h( @# G6 Z& c2 [- c1 _: a- h
  169. (defun board-show ()" S5 R: g5 a( F2 ?3 ]
  170.   (with-board
    + u8 X3 C6 |$ Z  u: S
  171.    (concat (buffer-string)
    ) w0 u# @3 ]; `
  172.            (if (game-over-p)
    8 Y& y! k. P, M; c
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    ! z. S5 p$ j3 ]$ G3 e# ]6 _0 R" o
  174.             (format "第%d步" (1+ (session "step"))))))): k6 E* f- K( ~3 m: E1 D
  175. + U  k0 M! i, u* M" z
  176. (defun board-position-parse (cmd)
    ( G9 Q2 e7 t5 ~
  177.   (if (= (length cmd) 2)
    9 e3 y! ]% |& \: H0 E1 ]% j5 I
  178.       (list (string-to-int (substring cmd 0 1))
    # a8 j  {7 c7 U& e. V, i* l
  179.             (string-to-int (substring cmd 1 2)))
    / ~: z# v( m( {5 ?
  180.     '(0 0)))
    . H8 z2 W  ~$ V# V- p+ P/ d8 C( b
  181. 3 M6 B3 D" Z& {/ N5 o$ {& K
  182. ;;; 游戏房间
    ) B1 j- {" ^2 R9 e# S4 Y" E
  183. (defun game-room-init (cmd)
    ! a3 P4 I+ r; `9 X5 V! o) g  w/ y
  184.   (let* ((middle (string-to-int cmd))
    5 P( {# M( g  U
  185.          (size (1- (* 2 middle))))# j- p( _2 k5 F: Y
  186.     (with-board
    & \* W2 ~, j* G& ^
  187.      (board-init size)) E0 W7 R2 E* b' O
  188.      (board-put middle middle)))
    ) f2 S+ b, I& I: k
  189.   'game-room)
    # P7 e. z* u0 y; i9 Z/ K0 P

  190. ' `* Z8 Q( M' y" A
  191. (def-room game-room
    5 W' m9 }. P8 L0 X
  192.   #'board-show
    . p" ]+ d% y4 Z
  193.   (t (lambda (cmd)
    # h1 y/ |. X- ?9 L: |( }2 D
  194.          (with-board" n; `4 y9 K: U" }
  195.           (if (game-over-p)5 k& @4 e4 ^+ x+ Q) n9 I4 p% n
  196.               'living-room
    8 x0 ?# i6 o( }4 W7 p
  197.             (destructuring-bind (y x) (board-position-parse cmd)
      ?+ ^* {1 G2 H2 |% G
  198.               (when (board-contains-p y x)
    - r  x1 e: }  S: r
  199.                 (board-toggle y x)
    / d8 |. T  s( ]
  200.                 (session "step" (1+ (session "step"))))
    , R/ i: L: ]3 u6 ]8 l9 X
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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