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

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

  1. 2 m/ M( a! }! h
  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 l4 e, C7 T1 C: d- @
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL7 d; s7 D/ z* ]$ U
  4.   'tutorial-room-0)                     ; 默认的入口# x0 t( m& m$ l/ W8 l: r' P

  5. ) T3 v1 Z" v, k  b9 W( F! N
  6. ;;; 游戏大厅
    4 I# V, V; G7 Y2 T
  7. (def-room living-room/ m0 ~' _0 c7 T3 V+ Q1 C
  8.   ;; 进入该房间后的提示语
    & j, w' B5 v, s+ T4 w( ?% D/ Y
  9.   "1. 教程
    , B6 M' P% R& n+ V0 Q" q
  10. 2. 入门(3x3)5 K( W' q9 h8 _" j7 R+ h8 \
  11. 3. 初级(5x5), t: V6 T2 n0 A" T, o
  12. 4. 中级(7x7)
    # o+ L) F5 o4 b' h6 L
  13. 5. 高级(9x9)6 z# @5 `0 D  q, |+ \# l
  14. 0. 关于作者
    ( `: ?! Y4 E! i/ |9 O/ `. `$ K
  15. 请选择1-5开始新游戏:"
    8 n5 h+ w3 n- I- O$ c; U
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名! h+ r( N( n1 H8 A& S" t
  17.   ("1" tutorial-room-0)
    ' e4 _8 M. v4 K% I" b  ]5 c
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    9 i# {& P/ u$ x+ m: Z0 D& Q/ X7 z
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    ; x9 ]9 k4 \. S& f0 |
  20.   (t living-room))                      ; 如果条件为t,为永真5 Q& x4 |6 V# o8 J4 o. N
  21. ; i4 x7 ~$ ]' X1 `* o: d% n. O3 {( z
  22. ;;; 作者信息
    9 \! E( @7 v' O( `/ n* C
  23. (def-room about-room
    6 f) q# e- x7 j6 k
  24.   "作者:redraiment+ I- x+ r) e4 f3 F5 P0 S
  25. 微博:http://weibo.com/redraiment
    / @2 U& X& v: z7 n% S, w
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    ) L9 d& H1 ?% K# N
  27. 请输入任意数字返回游戏大厅。"
    : h2 ]5 {3 S2 v3 s) Q) M/ e9 Y
  28.   (t living-room))
    - f3 {9 A0 Y+ Z: o" I

  29. ' q$ `* I) a- h2 ^* U9 B3 h
  30. ;;; 教程4 q$ f5 w! g8 w4 S$ R; J9 _
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    ! S% Q( ^- u6 u& O' z% F
  32.   "The number of tutorial rooms")  d0 i+ n) j( j% B4 ]1 V' t% i8 }

  33. & J* p! r* j0 k9 x( f& `% s
  34. ;;; 简化教程的定义
    - u4 O! K1 I& h
  35. (defun string-last-line (content)8 J8 Q& P8 o- ]/ H
  36.   "多行内容组成的字符串中的最后一行"
    4 @  ~0 A9 H2 G4 Q
  37.   (with-temp-buffer
    ; M0 b, W$ i+ ^5 X# s- y
  38.     (insert content)
    2 o/ W# n9 c0 d8 X# f' L! M
  39.     (buffer-substring (line-beginning-position)! r$ u3 M7 F; X; `  M
  40.                       (point-max))))
    + P1 B8 ~1 J; s4 M0 G

  41. . V# R1 k5 U8 g2 ], j+ Q% r
  42. (defun def-tutorial-room (prompt)8 P( J: X# B+ D: v/ W  R9 C, i5 N
  43.   "根据提示语自动生成教程房间。
    # Q9 J+ `$ [- V- ~

  44. ; d9 O1 Z; E; X+ L8 W7 D
  45. 1. 提取最后一行作为问题;7 |: x, r" a$ Q; [; z% N, W% |
  46. 2. 分析问题,获取期望用户输入的内容;
    # Q/ F: v) b( T/ g0 B7 F
  47. 3. 定义教程房间和重复提问房间。"
    * L0 j# M+ d( D4 y2 O' Z" E
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))' s; X# u) r7 q2 R+ Z" f1 ~6 j: W
  49.          (repeat-room (concat room-name "-repeat"))' R9 _) F0 X, Y* R& r8 k# n
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    / ]2 f# V! c7 Z) A9 d6 ^, s: J& m
  51.          (question (string-last-line prompt))
    ! b3 P# a/ _# N
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)/ c, s% T1 f2 D( q
  53.                      (match-string 1 question)))1 w  c4 S2 H8 _. V7 ]
  54.          (doors (if except
    ) j0 I4 x8 r4 g0 m' l1 C  c
  55.                     `((,except ,(intern next-room)); J* R# ~+ n1 q7 p8 {
  56.                       ("q" living-room)& d" d4 w9 v& k, K* \8 x
  57.                       ("Q" living-room)
    " X4 I. \# d  s; k7 ]( I
  58.                       (t ,(intern repeat-room)))
    : V( X1 x2 Z8 B9 m' r4 v) b: B6 D
  59.                   '((t living-room)))))
    ( w% M' Q3 @: F
  60.     (def-room-raw (intern room-name) prompt doors)
    " T4 J# u' M; x% l$ P$ D
  61.     (def-room-raw (intern repeat-room) question doors)))' K! l8 ^* I: h* s, [+ g# K
  62. 9 Z( O; W4 i1 ~/ o; S( h% P
  63. (defun def-tutorial (&rest prompts)
    9 R/ g' k3 F, O/ E
  64.   "批量生成教程房间。"; p3 `$ J: N- `: h$ G( o
  65.   (dolist (prompt prompts)( _2 k4 J2 ^5 w( Z6 Z
  66.     (def-tutorial-room prompt)))
    7 z, A; ~4 V6 J8 D+ t& ]4 o  t6 ?

  67.   c3 h1 }+ T( ~, q! Z- o
  68. (def-tutorial& M3 M8 E; I( V( z; ^% v0 ?% N
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    / Q' y7 W" G! c( O$ K
  70. 1. 教程
    3 U, M, @8 a# k* |- }
  71. 2. 入门(3x3)
    * L4 X2 h2 l  S: ^* P% \
  72. 3. 初级(5x5)* H4 y4 O3 p" O
  73. 4. 中级(7x7)
    6 @$ S0 t; F4 q, r
  74. 5. 高级(9x9): f. t1 F/ A, j! I6 h$ [, {' I
  75. 0. 关于作者0 v  }2 O' M. R6 P
  76. 请选择1-5开始新游戏:, w( ?7 k' n8 x2 i: e3 \
  77. 您现在正在游戏大厅里。5 Z5 e0 w) }+ r' t( n5 P5 ^
  78. 请输入“2”进入入门级房间") D" `* k. k; @5 ], d3 j
  79.   "  ①②③
    " o0 b; P2 \8 O8 r- \
  80. 1 ■ 
    " e! P4 g2 g8 ^! \  |
  81. 2■■■8 F1 W; g. C# u$ v9 v% d6 k  h
  82. 3 ■ 
    + a/ f' y, V. {
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!% T- R' u; m5 g; R) R$ n
  84. 请输入“22”来关闭第2行第2列的窗户。"
    8 s1 g! w0 \& q; F( c: |+ ^
  85.   "  ①②③
    , H/ U" }( I/ B" n# y/ i3 \. ~9 y+ D
  86. 1   ( ]4 o* X& i. b* l
  87. 2   4 d+ g5 v8 E* m! Z, U
  88. 3   
    3 ^8 N8 v7 \4 U+ t7 w/ m0 J
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。6 v* a! M( I# M" V/ X- \( m
  90. 请输入“11”,试着开启左上角的窗户。"8 H7 b2 ]1 B# d  N$ w6 _  Q5 t
  91.   "  ①②③
    9 J: t5 x& Q* A# F/ {, m
  92. 1■■ * O7 V3 i: g* C5 E  S' I
  93. 2■  
    : v9 m# P! \  t4 B* C. @0 T* c7 M* N
  94. 3     R7 B9 b& d0 G! Q* N
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    ) U0 d( ?3 s; D) t8 f
  96. 请输入“13”开启右上角的窗户。": t2 {; A4 b- \# w# q
  97.   "  ①②③
    7 A' j* k6 }  c# s. q, C: o3 w
  98. 1■ ■0 \) u, t# d9 L* s  w
  99. 2■ ■
    ! b+ J; a. p4 _' q
  100. 3   , _0 b! Q9 ^0 t
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    % J0 F; L# V" H3 ?! B/ P
  102. 请输入“31”开启左下角的窗户。"
    2 j4 N( |# }: c2 a+ \+ A
  103.   "  ①②③6 u/ @" T  e' {) K6 C+ z6 w9 F! j
  104. 1■ ■0 C4 K. `0 H% B5 U3 y2 s3 m( X' a
  105. 2  ■
    6 D2 _% O( W  z' l9 Q; M- `8 t* ~
  106. 3■■ % B+ A, s% s7 v# T$ {/ {
  107. 此时,总共有5扇窗户被开启了。2 k$ r6 x. h) K5 ^" r7 @
  108. 请输入“33”开启右下角的窗户。"
    0 [9 ~1 V3 _( ^& m
  109.   "  ①②③: L$ P+ g5 ^$ A+ w3 m
  110. 1■ ■/ v  ^" @* ^- Q1 v# M6 z+ T- ~+ P
  111. 2   
    $ d8 i4 r$ Q5 A2 t
  112. 3■ ■8 i' ~8 J% ^) e" N, n$ I
  113. 现在,只有四个角落的窗户被打开。
    - s7 }* x) ^3 B3 S2 y( o* y( v$ V
  114. 请输入“22”完成最后一击!". N6 Y7 V. I- z
  115.   "  ①②③9 _1 M2 M+ O* Q' y6 i
  116. 1■■■
    " {& Y( A4 \0 ^& ~" o3 _0 |
  117. 2■■■
    ' h* `9 t) R' B: }7 T
  118. 3■■■
    ) |4 V% @5 @/ b# R" J* L  Z
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")% Z  h6 w. G0 ~5 K

  120. ) m# u; }2 s; w5 l6 k, ~
  121. ;;; 棋盘9 w: C) k$ d) q. X. ^  q
  122. (defconst *wechat-5x5-white-chess* 12288
    ! ^, i5 q& m: i/ e8 \
  123.   " ")
    2 H4 h/ x$ I/ \

  124. 6 T; D5 P5 P, X2 N( c) T& j
  125. (defconst *wechat-5x5-black-chess* 9632
    2 B% ?7 P: C: f. Y9 H
  126.   "■")# i0 N8 t! b8 T$ O! g
  127. : d9 b" E" I$ e& m6 J4 k: T$ f
  128. (defmacro with-board (&rest body)
    ! w$ n& |. r9 Z* j% k' f
  129.   `(with-temp-buffer" ^1 E. w, N2 n' ^
  130.      (unwind-protect
    / N2 `, d' m% h$ f6 I0 N  h% ]; Y
  131.          (progn
    & h: p. x0 ]4 _
  132.            (if (session "board")% v' f' _4 v4 w% W; v# W* ]' @
  133.                (insert (session "board")))( |2 ]/ L+ F7 n% A; @
  134.            ,@body)4 h9 G% ]" f2 z4 P& Z  _
  135.        (session "board" (buffer-string)))))
    . F6 s$ A2 r9 V/ a9 X* o: @- @4 L
  136. - o8 J) I2 i1 x! x
  137. (defun board-init (size)
    " u* d& B- h. o$ M: y7 S
  138.   (session "size" size)
    9 u8 r# v* z' _+ N* n* p  s* M2 R2 s1 b
  139.   (session "step" 0)7 V8 V+ Y% a+ D8 `; N
  140.   (erase-buffer)
    ) _8 ?6 L' c0 Z% _9 D; F/ H
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    + b4 r0 O3 `1 W6 I" Z& k
  142.   (dotimes (row size)
    * N" }: ^- ?- B5 n( k  V8 L
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    * q- ?8 i9 v' `& \: Y

  144. ( {9 Q; X! K( `) Y: z. W
  145. (defun board-contains-p (y x)
    8 j$ V. Z6 }; @5 Y
  146.   (let ((size (session "size")))# p2 Z+ j! h4 O% _3 `3 c
  147.     (and (<= 1 y) (<= y size)2 J& {( p8 q  c/ j& W
  148.          (<= 1 x) (<= x size)))), U1 L! M; p& |) j3 G2 Y# e8 R" u
  149. ) ?  [% J3 {* J) _+ b" y( t
  150. (defun board-toggle (y x)( P7 y0 @+ d. I0 j
  151.   (when (board-contains-p y x)
    7 h: x9 {- {4 z
  152.     (goto-line (1+ y))
    2 M" k6 E: q, B3 N: j  A" V
  153.     (beginning-of-line)
    , N5 X8 B& \3 I" r$ j
  154.     (forward-char x)
    5 R6 V6 N: _! }5 j0 h
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    , _" X. s" x, ^$ U; n+ M
  156.                 *wechat-5x5-black-chess** N) ]8 E- g9 ]6 ~( g5 Q
  157.               *wechat-5x5-white-chess*))
    " j/ `! ^/ D% D0 y
  158.     (delete-char 1)))
    4 ?6 p+ Y# E2 c' [2 R+ Q

  159. , T8 B& i  ?- o" a$ W
  160. (defun board-put (y x)8 C& _# `5 P# O; X
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))# n+ @1 F: t# @& H5 Z
  162.     (board-toggle (+ y (first dir))
    % a0 R+ W) Q6 `4 P0 ^
  163.                   (+ x (second dir)))))
      q+ L& I$ M6 M# U
  164. & u! ]: `7 C  ^
  165. (defun game-over-p ()
    ! }" S  I6 V) @3 X
  166.   (beginning-of-buffer)& y, |0 r/ E' A* U1 v3 u
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t))). M$ `4 z) n6 a8 x

  168. 7 D, |, g. d% }! e6 C, L
  169. (defun board-show ()
    $ |3 E# `  E! q: k
  170.   (with-board
    8 x3 H, o' k. U5 h
  171.    (concat (buffer-string)
    & F6 c: ^- o# C& M
  172.            (if (game-over-p)
    ) D0 K" p9 i5 O, z/ g) T% n
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    8 }2 @5 i& ]  y0 r: f" B; A6 |
  174.             (format "第%d步" (1+ (session "step")))))))
    : t4 y" o- E, d! ?% X. R, t

  175. 3 N' G6 J+ R/ Q  ^$ @$ a( W2 U
  176. (defun board-position-parse (cmd)( p, m. ^; U$ z2 z) h2 G" a
  177.   (if (= (length cmd) 2), X: j8 R, A5 D0 f# v
  178.       (list (string-to-int (substring cmd 0 1)), K1 g) I( M0 L/ C7 ]; ]- q6 v
  179.             (string-to-int (substring cmd 1 2)))
    * d5 T  P, l% B  H! s) }$ v/ L
  180.     '(0 0)))' G2 I* \* k1 v: i% V7 {
  181. # f$ o/ f8 w& k( ]) j
  182. ;;; 游戏房间
    8 c) e0 a& k, U, P, _
  183. (defun game-room-init (cmd)
    * C; s9 H  p/ a3 s7 G
  184.   (let* ((middle (string-to-int cmd))% ]; Q$ K4 p, O. T. }. \
  185.          (size (1- (* 2 middle))))
    / V& {( B# V  i% o3 f6 X- o- H
  186.     (with-board
    ( y1 S" M9 g5 d% R4 }
  187.      (board-init size)+ _: e5 L) s* V8 ^0 B  R
  188.      (board-put middle middle)))
    * ]- M) T  y) @0 x' e$ ^
  189.   'game-room)3 ?/ v. V! ~# p4 J: s

  190. 6 D# J5 _2 c) t4 I' n; x
  191. (def-room game-room; \, {, p7 ^3 ]+ Y) ^2 F4 y( h. |4 X
  192.   #'board-show
    , @& o! D% N0 f) V/ M  p
  193.   (t (lambda (cmd)
    8 |, s+ h8 C* @7 U
  194.          (with-board
    - d5 f; J  C0 ]$ @0 E' J
  195.           (if (game-over-p)
    / P% p# |! V: v' o/ y
  196.               'living-room2 J! F+ j* _8 C: M
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    8 J6 l/ t1 H2 C! H
  198.               (when (board-contains-p y x)
    1 l$ V! ~6 @1 r+ p5 x
  199.                 (board-toggle y x)
    2 s& M6 D4 B4 g' L* [  g6 t! u
  200.                 (session "step" (1+ (session "step"))))7 l, m% M' j2 x0 I
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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