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

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

  1. 2 `4 l0 Z# s1 y4 v  Q" |  _
  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 L- u) I" l6 d( o9 V/ K
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL. H4 R, a# D+ t4 v7 m( K' w5 m
  4.   'tutorial-room-0)                     ; 默认的入口
    + O' c* X4 d2 ~) ]- H
  5. ! R% v, Z* @2 Y- q; o
  6. ;;; 游戏大厅
    ) X' \, z/ g5 v/ i4 M
  7. (def-room living-room
    ! o' ?4 `2 _* {! V* p' {! I& f$ c3 y
  8.   ;; 进入该房间后的提示语6 v2 p5 Z/ P% Y" L$ ~& F" ?: R$ Y# H) C
  9.   "1. 教程
    8 n0 X& g9 C5 ~
  10. 2. 入门(3x3)
    " w. a% J% E1 u$ Q, v
  11. 3. 初级(5x5)0 M  v" Y- Z/ C+ Y1 d# Y2 y" V
  12. 4. 中级(7x7)8 W7 ?* H' R8 I
  13. 5. 高级(9x9)# \7 X- F! r, ]  }7 F
  14. 0. 关于作者
    ) T& l1 h: [/ L3 }. z* s+ q) S
  15. 请选择1-5开始新游戏:"1 M$ B0 @! N7 c. h  j
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名( y% J% U( r& n) c5 n( s
  17.   ("1" tutorial-room-0)+ H, n0 Y: x) h5 ?" D% f7 P5 V
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    # C% U0 ^6 {0 |( \3 f
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
      L- h7 T4 k3 V8 \' q7 a
  20.   (t living-room))                      ; 如果条件为t,为永真* D3 S; O4 g# c

  21. ! b; p2 P$ x& Z1 b" D
  22. ;;; 作者信息
    6 N( q6 z0 q: n; ~% }
  23. (def-room about-room: k# u2 `7 q; a
  24.   "作者:redraiment3 i1 ^2 p9 a9 v& H' V2 O/ \& Z; c
  25. 微博:http://weibo.com/redraiment4 O1 H' ?* B% M. A  ~! R
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    ( E6 m' k, [( v( D' g% l
  27. 请输入任意数字返回游戏大厅。"% |0 p4 b0 Y8 \9 |; J; A" W
  28.   (t living-room))4 E1 R: F8 w4 K2 V+ c8 l0 M( {

  29. 9 E* S2 s! s4 l4 W0 u$ u
  30. ;;; 教程1 V! c0 ^/ J! J8 s' \7 d
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    & X+ ^4 Q7 W0 V; z5 i& B* h, Q
  32.   "The number of tutorial rooms")0 c1 m- |# P8 h9 ~9 X  ~
  33. ) G' @9 z/ L- E4 A" l# ]
  34. ;;; 简化教程的定义
    7 ^7 Q5 }  _) O: y: I
  35. (defun string-last-line (content)
    1 q& V: X  u3 M" {# O
  36.   "多行内容组成的字符串中的最后一行") b; j+ Q# l: p8 ~" p, x3 G) `5 u
  37.   (with-temp-buffer
    $ {- q+ s$ T0 D6 Q& Z% X& `$ b
  38.     (insert content)
      [7 |3 C  T+ m5 r
  39.     (buffer-substring (line-beginning-position)+ H) w! x& S) h0 @5 p
  40.                       (point-max))))5 |  s; U5 G- i* o

  41. 6 c- `; f* Y+ D  B* m7 _
  42. (defun def-tutorial-room (prompt)! V9 q1 ?% R$ E5 H+ d2 x
  43.   "根据提示语自动生成教程房间。6 v. i# @# Q, W9 y

  44. ' k" m5 ]8 V' j) C
  45. 1. 提取最后一行作为问题;
    - r* Z2 m" U+ ^& h
  46. 2. 分析问题,获取期望用户输入的内容;6 R- v+ Z0 s, u6 {9 I4 Q
  47. 3. 定义教程房间和重复提问房间。"
    : J: |. W5 t$ C) z- W, b( D( o
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    ( t; {/ @2 U4 u: ^6 g5 U8 O
  49.          (repeat-room (concat room-name "-repeat"))
    0 \1 P% R) ~0 z4 t  n; w0 `) {
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    " q% a2 p3 m8 z; P8 c
  51.          (question (string-last-line prompt))
    ! s* J' i3 k1 B
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    # z# @: k  d1 d& |* S
  53.                      (match-string 1 question)))$ {+ ?6 c1 @5 [% [( q
  54.          (doors (if except
    0 H" q  I8 N% P) b
  55.                     `((,except ,(intern next-room))6 g. e5 Q0 I! ?2 r
  56.                       ("q" living-room); A; S2 ]3 v9 t5 h! ]2 O
  57.                       ("Q" living-room)  u' u4 h8 R# a) }) r' i8 n. Q; U
  58.                       (t ,(intern repeat-room)))9 y( E1 J. G4 M% x, j9 J" J
  59.                   '((t living-room)))))
    8 V$ a8 _  }) Q: |
  60.     (def-room-raw (intern room-name) prompt doors). a9 O  ^1 h; h* p
  61.     (def-room-raw (intern repeat-room) question doors)))
    2 s) c$ c3 I. T! A/ Y, A
  62. * h6 C6 a9 L! {, c0 K+ Y8 k
  63. (defun def-tutorial (&rest prompts)
    8 Y9 J# l' e' M
  64.   "批量生成教程房间。"
    , R. [9 y% d$ ~8 N) s/ g( D
  65.   (dolist (prompt prompts)
    ! {* t" f6 M+ r4 I* H
  66.     (def-tutorial-room prompt)))& U" n) R3 v- x7 D9 n; h% T

  67. - ^' _% m. b% A* y# C7 S
  68. (def-tutorial/ [% |& {% w" Z- Y" I3 B& \( |
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    7 Y: m9 Q) ]- [8 e3 [; Y4 O8 y% J
  70. 1. 教程
    " o- c7 [+ c% `; K5 @
  71. 2. 入门(3x3)
    , H* L$ l% M( O$ Q
  72. 3. 初级(5x5); E; U; r6 ^' V
  73. 4. 中级(7x7)
    0 T: e- z! c  O( ^
  74. 5. 高级(9x9)
    ( z- M% T  j- E- Z! |
  75. 0. 关于作者9 x) E7 m( ]) I6 ]+ u
  76. 请选择1-5开始新游戏:: Q% Q' Z4 Q' P$ ~4 X/ y" K( p
  77. 您现在正在游戏大厅里。
    ; a1 @/ a( ^, ^' x: r# I* M
  78. 请输入“2”进入入门级房间"
    * F8 Z; Q! B: {# D
  79.   "  ①②③* B% J3 O" |1 D, c+ u9 Q
  80. 1 ■ 
    / e* [; D# v% s7 T8 `2 I% L( {
  81. 2■■■; L! Y- k& D' d: |2 b3 L
  82. 3 ■ 
    " N. j$ u, N) ^7 p( n
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    / a/ v* l  o4 A( [+ U! G) Z
  84. 请输入“22”来关闭第2行第2列的窗户。"
    5 l+ \& ^" P" i4 G
  85.   "  ①②③
    ' _# x/ a& A! |( G- ?" y6 S
  86. 1   
    9 O0 X4 p: b5 L# r8 q! N
  87. 2   $ x/ I  _+ D, h' c8 {. l
  88. 3   
    4 `7 j/ `3 R' ^/ j- v) r! Z% D
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。+ s# q2 b) a. l" P4 J
  90. 请输入“11”,试着开启左上角的窗户。") I, v0 r+ Q9 U& B5 \) B% l
  91.   "  ①②③
    2 D4 U- d9 l( _" h
  92. 1■■ 
    & f9 k/ k9 j4 T
  93. 2■  8 `& T8 [) m& o5 Y2 |
  94. 3   . b  e. g4 R+ b& M2 y% Q* B* M
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。3 K  q7 G9 A0 b$ x/ N  j
  96. 请输入“13”开启右上角的窗户。"
    2 j( b1 W4 R/ I1 j: F! N
  97.   "  ①②③
    . f7 _( n2 H+ r$ q6 X7 N4 J9 I
  98. 1■ ■3 u, Y/ C6 a0 C* ^- ?
  99. 2■ ■' V5 ?" o* C7 m, G! K7 R7 p; R
  100. 3   9 T  K7 ]: U8 G
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。3 A; a" I$ v, \) ?
  102. 请输入“31”开启左下角的窗户。"5 a6 ?- q2 {+ o2 p/ Q! A
  103.   "  ①②③3 V. [* _+ Y" v4 h, t7 u
  104. 1■ ■* {& Y& O9 ]" a/ w& s- h
  105. 2  ■
    # h/ A  t- u0 `  t( [3 M, E
  106. 3■■ 
    5 B4 ?1 C; m+ g" v
  107. 此时,总共有5扇窗户被开启了。1 O! F# p1 z* F2 y
  108. 请输入“33”开启右下角的窗户。"- e: X5 }  t  v
  109.   "  ①②③1 M% o5 D$ u! ~5 E
  110. 1■ ■, i& S8 X8 N, c' C6 e+ F; v
  111. 2   ( |& z! i& @3 `5 t1 {1 R; f8 v+ f
  112. 3■ ■0 f- x) p: e% i* x  x
  113. 现在,只有四个角落的窗户被打开。
    ) L7 s6 H9 v: S( P* H
  114. 请输入“22”完成最后一击!"
    - c7 k% {% |! g- q/ B
  115.   "  ①②③
    " g' b0 V4 k0 T
  116. 1■■■8 E$ ]5 V8 M' S8 h# t* Q
  117. 2■■■
      m3 F/ V, g! Y& l8 N2 }
  118. 3■■■
    5 O2 @% v3 u+ C6 S6 Z
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    8 N4 d* M* l7 x6 `
  120. & p6 B$ w) m6 U- E& R! N
  121. ;;; 棋盘( @; N- j1 l  l
  122. (defconst *wechat-5x5-white-chess* 12288
      M7 V# E2 S, B
  123.   " ")5 \& b& ~; }. v  y( Q% b4 ]! v
  124. ( c2 Y4 U5 i5 b. e2 g" E" R  `
  125. (defconst *wechat-5x5-black-chess* 9632
    * Q* H" N* ]8 s
  126.   "■")
    0 e; @7 n( o" c4 W: j/ q5 n

  127. - z) s. b8 z7 h# C' d" o. F
  128. (defmacro with-board (&rest body)4 I+ i' C+ X2 h* R/ {% z/ I9 }9 e; r
  129.   `(with-temp-buffer9 a' V4 |4 p9 S5 I" j
  130.      (unwind-protect0 p1 V6 l& ^' `9 q. n" s6 C
  131.          (progn; b% b  p: w' B" k& W2 s5 q7 g
  132.            (if (session "board")6 U7 [  p, k- k: N  [2 y
  133.                (insert (session "board")))# Z3 ~& g4 V+ a4 u9 c3 n
  134.            ,@body)
    ( b$ h/ b! M6 R% c; e
  135.        (session "board" (buffer-string)))))
    7 _- e1 f# |# s1 ?3 U, u
  136. ) _; x/ N! N+ J7 s- G: E" F
  137. (defun board-init (size)
    / L" N  O  i: x3 V/ Q& `* t! d
  138.   (session "size" size)" U" I1 _: D# T3 r& j6 R
  139.   (session "step" 0). `8 K* |: d& L0 J! ^$ J& V& {
  140.   (erase-buffer)% R8 Z: Y0 t3 ~6 D
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))+ l: L1 \# c2 H9 b4 A
  142.   (dotimes (row size)9 a5 z) d' @5 b) Z: E
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))# r, g0 Z( I3 x% i0 c2 z1 b

  144. . x' h/ a6 y' A2 c
  145. (defun board-contains-p (y x)
    " i+ w' @9 l, f+ W" F/ Y+ H  e" {
  146.   (let ((size (session "size")))2 w: K; ^" L% g- U* o
  147.     (and (<= 1 y) (<= y size). ^" v3 Z2 G/ P4 ^
  148.          (<= 1 x) (<= x size))))
    ( m. }0 g3 }2 }1 E/ |

  149. 9 j/ `0 O3 x8 w
  150. (defun board-toggle (y x)
    ; C$ o5 E$ |% C! _3 A  q
  151.   (when (board-contains-p y x)  R& U1 I' x) }5 y
  152.     (goto-line (1+ y))
    " i5 \0 P& w% s* f& o! A. n
  153.     (beginning-of-line)
    ' ]1 w- w1 y% l& R' H6 s
  154.     (forward-char x)* m2 w1 j. \; b# Y$ C% p; _
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))! K( I8 b) G/ w! i
  156.                 *wechat-5x5-black-chess*6 t  W, D' z/ H
  157.               *wechat-5x5-white-chess*))
    - u( Q$ f8 k. t9 q
  158.     (delete-char 1)))
    6 [1 _: _# ~2 B3 u

  159.   u6 ?* h/ A% R; f+ k* k+ i# t
  160. (defun board-put (y x)+ K4 c) _5 q) `- u! z5 }, }' N
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    1 b) x3 V. J+ m
  162.     (board-toggle (+ y (first dir)), b2 B7 c, J3 k" u  z/ `
  163.                   (+ x (second dir)))))
    # Q" ^9 n/ p) v9 A* a4 A

  164. " d, K5 w* k" Z: ?) H$ \0 i2 k
  165. (defun game-over-p (): _/ F4 }7 h8 V* Q+ `7 X% c
  166.   (beginning-of-buffer)
    # B; g; ~0 g4 B3 ]
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    $ [& S1 I4 M6 ?) R' M8 y

  168. % D) [, u8 S9 c
  169. (defun board-show ()  g0 E7 C3 \' p/ U
  170.   (with-board; [6 p5 |8 E8 J' q% h! Z
  171.    (concat (buffer-string)
    . ]4 T7 \, O9 R: X) O% O$ _4 g
  172.            (if (game-over-p)
    % e) }4 G- H7 E4 x* T
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    . p; |$ n$ i7 H
  174.             (format "第%d步" (1+ (session "step")))))))
    # c  P$ h, p2 [

  175. 3 }" W7 n! R1 ]2 j3 |" r5 K9 O2 B
  176. (defun board-position-parse (cmd)
    + h  \0 t! C8 g% A/ D9 h6 X! i# N
  177.   (if (= (length cmd) 2)( `6 H1 K3 H/ W
  178.       (list (string-to-int (substring cmd 0 1))
    ( n% _  O8 _. A: S
  179.             (string-to-int (substring cmd 1 2)))) B( _! y4 s. w* N1 q$ m
  180.     '(0 0)))
    2 Z, |/ I' w: w+ H0 p% O3 F

  181. , n, N/ O% J  o0 w1 p- m
  182. ;;; 游戏房间
    6 v! u1 @/ @0 Y; c/ w: z0 h' a! |/ |
  183. (defun game-room-init (cmd)0 z) p' x  |9 I1 C- X8 h, w
  184.   (let* ((middle (string-to-int cmd))8 O/ R( S' d% D+ q+ D3 r# V
  185.          (size (1- (* 2 middle))))" x& w9 e: @; i$ i! E+ Z
  186.     (with-board
    2 H  ^' L9 C7 E( G
  187.      (board-init size)
    ( u( E+ v( ?. \/ M/ ~
  188.      (board-put middle middle)))
    ) d+ f8 K. m8 k2 N: _6 D
  189.   'game-room)
    3 e& C; U) w! U4 j
  190. . z$ K  w" t) u4 q( }; P
  191. (def-room game-room$ h& S% Y: P" N$ o, F
  192.   #'board-show
    6 e& [, F2 [. `
  193.   (t (lambda (cmd), g7 J6 f7 Z+ Z$ |4 m2 N
  194.          (with-board
    # Q! h% E$ q8 B
  195.           (if (game-over-p). M2 \0 X! }; v: n
  196.               'living-room1 M6 _; N" s& Y' k; Q% w" Q
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    , I% u! ?$ d  L; N
  198.               (when (board-contains-p y x)* p: i* u! J' z
  199.                 (board-toggle y x)0 Z) t% G3 X. t
  200.                 (session "step" (1+ (session "step"))))
    ( ~5 K- c" H7 @: [+ X: O& r$ T
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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