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

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

  1. 1 c* K8 N9 z8 ~) ?. a6 a  [
  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;">;; 定义新的游戏地图
    ' |1 p) d6 ^( t
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL% X# T; ]1 j# R4 {4 T& f
  4.   'tutorial-room-0)                     ; 默认的入口0 y4 q7 u* p; G; m7 S* g

  5. 9 _& |+ r5 ?$ \: |* H- T
  6. ;;; 游戏大厅0 C" G7 l0 U& n/ ?0 i* p3 g/ L
  7. (def-room living-room  W& y* i$ F  A, G" B
  8.   ;; 进入该房间后的提示语0 G  [( c: @9 z1 O
  9.   "1. 教程1 F& E, s/ N/ m* }, y
  10. 2. 入门(3x3)
    1 M, Y# B+ R( O1 E9 K
  11. 3. 初级(5x5)
    - J7 w& F0 D$ D" W  ?* k8 \
  12. 4. 中级(7x7): o5 D5 D' T( o( f- H
  13. 5. 高级(9x9)" k% H2 V( N1 h5 O6 n
  14. 0. 关于作者
      x: H4 K* Z6 w# y$ h- v5 B4 w
  15. 请选择1-5开始新游戏:"- ]0 b7 z1 b0 P, Q! y% W2 O
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名/ \. t" C2 A6 _& g' L7 \
  17.   ("1" tutorial-room-0)& V% j% \/ B# C% H
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配: s. y5 Q6 |. B3 P/ W) ?2 g
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    . C! h5 ?( }1 N
  20.   (t living-room))                      ; 如果条件为t,为永真% V+ r' R6 U6 m' M

  21. ! K7 S0 r% L& S5 E6 a5 f) T
  22. ;;; 作者信息
    & \9 k4 T, j6 K5 z
  23. (def-room about-room
    ( x* Y/ M: Y: D8 F) e: A
  24.   "作者:redraiment
    5 u/ M: V- ]+ Y) ~0 f5 H( w
  25. 微博:http://weibo.com/redraiment  H9 s  t* L+ _/ v  i/ t
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    1 V; J0 ~( a9 Y! Z% o
  27. 请输入任意数字返回游戏大厅。"
    & f+ ?0 Q7 O' o/ k  D
  28.   (t living-room))
    % J# B1 W/ g' e

  29. . H. F& R" Y( F* d, F3 x: a
  30. ;;; 教程: \/ T1 p) U1 Y' P( W( I
  31. (defvar *wechat-5x5-tutorial-rooms* 0/ O$ r/ q; c6 b* z! O, Z
  32.   "The number of tutorial rooms")
    # m2 T8 }3 a$ N2 V
  33. 2 w) k& E" a( ~! C. k
  34. ;;; 简化教程的定义
    ' v& q/ _4 f$ W! {
  35. (defun string-last-line (content)6 V' M  u. K/ F' G% V
  36.   "多行内容组成的字符串中的最后一行"8 u& U0 [/ @) K$ K4 o1 t
  37.   (with-temp-buffer
    5 g: `) P+ U2 D  y8 u3 F
  38.     (insert content)
    / Y. V9 Z/ e( g- ~' a
  39.     (buffer-substring (line-beginning-position)
    ) S5 x) m8 e7 m# c
  40.                       (point-max))))* y6 ?% F+ ]6 f2 b, W, I) b

  41. 1 x, p6 G, E& u% d5 S
  42. (defun def-tutorial-room (prompt)- M' b# i, @  l3 Z
  43.   "根据提示语自动生成教程房间。
    ! L  V+ O3 q! s- v/ v

  44. % B/ _8 U( M: S) G: P
  45. 1. 提取最后一行作为问题;
    ( `. k" k# `8 N3 H" \1 Z$ `! x
  46. 2. 分析问题,获取期望用户输入的内容;
    3 a7 ?: _- Z' i6 h6 C/ v
  47. 3. 定义教程房间和重复提问房间。"6 [6 B1 x( _. i+ ?
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    9 b* p* G0 ~6 a$ x0 R
  49.          (repeat-room (concat room-name "-repeat"))
    " A! F( a( _; l' P
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    % c: v8 ^, h$ N+ _
  51.          (question (string-last-line prompt))
    % b) w9 [' v! z, m
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)6 j; ~/ Y. c: f1 {2 k
  53.                      (match-string 1 question)))- j3 z3 u3 s' Y3 v. ]# U$ ]
  54.          (doors (if except
    # Z, b* p1 R* G
  55.                     `((,except ,(intern next-room))& E9 n) V4 Z  [% ~1 }
  56.                       ("q" living-room)
    1 V' _$ T. F! B# ]8 {9 A' r- ~; ^
  57.                       ("Q" living-room)4 a& _) v( X  M
  58.                       (t ,(intern repeat-room)))
    $ _- K( |& Y# t+ K8 G4 r
  59.                   '((t living-room)))))
    & x( f. O, l* |
  60.     (def-room-raw (intern room-name) prompt doors)
    9 X& Q1 W) d6 p. u
  61.     (def-room-raw (intern repeat-room) question doors)))6 _9 T- S$ U/ s3 }0 K5 p

  62. 9 T; I% _* o- K- z0 Q  s* p" O- C
  63. (defun def-tutorial (&rest prompts)
    1 C. Y" ]% I  f1 i
  64.   "批量生成教程房间。"3 v& \/ L) Y% U, K: A
  65.   (dolist (prompt prompts)
    3 ~( U. B7 c3 r: a/ Y( X
  66.     (def-tutorial-room prompt)))
    7 D. H$ ?% W; |' W
  67. . }( x, P6 v2 [  x% E
  68. (def-tutorial
    # q9 V( R& F7 m) u7 r. H8 E8 v3 Z/ b/ o
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。5 r5 ?7 W) b9 q! t
  70. 1. 教程
    + W' \/ ?6 I9 b; R
  71. 2. 入门(3x3)# S3 w7 r; }+ X/ m1 Q1 Q6 R
  72. 3. 初级(5x5)/ \' g- w2 R  A, @
  73. 4. 中级(7x7)
    ! h) t3 C& o$ A+ A9 ]
  74. 5. 高级(9x9)
    6 t6 W; V" X7 B" @! E+ q) N
  75. 0. 关于作者! A  R" |/ c  d; @; y6 ]3 R
  76. 请选择1-5开始新游戏:
    9 `' s8 q; g9 b. K, E3 @& k
  77. 您现在正在游戏大厅里。
    7 d( p2 D$ J) I, V) p' x: J4 c
  78. 请输入“2”进入入门级房间"2 A8 ^! j7 U; R5 S, c/ D
  79.   "  ①②③9 o( c/ ?+ c0 M$ t; C6 J) d
  80. 1 ■ ) N/ c' W& g) ]  @6 ]+ O2 o' n
  81. 2■■■! T" H( A# g! h
  82. 3 ■ 
    ) X& i: B' G) \
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!8 b3 o/ v% i2 k. \
  84. 请输入“22”来关闭第2行第2列的窗户。"9 f8 n. @" j" Q$ F+ `
  85.   "  ①②③! s9 D2 Q; N6 Y
  86. 1   # a& ?: f4 z2 M3 g7 r7 l# Z
  87. 2   # w$ B0 N' l/ w
  88. 3   
    + |6 M' X& p7 S6 D/ U7 }
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。: P2 C" T* @3 D. |3 ]8 d
  90. 请输入“11”,试着开启左上角的窗户。"
    " Q0 Y3 W& [! O2 q! c
  91.   "  ①②③" f5 m' H- X# ^/ R4 T% X
  92. 1■■ 3 x. u/ ^% f: L) [1 N
  93. 2■  
    5 h% ]) e& i0 Z4 \( _9 E
  94. 3   
    * M9 u7 C- }0 ]' y
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。! v' x/ \" l$ K  g8 l8 _& T
  96. 请输入“13”开启右上角的窗户。"9 ]0 B" N0 F. O; T5 Y
  97.   "  ①②③; l* T+ [+ \) O& a2 B
  98. 1■ ■  n; F: c# v2 F5 u" n$ X
  99. 2■ ■
    2 `+ N& C! N0 g9 U. ^
  100. 3   , M# O# d& G) I! S/ T& M
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    * d, J9 H) r5 B7 k) A
  102. 请输入“31”开启左下角的窗户。"3 L( ]. ]$ L* I* {
  103.   "  ①②③3 b4 s, f# R) a$ p& u" }" b( B1 [
  104. 1■ ■
    7 Q' T! v7 H1 M2 d
  105. 2  ■
    0 G. |# X" \( o2 M# T! g1 C! Q
  106. 3■■ 
    8 u9 A) v$ w& L4 p: G8 s6 t
  107. 此时,总共有5扇窗户被开启了。0 d5 G8 G! j& ^+ g) U5 q
  108. 请输入“33”开启右下角的窗户。"
    0 H  X8 B* @  F/ x
  109.   "  ①②③8 O- v8 C# R7 D" O
  110. 1■ ■
    # r+ q2 H" a: S% _5 H) i5 Z
  111. 2   
    & a- Z0 i: [% h& f0 I* \8 ^( A" \$ A
  112. 3■ ■
    9 F4 G. Q, x/ @8 I" ^. f
  113. 现在,只有四个角落的窗户被打开。
    : ]5 T# T& ]- m1 P
  114. 请输入“22”完成最后一击!"
      S+ ?& W8 P9 A0 d0 A
  115.   "  ①②③  b- ]& x( r' L/ p1 o
  116. 1■■■7 A# B' a( J7 y5 i+ `$ k
  117. 2■■■: N8 `" q0 Y& s: J" q, [" w
  118. 3■■■9 H" V/ r& c' M6 S
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    ; y# w4 Q3 G; c. \4 u' F3 A$ D
  120. ; o9 H3 g1 k2 \( `) r7 w  ]5 e
  121. ;;; 棋盘5 ?: O5 u/ a' ~9 |  j7 A+ }
  122. (defconst *wechat-5x5-white-chess* 12288
    0 B; q6 `' \3 Y/ H
  123.   " ")' A& |3 B3 j1 s1 x

  124. 9 s# P6 n  f% ^0 g% K/ ^# C7 W4 I
  125. (defconst *wechat-5x5-black-chess* 96327 t$ ?* K/ ]0 k
  126.   "■")
    ! @3 P* r$ Z6 {% A& v

  127. - r! M4 C4 b" x4 k
  128. (defmacro with-board (&rest body)% ?4 Z+ \3 c+ K4 q+ v# [( L8 p
  129.   `(with-temp-buffer
    ) \1 U0 W- _8 M' G
  130.      (unwind-protect" H. K+ Y+ o* H% i% s4 v
  131.          (progn4 [2 A, n3 y, p8 ~. p% R* t
  132.            (if (session "board")
    ) R4 ~# S% K- z: B2 @$ P7 A, d
  133.                (insert (session "board")))
    , d. Y7 J9 H3 s. [# t/ @
  134.            ,@body)$ R4 ?' @. ~* u2 b% H% d- Q
  135.        (session "board" (buffer-string)))))
    6 j# H7 s5 N0 R: V  M9 m7 w4 j) Z! `

  136. ( Z+ c6 L# j( x$ a
  137. (defun board-init (size)
    % ?& ~+ b1 b7 G" v
  138.   (session "size" size)
    5 M9 W: L# J+ |; x9 G$ l' K  y
  139.   (session "step" 0)
    9 `9 D, Q6 z6 m. s* E
  140.   (erase-buffer)! f1 y. |/ b- n' @# A% R
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨")). o% \/ Y$ [# K- I
  142.   (dotimes (row size)
    & r* j# I) U# m0 \8 O1 w8 R
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))# ~( B- `. O& O4 n

  144. # ~" e  w5 n) b8 F4 j1 z
  145. (defun board-contains-p (y x)& U! P) Y7 l% z% J, p+ P2 K
  146.   (let ((size (session "size")))$ X) B3 _' ^+ @4 D
  147.     (and (<= 1 y) (<= y size)
    4 [* v7 s+ H1 y/ {5 b
  148.          (<= 1 x) (<= x size))))8 w  J! W: _: r+ c6 p# z. A* }

  149. 1 v5 }4 r  r/ g: Y
  150. (defun board-toggle (y x)
    # C9 }# d/ w% S5 M3 a
  151.   (when (board-contains-p y x). a9 r, t" r5 X, \6 S& \6 u
  152.     (goto-line (1+ y))# _0 m5 V0 V: p* q
  153.     (beginning-of-line)2 {# S  U+ G) Y8 F7 b2 k: k$ C4 o
  154.     (forward-char x)
    ) g2 ], d: P6 E" p& ^+ j
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))5 |0 `# w: N% \( ]0 Z
  156.                 *wechat-5x5-black-chess*! K1 ^9 Q7 y# W3 j  y  B( K
  157.               *wechat-5x5-white-chess*))
    ; z" K; d  `- g
  158.     (delete-char 1)))
    2 W- E" V" l0 z* A/ x# [0 H; ~

  159. * s9 f8 c3 i* o" r
  160. (defun board-put (y x)
    % m+ O0 p. i" j) D( K& s
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    1 \- T, J7 \) e0 y! I  Q& @
  162.     (board-toggle (+ y (first dir))
    ' j8 k6 W2 X$ V$ G% K
  163.                   (+ x (second dir)))))
    : ]; ~# i% p* ]5 t; m2 Y

  164.   D8 ~' v/ A9 D8 d! F
  165. (defun game-over-p ()# ^- D. d' F- C) ?5 n8 q* ^
  166.   (beginning-of-buffer)3 W8 T5 W: K2 h; w
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))2 l; f+ ^1 I( K+ d7 J

  168. ; L: U: V; K" g2 W& W6 T
  169. (defun board-show (). W: @8 I; a  e, x, Q: k# r5 F
  170.   (with-board
    6 r  H1 V) `# q0 f  y9 O2 |, ~
  171.    (concat (buffer-string)
    % d4 E( Z) k7 Y
  172.            (if (game-over-p)0 Q: J- F* p9 ]- ?: l1 }6 Y8 c
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    0 D5 C3 j& O) O: R# ^/ s' W1 t
  174.             (format "第%d步" (1+ (session "step")))))))! x# ?/ l# L! R; W
  175. : ~1 C  ~# m! m
  176. (defun board-position-parse (cmd)
    . x: [, t' l7 |
  177.   (if (= (length cmd) 2)
    2 R4 \6 t7 S+ a( j  h! I
  178.       (list (string-to-int (substring cmd 0 1)); W; V' R, N3 Q* b" |3 C* d
  179.             (string-to-int (substring cmd 1 2)))& g5 s% s3 ~) Q: |$ B. u4 u0 {% j# O3 n
  180.     '(0 0)))4 C$ q0 [3 a$ Q8 n
  181. 5 W+ p' [- ~8 l
  182. ;;; 游戏房间
    " P% b3 x- x) c. j
  183. (defun game-room-init (cmd)
    0 J5 u4 \2 p. Z, J. o
  184.   (let* ((middle (string-to-int cmd)). P3 Y1 S* E5 ^7 F. D& X# W% I
  185.          (size (1- (* 2 middle))))" Z, y0 t% ~1 i; O6 B
  186.     (with-board( u, c& D4 S8 N/ U) L4 j
  187.      (board-init size)8 P+ p' D7 Q) m5 i! B( U
  188.      (board-put middle middle)))" }1 U4 W* E! R2 h' t/ `7 t. ~
  189.   'game-room)3 p. r. @6 X: z6 F! K5 X
  190. ! I( z8 c/ J/ F! l
  191. (def-room game-room
    : Y% f! [& }- J6 n5 ?
  192.   #'board-show5 a1 f7 {& m- Y" f# B$ D
  193.   (t (lambda (cmd)- R4 d& u$ m! e! c
  194.          (with-board, S* q& I/ e$ \# x: h1 b
  195.           (if (game-over-p)! V# w% f9 Y/ d1 j" P
  196.               'living-room7 D1 q+ h" c: n) f9 S% y
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    ! k1 d' q; p$ X8 q0 A
  198.               (when (board-contains-p y x)& B) n, x" E& @+ _4 R
  199.                 (board-toggle y x)9 Y# t6 o( M% v6 A
  200.                 (session "step" (1+ (session "step"))))
    ) ~" ]- {- o( e  x% ]
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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