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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。& f& ~6 R# |; f& z3 w+ h' D6 T+ h
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. % F, k+ D* a. e9 z( S5 z0 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;">;; 定义新的游戏地图# k, W! a/ F, Z" e$ B' N
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL1 P# ]: [( e  C1 h6 ~
  4.   'tutorial-room-0)                     ; 默认的入口
    ( V/ O, M, ]% ?! I8 {4 `
  5. / E9 E' ~, }$ D6 @) l, t
  6. ;;; 游戏大厅2 s7 T3 ?/ x% S; N/ p  S
  7. (def-room living-room
    $ G" O& p  L8 c) b2 _! Z. ~3 z
  8.   ;; 进入该房间后的提示语. ]! R* _  n& A* Y; [
  9.   "1. 教程
    : Q1 o- v. B1 c
  10. 2. 入门(3x3)6 i; @' P; C" b: U
  11. 3. 初级(5x5)& Z9 x" p5 U7 C! {- Q
  12. 4. 中级(7x7)
    8 h, g0 N" _1 d
  13. 5. 高级(9x9)
    : Y' n1 g+ ?+ g3 c# I! U* l
  14. 0. 关于作者. V  c# S* B8 o6 A4 G8 U! g4 a
  15. 请选择1-5开始新游戏:"
    , F1 E' D  h, B' B
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    ( i8 U$ u  X0 X: \
  17.   ("1" tutorial-room-0)
    3 ]1 y2 F6 N1 }
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配7 O2 U7 h4 _! u; N
  19.                                         ; 相应的返回也可以为函数,动态返回房间名5 C8 B$ h. q. O* b9 n
  20.   (t living-room))                      ; 如果条件为t,为永真% q2 X9 v8 V4 P/ F7 m- j* F
  21. 8 J4 j& n! _+ s
  22. ;;; 作者信息& ]) M' ]6 Q) p& i. ?3 T
  23. (def-room about-room! U6 i7 Y" E/ G2 `+ q
  24.   "作者:redraiment
    , n1 r* k" D+ j9 k$ u' Y1 i! X/ K: S$ Y
  25. 微博:http://weibo.com/redraiment
    : G* l. m- M6 ]- o& E" S
  26. 有任何建议,欢迎在微博或微信上联系redraiment。5 I7 |. K8 x+ s8 q7 }4 ]4 ~; \& R
  27. 请输入任意数字返回游戏大厅。"
    0 G! l; X; G  n) [! F
  28.   (t living-room))7 g; N) `& Z; _3 ?/ Q) q4 \

  29. 4 ?+ t7 ^( t" T
  30. ;;; 教程
    8 W; n6 T" L: W) m; a
  31. (defvar *wechat-5x5-tutorial-rooms* 0! Y3 Y8 l. }, h; ~3 k
  32.   "The number of tutorial rooms")6 u! s- [! M, V  J* i

  33. ; y4 d/ a0 n; b; _. r
  34. ;;; 简化教程的定义; |. M" e0 H1 w; H# V. l* O0 H! I
  35. (defun string-last-line (content)5 Q+ ?) x% ^0 G" `, K* ]0 g
  36.   "多行内容组成的字符串中的最后一行"$ L' B) r8 |: E) X- o
  37.   (with-temp-buffer
    ( g$ l: V  ?5 V+ s! S
  38.     (insert content), \+ R0 ?! D' t+ z" K- X! s- k
  39.     (buffer-substring (line-beginning-position)
    - _; `( c* g* q
  40.                       (point-max))))' `% |( v/ n6 o9 g
  41. ) e6 X9 g7 A9 e/ I7 @& Q
  42. (defun def-tutorial-room (prompt)/ r) i5 ~% R# M: e
  43.   "根据提示语自动生成教程房间。8 ^) e9 i( B7 {1 Y9 e2 |
  44. : M. [' O/ H* ~- z5 L8 o1 s/ `; I
  45. 1. 提取最后一行作为问题;6 n- o$ w( B' O, k8 Z4 A
  46. 2. 分析问题,获取期望用户输入的内容;) i! n5 @( u3 G$ P+ V
  47. 3. 定义教程房间和重复提问房间。"1 n* m, `& E* F
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))' m+ C; O8 ~1 N3 @  T/ t2 ?4 i5 w7 i
  49.          (repeat-room (concat room-name "-repeat"))
    7 m3 |; N4 O  T. x
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))3 P( j: Y+ m7 |( D, X  y
  51.          (question (string-last-line prompt))/ C2 k) B, W5 N. k5 H
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)  ?5 U3 Q* U8 C7 h" h% `
  53.                      (match-string 1 question)))
    0 M$ X+ M# k, J* n3 f5 Z, b
  54.          (doors (if except0 b$ o( ]1 `! P" U# w* {5 y
  55.                     `((,except ,(intern next-room))
      S/ w( O' ]) }1 L3 M
  56.                       ("q" living-room)* W- J# G9 }( h/ e7 i, D: D. i
  57.                       ("Q" living-room)# _( G9 N9 q* L: Z5 U
  58.                       (t ,(intern repeat-room)))
    % {$ e9 K6 K$ G
  59.                   '((t living-room)))))( E7 s+ E  M. B% T; K- v+ ^" |: K
  60.     (def-room-raw (intern room-name) prompt doors)2 J3 o$ J; \* g% i8 O( b. F! `/ I* `/ _
  61.     (def-room-raw (intern repeat-room) question doors)))
    % H" J8 m4 s3 f1 k& ~2 F

  62. : Z4 j. H0 i+ b- D: h% t6 F
  63. (defun def-tutorial (&rest prompts)  Q9 ]* T3 i+ G# F0 n5 A
  64.   "批量生成教程房间。"- O" b1 z6 f( D( ]; u2 [) v0 C  I8 K
  65.   (dolist (prompt prompts)1 e  S3 m7 {; Q& M9 ~+ N- J
  66.     (def-tutorial-room prompt)))9 i4 G9 Y0 Z; z$ Q1 c: r' T
  67. # ~8 n3 T  e6 \# _' C7 p% u0 v
  68. (def-tutorial
    ' F: X4 t- j# x  s3 |
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    5 \4 v& M. ~# ?& W
  70. 1. 教程1 g6 L8 y1 ~7 J0 F9 B6 A' \7 d% q% b
  71. 2. 入门(3x3)
    " H7 {" y# j- x! ?( g
  72. 3. 初级(5x5)- y4 e% s4 y1 j6 C
  73. 4. 中级(7x7)
    - h% a; `% R% `& O4 \) }' i" l
  74. 5. 高级(9x9)* X( _  B- h( L. j& [
  75. 0. 关于作者
    9 N/ `. H3 O9 x/ o1 W+ Q7 ^# B
  76. 请选择1-5开始新游戏:. ?; R" V' G0 l% B( [  t
  77. 您现在正在游戏大厅里。
    7 u4 k/ D6 e2 \) |  C1 z
  78. 请输入“2”进入入门级房间"
    3 N5 h) \. u+ ~7 D
  79.   "  ①②③
    6 ]1 F$ [# Q3 U- X( s4 H" H3 w
  80. 1 ■ 
    4 M0 L# N  q/ i
  81. 2■■■
    $ A% u# }( G4 \# ~( [
  82. 3 ■ ( i* Z7 ]6 ?0 t" Y1 }2 e% G
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!2 q9 H) h- l( ^
  84. 请输入“22”来关闭第2行第2列的窗户。"( t8 }5 n4 i; P( }3 l& |/ Q: E
  85.   "  ①②③
    % \# ~2 S8 `2 w( \/ c
  86. 1   2 ~+ y; Y! F+ b4 `# j% d4 U
  87. 2   $ I+ d; Q2 @5 N5 r( z
  88. 3   
    & \. L8 Q( d$ @) \
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    + |7 o8 y& [/ S, B# |
  90. 请输入“11”,试着开启左上角的窗户。"8 W; u7 }6 L+ }
  91.   "  ①②③' F4 b3 b3 J9 q# ]2 u, ?
  92. 1■■   E" r  c% ]* w$ m% `! P" D
  93. 2■  - z2 r$ p& M' ]5 L
  94. 3   
    : T, U2 l1 I) U
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。" P3 j  p- s! h4 X$ w
  96. 请输入“13”开启右上角的窗户。"0 D* `" p  ^7 J, ^3 v, h7 Y9 P
  97.   "  ①②③
    3 t( f1 h7 l( t6 C
  98. 1■ ■
    $ A9 a% C9 \/ N- g* P
  99. 2■ ■4 I$ q5 T# H2 @3 ^' f: }  U
  100. 3   ( {, T; j; p7 ?0 N
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    ( z' M' J& ^& O9 `
  102. 请输入“31”开启左下角的窗户。"
    / ?5 q! A! N/ d3 o6 ~/ e" f
  103.   "  ①②③
    : P% c5 D; {1 c
  104. 1■ ■
    0 K. i# I0 _3 R8 h1 \/ ~) C+ u
  105. 2  ■6 |5 x- V! s$ o" T/ \$ q
  106. 3■■ 
    ; t. m+ {8 h  L/ O0 F
  107. 此时,总共有5扇窗户被开启了。
    2 z, N( b2 e% v" T, c
  108. 请输入“33”开启右下角的窗户。"
    ( W& X+ E+ o. H: ]8 b  D
  109.   "  ①②③9 ?+ \% A4 c$ a
  110. 1■ ■7 h/ u7 _# l0 S+ j, o
  111. 2   
    0 U1 U$ y( o/ q1 g
  112. 3■ ■; O$ K& S- I# ?
  113. 现在,只有四个角落的窗户被打开。
    1 T4 g# ?" k, e, S& V; x
  114. 请输入“22”完成最后一击!"
    3 c  ?; Q' u6 B" s1 K; |
  115.   "  ①②③. E/ c" W2 J8 A% ?/ W. m
  116. 1■■■
    + s' X) v. K9 X' q* K
  117. 2■■■8 m# Z2 C) I- c) d2 V, R
  118. 3■■■  X- v# I$ ^, a. [" N' B& }
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")+ m$ `) Z8 I; b

  120. & a  I1 l( D! J2 h' n& B- e
  121. ;;; 棋盘
    ( f* O. S9 C  v6 _) D* h9 g
  122. (defconst *wechat-5x5-white-chess* 122886 q9 v6 p6 @$ G; y3 t
  123.   " ")
    3 W9 V6 T& a' t6 a) W8 I- T2 H" T# l

  124. ! b4 \$ a! F6 l1 C1 W9 z
  125. (defconst *wechat-5x5-black-chess* 96325 J: C3 a& p& u6 |5 p& r  n
  126.   "■")
    6 f! S9 i' X5 B
  127. 8 e) r' p2 c& G2 Q/ g. v9 L
  128. (defmacro with-board (&rest body)8 [) ?% z8 v, M# r
  129.   `(with-temp-buffer; l  T0 |! M. M) u, z
  130.      (unwind-protect
    ; I9 ]5 x/ g! e
  131.          (progn
    3 W# w$ H$ F+ p  a$ e! j/ |! {, e
  132.            (if (session "board")
    ; H& E# \$ s- p) x, c. A! P9 d
  133.                (insert (session "board")))/ g* ]6 e5 F; T9 h. a
  134.            ,@body)
    4 L8 g2 T3 I- X' R8 x5 D3 ^/ p
  135.        (session "board" (buffer-string)))))
    ; Y: Z) _0 Q4 v8 M/ g- R
  136. ! Z9 o/ r6 |0 n4 v0 U6 w1 M; @
  137. (defun board-init (size)- [* s: e4 L% Y! {. ^  S. H6 k
  138.   (session "size" size)) I, x! r. H7 ~8 ^) R
  139.   (session "step" 0)
    4 t0 n2 N  n9 D- m8 a2 i  K6 m0 D( R
  140.   (erase-buffer)5 E9 y3 J" r- ?/ F! k+ j
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    ( d5 X- U- |/ @0 f  d6 D
  142.   (dotimes (row size)  ]- t: n+ H6 o/ v# W1 O
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    0 K4 }' i: D! M8 b* K( ?
  144. : Z. Z8 ~2 \) m! U) q
  145. (defun board-contains-p (y x)( S  }4 p6 S# |+ q/ x& M- Q& A6 ?0 m! p
  146.   (let ((size (session "size")))5 k. Z1 C/ ^! H! c1 V
  147.     (and (<= 1 y) (<= y size)
    + Q( Z, r; W6 K$ [" T2 {
  148.          (<= 1 x) (<= x size))))
    ' N3 w5 K, Z+ r( X" s
  149. ; |; Q9 [0 H" Y" l0 [
  150. (defun board-toggle (y x)/ U' d0 D0 ~1 _
  151.   (when (board-contains-p y x)
    # u9 P0 v* z' M9 P' y
  152.     (goto-line (1+ y))
    5 k* F$ E2 i% e' [- o( x' i. i
  153.     (beginning-of-line)
    6 Q  ^/ b) m7 ?, _- v. w
  154.     (forward-char x)
    & y% h+ R9 l0 ~; M8 b
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    # n2 v2 R! o* {5 r7 T: F! M3 u. L1 D. b5 D
  156.                 *wechat-5x5-black-chess*
    $ A* T; u9 U, h# V& e3 r6 Q
  157.               *wechat-5x5-white-chess*))
    * i! x5 @6 P3 f/ F! h4 g: U3 G  ]
  158.     (delete-char 1)))- C7 j/ ?3 t6 z1 H
  159.   i8 {/ v" F& a; b; \. D
  160. (defun board-put (y x)
    . T+ {- O) G, \& g8 [9 {) ~" H; k
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    - |$ W* y0 _' G* L) R  R! ~6 f2 H
  162.     (board-toggle (+ y (first dir))
    . T/ r& P: h. O0 X
  163.                   (+ x (second dir)))))0 T* ^. M+ g7 \0 Y; s  }

  164. ! u- ?9 a1 ^9 F( u) g
  165. (defun game-over-p ()
      N+ Q5 M  h8 L0 o6 w
  166.   (beginning-of-buffer)7 G3 _& p2 e2 `) H' X- m4 n2 J
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))& n. b* h% N5 B* p* X6 F: t8 W7 Q
  168. * v5 C9 P* j2 X. ]$ l1 \$ u
  169. (defun board-show ()) s8 d- q" X  G, J! d
  170.   (with-board+ c3 g: E+ J, C
  171.    (concat (buffer-string)# R$ Z! c' x9 X# }" w  i% }: I  E1 t
  172.            (if (game-over-p)) C: E+ K" [$ t. b5 p
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    + L$ t2 L3 s4 ]3 M
  174.             (format "第%d步" (1+ (session "step")))))))
    # W; i8 Z) X% K; J7 o+ k

  175. + }# j! ?& w# P# ~" l' }6 [1 d6 ^8 ?/ p
  176. (defun board-position-parse (cmd)
    4 h1 s( o' V6 Z- Z
  177.   (if (= (length cmd) 2)
    ( T* Q" A8 {8 s5 |) _* h+ [8 `+ m
  178.       (list (string-to-int (substring cmd 0 1))
    1 m. d% E8 l8 q
  179.             (string-to-int (substring cmd 1 2)))+ p: B' N' ~+ x# M. `. S
  180.     '(0 0)))# T% J& q1 ^5 b* c8 B$ O0 B5 l
  181. 8 |' `+ ~4 @  y. @' I' p
  182. ;;; 游戏房间4 ~+ Z( J$ T+ l2 _
  183. (defun game-room-init (cmd)7 C0 e6 B" w* P# R2 q' T
  184.   (let* ((middle (string-to-int cmd))
    2 o0 Q3 s1 p7 x+ P4 ^! M) v# R
  185.          (size (1- (* 2 middle))))
    0 L9 I8 h# Y8 _0 Z
  186.     (with-board
    ! q# m2 O( V) e# x
  187.      (board-init size)" a5 E/ Y. n$ u2 {% S2 [" o
  188.      (board-put middle middle))); C3 j1 O* e8 Z
  189.   'game-room)& M6 M3 `' w8 h$ J. c. k& `0 q
  190. " ^9 z( Q* F6 @& R
  191. (def-room game-room
    ) Z1 o% i# b1 p* [, X
  192.   #'board-show
      d; C% b+ c4 W& P- w9 I
  193.   (t (lambda (cmd)
      k" L, W; x9 t) D0 I( {
  194.          (with-board
    ' j/ b( y* d1 J" ^; D
  195.           (if (game-over-p)
      ~& U/ ]8 F' b5 l0 Q; S
  196.               'living-room
    - H2 D9 v" A5 g
  197.             (destructuring-bind (y x) (board-position-parse cmd). ]' V( H" B3 N: _" m
  198.               (when (board-contains-p y x)
    " M" K( J: m0 Y
  199.                 (board-toggle y x)1 j, R  l$ L5 Y2 k0 }
  200.                 (session "step" (1+ (session "step"))))
    % K" q* U  k6 W4 ]- s+ a
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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