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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。. D2 `% J2 n2 C3 @2 G& K" u$ T
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. ' E: p4 p4 M( B+ @
  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;">;; 定义新的游戏地图
    ! x+ |! U' ~5 f5 L! {/ I* X" y1 z
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL' W" k/ w- j+ l2 W# y
  4.   'tutorial-room-0)                     ; 默认的入口
    4 r0 R2 S- d; u$ d4 R1 ~* g
  5. # V. j) C( Y4 ]: {( C. q/ n
  6. ;;; 游戏大厅
    # j) ?4 D/ W) ]3 r7 U/ ^/ Z/ t! t
  7. (def-room living-room
    & x3 g. `% F( Z% A# ]6 c
  8.   ;; 进入该房间后的提示语1 E7 ]' n9 I) P5 j" A. U
  9.   "1. 教程) S6 X3 l/ j' Y$ z, ]% Q# M
  10. 2. 入门(3x3). B4 F- |! U6 z
  11. 3. 初级(5x5)  {) [0 ^  ?3 d$ q
  12. 4. 中级(7x7)- z8 R6 i! U* T9 y
  13. 5. 高级(9x9)0 ~0 m( F1 u3 k+ k' H. o" C
  14. 0. 关于作者" a2 y( e7 E& S6 }1 }$ @7 d( [
  15. 请选择1-5开始新游戏:"$ r5 d  W, v" e( k9 n4 I
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    # i" D( A  d! Z7 H& u* D) l
  17.   ("1" tutorial-room-0)
    " X, g4 Q$ F) ^
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配' v& n. y2 t! Z+ y- @+ c
  19.                                         ; 相应的返回也可以为函数,动态返回房间名9 @/ z+ H' D+ e. n% P. s) a7 X
  20.   (t living-room))                      ; 如果条件为t,为永真2 [* N% B: B. x8 y, c

  21. : A# n! w/ W+ p
  22. ;;; 作者信息
    0 T6 I% m! W0 I! e% {1 P
  23. (def-room about-room8 d) u3 s7 s& Q  i$ c# H
  24.   "作者:redraiment
    : L% d3 i4 z, x& s
  25. 微博:http://weibo.com/redraiment
    2 f) a9 C( H$ d: Z7 b& l/ F/ Z4 {" v
  26. 有任何建议,欢迎在微博或微信上联系redraiment。0 o8 \; s$ x- m% H* o( L: Y2 _
  27. 请输入任意数字返回游戏大厅。"" [% p9 }6 g6 E4 \3 J2 w
  28.   (t living-room))7 [5 l8 n4 {6 I5 H

  29. : n, j* h% x- z. H% T9 p1 y/ E
  30. ;;; 教程& @& h( D  B! n  J8 M2 y6 Q
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    7 a( I9 f. ]6 m0 Y& N( `
  32.   "The number of tutorial rooms")
    5 _1 [  a- @( o" U
  33. - b' _/ z4 S6 [* w" s: q8 @
  34. ;;; 简化教程的定义
    ( U* f1 V: w' R4 A1 ?
  35. (defun string-last-line (content)3 y8 d3 {4 |, \5 y
  36.   "多行内容组成的字符串中的最后一行"
    ; C1 S: @7 e  V% t5 h
  37.   (with-temp-buffer
    : R* N& @- Y/ a8 Z
  38.     (insert content)* Q! B" ^+ ]: `3 x3 C: A
  39.     (buffer-substring (line-beginning-position)
    , @6 C* B6 y; j
  40.                       (point-max))))! b; g, v! y" f
  41. 0 `3 I9 K2 v5 x" N+ N
  42. (defun def-tutorial-room (prompt). }8 ?5 p( [! E' l& P
  43.   "根据提示语自动生成教程房间。
    & E# t( T: W1 c4 k2 I
  44. 7 ?5 G; k% a- _5 a. Z4 W
  45. 1. 提取最后一行作为问题;2 N! i1 [% {. O- G' L
  46. 2. 分析问题,获取期望用户输入的内容;
    4 }  d6 O6 P: P. i# k- Q: N7 u# i
  47. 3. 定义教程房间和重复提问房间。"
    4 O. J" R, t/ C1 f
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    - J2 p& x. h+ D& b2 |
  49.          (repeat-room (concat room-name "-repeat"))
      r  }+ h$ W- Q. E$ a% E. e
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    % j( y0 U' \' q9 l% H
  51.          (question (string-last-line prompt))
    4 Q6 G! c1 H* U+ j3 L2 g$ A& Y
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)# `4 _% w+ j  }# d9 a& l' x8 P
  53.                      (match-string 1 question)))
    6 x- g- N: G. G' H  b
  54.          (doors (if except
      [; l1 a1 W/ @& c% ?
  55.                     `((,except ,(intern next-room))
    6 V1 p3 ^+ }: L0 \
  56.                       ("q" living-room)
    ! l" B8 A# T* t
  57.                       ("Q" living-room)
    % {6 ~. x& k' a) U4 ]6 x6 {2 }9 Q( V
  58.                       (t ,(intern repeat-room)))
    * N8 c$ U; \5 s2 E' |3 k: J4 r
  59.                   '((t living-room)))))
    & o9 }' g- s& y) f4 F# V
  60.     (def-room-raw (intern room-name) prompt doors)- a; S3 ]- j  w+ m+ n6 H$ y
  61.     (def-room-raw (intern repeat-room) question doors)))+ A( j7 V) V/ a7 F5 {; ^2 G2 z
  62. ' n1 a, h( B& k0 @& z
  63. (defun def-tutorial (&rest prompts)7 a. w" ~9 w! _" O
  64.   "批量生成教程房间。"
    0 e; Z* I5 R% ?3 ^7 u
  65.   (dolist (prompt prompts)+ A5 ]% g. j- ~/ |: h5 h
  66.     (def-tutorial-room prompt)))8 C' R8 e$ S/ y" C5 k- F

  67. . Y! U) R" Y5 K2 B% O4 b
  68. (def-tutorial
    / O! x0 L  o( B+ p1 `" _$ v
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。* T. K; U" e4 S) d3 J8 o
  70. 1. 教程- ]9 w' j% g- e. h2 R8 g
  71. 2. 入门(3x3)) y0 d2 l* l! @3 ^- V* k
  72. 3. 初级(5x5)9 \& j2 r) h" |) Q
  73. 4. 中级(7x7)
    9 d6 J2 l1 r) e! m( e! s; F2 j
  74. 5. 高级(9x9)
    9 b, D, }* G5 L' M6 e: U2 j. H! H, O
  75. 0. 关于作者
    + [: }% T/ r2 D. _3 L
  76. 请选择1-5开始新游戏:" H! X: j/ t( C* j" d
  77. 您现在正在游戏大厅里。5 k+ I* R/ z. h# ]
  78. 请输入“2”进入入门级房间"
    , `2 d5 v. C% n1 ^# h! M& R
  79.   "  ①②③! l6 ?7 S) |- q
  80. 1 ■ - V! W/ F5 w" K* l, U/ Z$ {
  81. 2■■■
    , e& i, z% B. U- y9 ]
  82. 3 ■ 
    ( ?1 r; j5 o0 a$ Q/ V% @1 m
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    8 p# y* }( }* z! u, C0 _
  84. 请输入“22”来关闭第2行第2列的窗户。"
    ! V1 l" s2 L+ A
  85.   "  ①②③
    2 G( t& ^0 A. ~: H: y: I
  86. 1   $ g+ C2 ]4 q! }
  87. 2   & k6 a. p- d8 C" f* E; B# U
  88. 3   
    9 i1 ^, p, D! O* x6 A5 R# C
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    $ G# ~( m3 s% c3 g9 C$ q8 k( v: g
  90. 请输入“11”,试着开启左上角的窗户。") C3 q3 T9 r) i) J: E( p
  91.   "  ①②③9 ~' s" u' \" ]4 m$ l& {
  92. 1■■ 
    9 T" K4 h6 z* ~. E% P$ M1 I
  93. 2■  
    " }" M. K. }0 {3 O6 d  A
  94. 3   4 U6 I- _! Y$ t. i: S* s1 Z
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。- X$ i* Z% l! A
  96. 请输入“13”开启右上角的窗户。"
    2 y# h( m9 C  x; c' ^
  97.   "  ①②③  C7 E) [( G7 j9 b8 k
  98. 1■ ■9 j! k, s  Z3 {$ m3 v- f
  99. 2■ ■+ k" I* P& Y1 I$ Q
  100. 3   ( }9 ]7 i1 Y: [7 M; ^4 O( D
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    7 [" n: r  _* E5 v! u3 I/ m
  102. 请输入“31”开启左下角的窗户。"
    . J. C" _5 ], A* Y+ [- v2 Q1 q
  103.   "  ①②③
    0 ^" }, H# P! w7 ?
  104. 1■ ■
    / r6 D/ F4 e" m
  105. 2  ■
      t4 S1 g2 j1 \( s; @
  106. 3■■ 3 ?# s0 I: @. M# e, D' g
  107. 此时,总共有5扇窗户被开启了。8 Q, C' O; a1 ~8 y1 G( f% T
  108. 请输入“33”开启右下角的窗户。"
    2 U  F/ Q/ s* w0 p
  109.   "  ①②③! \, i3 T7 ?& K  n/ u! Q7 E
  110. 1■ ■
    ! \. H1 P, E6 W3 V# Q, }5 n6 X
  111. 2   
    , y0 B# @7 J6 W  U
  112. 3■ ■
    2 h2 V) J) s% U3 E, o2 A) R# n
  113. 现在,只有四个角落的窗户被打开。
    , T3 B  X& a+ O( R( v
  114. 请输入“22”完成最后一击!"5 v+ X" y  d. ~$ f2 F
  115.   "  ①②③
    4 G5 w- |; `  @& q
  116. 1■■■
    , g/ S' e/ [' [
  117. 2■■■
    2 V2 ^4 P2 W( X! v9 S) H: {3 b3 z+ z0 l
  118. 3■■■+ @6 N0 X6 _% ^* _
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    $ C" z' v8 C+ e" o

  120. : {; j4 n8 h, n( w6 `6 a. G
  121. ;;; 棋盘
    7 ?2 {6 L2 Y; k9 ?' b
  122. (defconst *wechat-5x5-white-chess* 12288
    & t- U! o+ S0 ]! D% W
  123.   " ")7 l5 T# P, D4 F+ V6 v, M

  124. : `9 {1 e6 {/ ]% H
  125. (defconst *wechat-5x5-black-chess* 9632
    " J4 O8 y) G( H! H+ K
  126.   "■")
    . B1 B: v+ x+ {8 f
  127. 0 @, P8 U1 o2 l/ z0 `
  128. (defmacro with-board (&rest body)/ y( b. q0 M! R" L3 C
  129.   `(with-temp-buffer
    ( b7 Q$ o" K: S
  130.      (unwind-protect
    ; C9 S# g( g9 A, S" R9 K+ Q* I
  131.          (progn
    ) ^" V  s9 U( D2 D
  132.            (if (session "board")* Y- k! ^# F' p' d/ v( T3 K
  133.                (insert (session "board")))
    5 N; z5 g8 o( Z% s0 |; o4 N
  134.            ,@body)
    # n# j7 X+ j& a( I, s! K
  135.        (session "board" (buffer-string)))))2 S/ R, J, I) B; H4 b( n
  136. 4 w5 ?* A' A: R/ i3 y1 i
  137. (defun board-init (size)
    , _: G* j' r. M! h4 O7 L
  138.   (session "size" size)# L' D( P# p% L
  139.   (session "step" 0)
    9 s, Z- M# N) B( _. G: M- E5 @' F
  140.   (erase-buffer)2 ~. l1 x0 Z" \
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    2 }2 I+ f% X, [  m. B; {) `
  142.   (dotimes (row size)
    + H- d' H0 H9 a/ C% T! N
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))" v' d% U2 i. w5 i* y: J
  144.   C" V; l. F0 f% s' p' E1 L
  145. (defun board-contains-p (y x)
    : r% @6 [# Y( p" }! j
  146.   (let ((size (session "size")))  X" j$ i) l5 @8 M4 J3 x. ?+ y
  147.     (and (<= 1 y) (<= y size)% ?& v9 K0 J, Q2 I2 k
  148.          (<= 1 x) (<= x size)))): w9 M- _  H0 U, H6 j8 I6 V, H

  149. " S9 x! N/ G! a& u
  150. (defun board-toggle (y x)9 s8 ^& l0 m) m
  151.   (when (board-contains-p y x)
    ' m3 u/ F4 N+ ^" N' o
  152.     (goto-line (1+ y))
    1 p8 m5 @9 ]0 B" L; M# I8 c
  153.     (beginning-of-line)
    3 P1 y3 M6 N, M1 q6 V
  154.     (forward-char x): p) i  w) a! |( a( e
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))4 U: B  A! ^/ E: W+ B8 O) s
  156.                 *wechat-5x5-black-chess*
    6 K$ G; ^8 o- K7 Q
  157.               *wechat-5x5-white-chess*))# N. Y) D4 M. p1 A5 v
  158.     (delete-char 1)))  J3 R1 i& L; @+ c" [, V

  159. : Z4 q6 C6 [, h4 g
  160. (defun board-put (y x)4 n4 j- |2 s7 I! y  X! Y- c
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    7 Y  q, n3 ~( [% I7 `" `3 m
  162.     (board-toggle (+ y (first dir))
    7 z5 z7 N, }$ v& n0 W
  163.                   (+ x (second dir)))))7 z9 T5 H% \) [/ ?' d5 w6 f

  164.   Q/ p5 B' e1 A: n* d
  165. (defun game-over-p ()
    ! l! A- s% R8 U* o0 [
  166.   (beginning-of-buffer). `5 t. F+ m3 T. V4 ~
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))! q/ o# p0 J0 @: e

  168. 3 m5 t9 b5 X+ w4 D1 j5 [" X) Y4 K
  169. (defun board-show ()
    2 X" H: [- a1 }, u4 M6 o
  170.   (with-board
    5 ]- W/ J) N' [! o# B
  171.    (concat (buffer-string)
    7 {$ ~! g) L; O, n$ v; {& d6 _. e
  172.            (if (game-over-p)
    , o/ f5 v" l$ h, v. @/ h5 a
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))7 E9 q9 K2 k5 n2 H& ^5 ?5 \
  174.             (format "第%d步" (1+ (session "step")))))))
    ( s6 H1 y8 T" F8 B" X+ Z8 v1 W
  175. + J- L, q# _( l. ]- J
  176. (defun board-position-parse (cmd)
    / Y& B, H  T+ s6 H* {
  177.   (if (= (length cmd) 2)
    & ~" B5 `- F8 {
  178.       (list (string-to-int (substring cmd 0 1))
    7 m, J* a6 t/ Y6 Y; W$ M
  179.             (string-to-int (substring cmd 1 2)))3 Q- r6 }/ s6 C- X7 d
  180.     '(0 0)))
    . M/ a- j* e8 J& Q8 s$ e7 X
  181. 9 h' }; V3 T# E( S: K
  182. ;;; 游戏房间
    ! D6 z7 ^' x3 x& q7 c# w' @  x
  183. (defun game-room-init (cmd): P' u& p. J: P+ d' a$ F  `! O
  184.   (let* ((middle (string-to-int cmd))
    ! Q+ E$ M, q  t$ y
  185.          (size (1- (* 2 middle))))' W2 u- R0 V8 _
  186.     (with-board5 A" j+ F  i; R- }
  187.      (board-init size)4 X5 z; y3 w" {& \" _& ?
  188.      (board-put middle middle)))2 G5 L; K8 w6 W! K+ @. t
  189.   'game-room)
    ! N: Z: p: z6 ]- x  H

  190. + o2 s/ Y( \1 w4 G. X# @
  191. (def-room game-room
    1 {, m# ~' s$ h# f+ O, {! b
  192.   #'board-show0 ], w8 J: T3 C+ D$ O3 D  H& K! ^
  193.   (t (lambda (cmd)
    ! z# d3 M/ {, @2 B$ L" W
  194.          (with-board
    ' _. Q  m7 O. M: v. d
  195.           (if (game-over-p)
    * q  y9 M' g- N7 _1 z5 K
  196.               'living-room: [/ O2 X' \3 c9 J" [
  197.             (destructuring-bind (y x) (board-position-parse cmd)8 ~' Z7 R  v' x) j# r
  198.               (when (board-contains-p y x)
    1 ~7 f$ Y3 U7 R% [* [1 d: x
  199.                 (board-toggle y x)* z; c2 l# H& b) v( @
  200.                 (session "step" (1+ (session "step"))))
    % D5 j; N+ a6 J9 X! R1 g2 I
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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