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

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

  1. 0 [! B/ Q; Q% b4 \5 c2 @- M
  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;">;; 定义新的游戏地图
      d# Q- V2 m2 X% D+ H4 F
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    , k' T' W+ o6 Z) d3 @6 G
  4.   'tutorial-room-0)                     ; 默认的入口* _; |/ i" Q2 S, w. ~/ K1 Q- b

  5. - S/ }5 [& A  }8 K- P
  6. ;;; 游戏大厅
    : \; h$ i  J* Y
  7. (def-room living-room+ I. A& l; i+ j) P& y' V
  8.   ;; 进入该房间后的提示语* P4 W6 b- m5 a  j: _$ }& {5 Y$ k
  9.   "1. 教程
    0 f" O9 o3 X2 [
  10. 2. 入门(3x3)
    # u, h1 _+ ~5 w9 x
  11. 3. 初级(5x5)
    . U+ A/ p& V2 z# }6 @$ b
  12. 4. 中级(7x7)& \0 c. p2 I+ s0 k) X
  13. 5. 高级(9x9)! J2 ^& g* p( j# X; F
  14. 0. 关于作者
    ' r7 Z( M( @/ U1 E6 {
  15. 请选择1-5开始新游戏:"
    4 w( s& l/ W9 `& d! h
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名; r+ W  R1 A- _- T+ Y
  17.   ("1" tutorial-room-0)( o' c* i" Z$ ]- y2 f
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    1 M5 Q* T, U+ L+ e: I0 b7 L! T
  19.                                         ; 相应的返回也可以为函数,动态返回房间名. X, T6 M( h3 R
  20.   (t living-room))                      ; 如果条件为t,为永真
    ' D: {, S8 I  ~# P- {/ l  U' S
  21. ; y" ~( t, h* [$ Z) ?0 Q; n: X6 `
  22. ;;; 作者信息
    1 @' G0 u7 T( N0 K, c% h  u
  23. (def-room about-room
    - r; T' a1 e% C7 n. l( ?1 h
  24.   "作者:redraiment
    / Z+ W* A, ~/ C4 N
  25. 微博:http://weibo.com/redraiment
    5 g) h; j3 |7 t2 M
  26. 有任何建议,欢迎在微博或微信上联系redraiment。4 O- e2 J9 l  `$ x
  27. 请输入任意数字返回游戏大厅。"
      f" r0 F1 G7 p/ w0 U: i; y
  28.   (t living-room))+ b3 j6 ^% ^" a# z9 Z3 o& D

  29. 6 V/ J4 C, g; D% f3 p
  30. ;;; 教程
    & @6 d- i% T$ F& m2 B2 l) X" t
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    : Z5 t! c  l, v: a! _
  32.   "The number of tutorial rooms")+ W. H8 c1 d$ h+ y/ x- g

  33. % W2 l: @: n5 p  E( N: V
  34. ;;; 简化教程的定义7 Z% c, e' z4 b# }5 c1 w
  35. (defun string-last-line (content)
    0 S8 v  ^( c! j2 o
  36.   "多行内容组成的字符串中的最后一行"
    2 B$ Q/ M( A) H, G0 }
  37.   (with-temp-buffer
    . i5 \4 v+ v3 P8 r3 [. f
  38.     (insert content)4 d1 E" a% J7 `0 {1 |) ^# b% |- m
  39.     (buffer-substring (line-beginning-position)6 k% J' s. T7 g. L$ H: X5 T
  40.                       (point-max))))
    * q; Q, G1 }# `  \$ y: b" G0 m
  41. & q* c3 [. f: M
  42. (defun def-tutorial-room (prompt)' B2 R9 y! N& l2 p! ?* {0 C
  43.   "根据提示语自动生成教程房间。
    ; z8 I& v4 U( Q' X, p/ y6 I9 F
  44. 1 D, O5 j+ G; S/ f) z" W
  45. 1. 提取最后一行作为问题;
      D6 I  i; M: \# q0 l
  46. 2. 分析问题,获取期望用户输入的内容;7 z  q  h& P1 e( u! X/ V( h) Q
  47. 3. 定义教程房间和重复提问房间。"
    5 Q( C' S' k, n
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))6 X) {) [3 E+ g8 E# Y0 _" m
  49.          (repeat-room (concat room-name "-repeat"))
    - \$ [4 ?* j4 s8 b  {2 [
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    ( t0 U4 K+ ?& g4 e% U; O) V
  51.          (question (string-last-line prompt))
    8 J4 K6 J# e$ i5 \6 F' Q
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    " S% E" S" S& n* ^, f
  53.                      (match-string 1 question)))
    7 p( D- c  {8 u: R, s1 _+ s5 @; q& W
  54.          (doors (if except) {; N) P1 G, J8 E9 i+ Z
  55.                     `((,except ,(intern next-room))
    - O3 R# \; f8 v" n
  56.                       ("q" living-room)
    & ?/ ?! I! H$ f. {3 q
  57.                       ("Q" living-room)
    " I% c: [6 d! r7 e; L  n
  58.                       (t ,(intern repeat-room)))
    2 t  m& V1 j1 Q
  59.                   '((t living-room)))))
    ) ?3 v$ k) g! ~, x2 |
  60.     (def-room-raw (intern room-name) prompt doors); K) T' K3 E! Q' V  V
  61.     (def-room-raw (intern repeat-room) question doors)))
    2 ^  N$ c& g( X, a( ~* z, M
  62. # M' E+ j+ T/ {* r9 [( S4 P
  63. (defun def-tutorial (&rest prompts)( _' A% W: d% R2 u  ~) Z8 U" g: I
  64.   "批量生成教程房间。"
    - P% ^) C6 }/ D3 r9 K1 h6 q
  65.   (dolist (prompt prompts)
    - J0 I' P8 ?" L9 t
  66.     (def-tutorial-room prompt)))$ l: e- d& [6 L6 C6 Q
  67. ) S% i* w7 ]  G7 x7 F3 i
  68. (def-tutorial- O; B: C, E% M
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。" ^+ |' w, N1 K7 }3 e6 f7 J
  70. 1. 教程
    & j2 q7 o" ~3 K/ @
  71. 2. 入门(3x3)
    8 D4 e/ N" m' ?. {
  72. 3. 初级(5x5)' ?; C/ j# r/ @- |! ]; J6 i7 Q2 R
  73. 4. 中级(7x7)
    : n5 n% H, s0 b$ _" _/ |! F3 e6 O
  74. 5. 高级(9x9)
      U8 Y  ]( c+ a
  75. 0. 关于作者
    + ^* ~3 h% e/ I
  76. 请选择1-5开始新游戏:
    + S: m% T$ G0 t) T2 A6 I6 ]* X
  77. 您现在正在游戏大厅里。
    ) E+ G! C- L5 o2 }6 y4 |# a
  78. 请输入“2”进入入门级房间"
    6 B; p1 r! l* {
  79.   "  ①②③( h- n9 _! N: T  c. w4 H
  80. 1 ■ & W4 Z* `, w* S! R
  81. 2■■■
    0 ~/ D& T5 D" e+ T
  82. 3 ■ 
    2 B* z1 l  o4 }/ T+ m
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    , w: G: K9 |& v& k4 [. Y+ S5 P
  84. 请输入“22”来关闭第2行第2列的窗户。"4 x2 H3 f- l) E# K
  85.   "  ①②③
    6 L3 t7 y/ @( y" S
  86. 1   
    ( _" P: X. e8 W) B! o
  87. 2   6 g* b' c4 g' v7 w2 P
  88. 3   
    9 y) |& c4 L1 Y' T
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
      K' D% ~* Z9 N! {
  90. 请输入“11”,试着开启左上角的窗户。"
    7 m0 P- J' T1 @3 {- a; e4 f
  91.   "  ①②③( `5 G6 ^3 i+ P0 h  z
  92. 1■■ 
    ! T; R% Q4 M7 X5 d" J
  93. 2■  
    ) G. H3 m2 D7 S
  94. 3   
    8 U& B; R* @' d+ y
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。. u& ?* f: H9 b& s. c* n
  96. 请输入“13”开启右上角的窗户。"
    + ]* F* {! Y1 j1 _$ x0 L/ O
  97.   "  ①②③
    3 @4 Z% Z$ ]  U" \) y
  98. 1■ ■
    3 r5 M) T0 }9 i/ y- K
  99. 2■ ■
    / L0 J4 P, v/ V9 ]' L0 D
  100. 3   " i" E3 b  k# Z7 I
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。, _2 x4 b: d( Q
  102. 请输入“31”开启左下角的窗户。"
    & T: e7 Q  i, Z; c! o5 Y, S
  103.   "  ①②③3 Z% E3 A5 ]( Y, O! q# S
  104. 1■ ■" @5 s9 r2 N! G/ b
  105. 2  ■
    5 a7 U$ g6 D: g" N1 r; E3 B
  106. 3■■ 
    ! Z6 e% Z7 a# A' m4 L
  107. 此时,总共有5扇窗户被开启了。$ J+ z. |% {1 M  D* `0 m% X" n0 g  L
  108. 请输入“33”开启右下角的窗户。"
    , \3 ?& }  b% e
  109.   "  ①②③
      H5 B- J" t% l3 p0 _  s  H& P/ p
  110. 1■ ■
    , l; _, ~* j/ i* n6 j
  111. 2   
    6 C. }) T9 f6 k$ B
  112. 3■ ■
    ( W6 P2 n* ?3 m, V7 Q0 h
  113. 现在,只有四个角落的窗户被打开。
    , K1 \# i: m( Q& b
  114. 请输入“22”完成最后一击!"
    ( h5 t! ?8 T  B' g' \. Y
  115.   "  ①②③& H! i2 k, j+ y; m
  116. 1■■■
    * y4 ^2 i4 _1 N# N$ U7 [: r9 A$ I) F
  117. 2■■■
    4 S" e" K7 e$ E% l
  118. 3■■■3 @3 k# k! \! d) ^4 c
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")7 [# B) _( z1 Q: u1 x
  120. 3 N% Y2 l8 w' l) Z6 b2 p" _
  121. ;;; 棋盘9 y) m/ {* g8 o9 U! |7 y( S1 k
  122. (defconst *wechat-5x5-white-chess* 12288* ^7 ]9 a! ]7 u) I0 }# c9 A
  123.   " ")
    5 g  g% A/ E! F" u6 _( |1 ^7 u' n- d
  124. 7 |! _  t* f; T! P1 M1 E/ x
  125. (defconst *wechat-5x5-black-chess* 9632
    " O4 J1 J) r3 S: L! j) P$ f, }4 b
  126.   "■")* b& m1 D' ?2 }: l9 p/ H7 }

  127. ! R6 ]  Z8 T8 J- l' o; W
  128. (defmacro with-board (&rest body)0 E2 d5 I& |) ^9 \& ?: J
  129.   `(with-temp-buffer# V5 ?( ^& [1 |( J' Y
  130.      (unwind-protect
    " a( y  P0 n$ q4 b* N, n
  131.          (progn' L+ o7 Q6 ~8 m/ q; V+ T" R) K
  132.            (if (session "board")- g& ~$ A" u0 m' z% R
  133.                (insert (session "board")))
    " }) y& s3 H+ x1 j
  134.            ,@body)* A. @6 }7 M6 u0 \3 j; I/ W
  135.        (session "board" (buffer-string)))))! t1 N, M- e5 p' D7 T* f
  136. . X& P3 V+ Q- Z4 m( m6 _
  137. (defun board-init (size)8 E/ w+ ~; T! C  n+ C# N
  138.   (session "size" size)" x7 d, _1 ?: {1 {9 V
  139.   (session "step" 0)
    , X5 ]5 q  Q( t1 M
  140.   (erase-buffer)
    7 A; f1 @! o/ e8 }
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    , N( j4 L& F* d9 I$ V- @4 p
  142.   (dotimes (row size)" z4 R9 P3 |3 F# b4 t7 B6 M
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*))))). m" W) ]+ V# B7 W. {% u5 I5 j

  144. 3 W+ H& p; G! v3 m: P% J
  145. (defun board-contains-p (y x)
    0 ^1 N- I8 g0 E2 F4 S- r1 h! z
  146.   (let ((size (session "size")))
    3 Q, h) ^% v8 |  [! L& A* `
  147.     (and (<= 1 y) (<= y size)
    - K2 P1 S: f( g9 j* {6 Z1 f
  148.          (<= 1 x) (<= x size))))
    " d2 n$ O1 J7 p' n
  149. 8 V* P% ], B3 z, @
  150. (defun board-toggle (y x)8 e1 f* I( K, b
  151.   (when (board-contains-p y x)+ e8 |$ R% m; E9 v  h5 k
  152.     (goto-line (1+ y))
    $ B  w- ?( \9 K# c  ]* [
  153.     (beginning-of-line)
      d/ N3 N& S( P4 M- q3 \# {
  154.     (forward-char x)
    " M% t0 ?6 W: c; b: X
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    , C  O9 {0 D6 W% ~* @
  156.                 *wechat-5x5-black-chess*) w1 b! w) C8 O
  157.               *wechat-5x5-white-chess*)), h1 b8 C) f; @  [+ j) u+ z
  158.     (delete-char 1)))) {% @7 C1 Q5 N8 @# M+ u6 R: ~

  159.   e; S7 [' E: s  Y: x
  160. (defun board-put (y x)
    1 j* l+ d: p/ v2 K
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    & o6 d( z* E0 `5 K3 ?5 l$ p
  162.     (board-toggle (+ y (first dir)). ?* y0 c( A* t' W' U
  163.                   (+ x (second dir)))))4 y% p5 G0 b) y% K6 L" }% L* X

  164. 9 I# X* r4 c; f7 m4 N/ }9 N) y' [
  165. (defun game-over-p ()
    2 h, |, Y( t2 r5 h1 T$ a
  166.   (beginning-of-buffer)/ g$ V0 U6 {- _# }, {7 p2 Z
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))$ n# e, g. k% y+ Y
  168. 8 t# g+ E4 P4 g6 c4 H/ h! l
  169. (defun board-show (): u9 T$ H4 p& e. w
  170.   (with-board
    ( ?% Y! U' Z1 ^
  171.    (concat (buffer-string)
    2 L9 R- A+ S' Z( D9 S  @5 _
  172.            (if (game-over-p)# X6 Z; ~; @( u, y# M6 @
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))& v5 W, C: T4 G4 r0 n$ `9 Y0 C' V6 R
  174.             (format "第%d步" (1+ (session "step")))))))8 ^1 i. Z% w& o2 h4 X

  175. 7 X* O! X6 J! _/ g* L- l( O$ h$ u0 T
  176. (defun board-position-parse (cmd)
    - C  J2 c" p( i4 M. o6 W6 f& G  p' v
  177.   (if (= (length cmd) 2)/ z# [& V  J5 |3 Z" Y
  178.       (list (string-to-int (substring cmd 0 1))
    2 L- f! O+ n8 ]8 D
  179.             (string-to-int (substring cmd 1 2)))
    " G% }3 y' t1 m3 _7 N: }- S
  180.     '(0 0)))
    - E& O# l9 O( Q5 l/ y8 w( n

  181. 5 p9 t# l! k2 X( F
  182. ;;; 游戏房间
    ; t1 c! x* Y7 X9 _9 |
  183. (defun game-room-init (cmd)9 _0 s6 v: v* H9 I) ~; i
  184.   (let* ((middle (string-to-int cmd))
    ( Z# Y$ B/ D" h8 d
  185.          (size (1- (* 2 middle))))2 K% f" ~7 S: {4 Q# \" B: m
  186.     (with-board# B7 `1 z! I, r8 K
  187.      (board-init size)
    9 k* v; x9 i& I( O" Y: I1 B
  188.      (board-put middle middle)))
    9 W9 j* p) G) W$ A
  189.   'game-room)2 Z3 Y6 F- o4 R
  190. : U1 W* B$ {' N/ P: K; X4 j4 b
  191. (def-room game-room  A' v" y* T# v- W' `2 W
  192.   #'board-show5 ?1 o6 U+ P& Y$ r( ^
  193.   (t (lambda (cmd)
    # D+ `" Z" A. o% I6 b
  194.          (with-board4 _8 L( S3 q+ U+ b$ J$ w
  195.           (if (game-over-p)
    ( u5 a7 |. G- B3 ~( `% k: ]+ ~
  196.               'living-room/ i& J  G& R) q* D8 c9 d" u
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    % L- h. ^1 \0 _5 s
  198.               (when (board-contains-p y x)9 O  g! ]$ D0 \- e
  199.                 (board-toggle y x)
    - r) _3 s8 `6 K, j, q
  200.                 (session "step" (1+ (session "step"))))( e3 @0 @' k% E6 Q' g6 `' w3 n4 i
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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