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

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

  1. $ Z! C! _$ ]& c7 V* e  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;">;; 定义新的游戏地图4 o# K% @: c5 M- x( g: A
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    0 Q/ F. f0 A; i" B3 y0 l
  4.   'tutorial-room-0)                     ; 默认的入口
    & m) v7 n9 T: v+ l4 v

  5. 3 @( M0 i% y$ `4 Q
  6. ;;; 游戏大厅+ I- f: ]. l2 _1 ]3 ?0 y* W
  7. (def-room living-room- Y/ w5 y8 D) W, n5 |
  8.   ;; 进入该房间后的提示语
    & i! e: u$ }) t3 h: s0 V
  9.   "1. 教程4 M4 C" h1 y+ O& i2 o% i. M
  10. 2. 入门(3x3)
    + o! k( \' e# n3 k0 j# d6 c4 t
  11. 3. 初级(5x5)
    5 z+ t" [0 N* ?2 I/ @1 j( y; a
  12. 4. 中级(7x7)
    % D0 w, l/ Q) z4 v8 U1 @/ J* p
  13. 5. 高级(9x9)
    6 m: G$ o3 D* K/ u% |! y
  14. 0. 关于作者
    8 T! @3 ^# t: b0 i7 F  c# C. q1 I$ L
  15. 请选择1-5开始新游戏:"% C9 ]3 {& v7 X5 A
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    1 ^( s/ v0 o. T  X8 T! \0 J9 \) H
  17.   ("1" tutorial-room-0)' b. i$ P- ^, }& p9 q6 r
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
      U+ @2 g# s% T: Y' K8 I, W+ f1 V
  19.                                         ; 相应的返回也可以为函数,动态返回房间名/ T( o# A: ?9 N: h7 E
  20.   (t living-room))                      ; 如果条件为t,为永真
    ) Z: _  a9 i. p$ s

  21. 4 n) a# h; c1 P; }& C
  22. ;;; 作者信息
    1 G5 g5 t7 I9 z& R
  23. (def-room about-room
    7 P. n4 I7 {) \  F8 Q/ x7 B% M
  24.   "作者:redraiment" c4 g  k; s) v
  25. 微博:http://weibo.com/redraiment3 J7 c# d* x6 |4 L8 r2 z3 n8 \' K
  26. 有任何建议,欢迎在微博或微信上联系redraiment。" p$ p7 {/ f# B% A/ N, u
  27. 请输入任意数字返回游戏大厅。") z3 [9 P& R+ O& i3 }
  28.   (t living-room))7 {9 A" U; t) r( u1 D- u
  29. ; ~; H! u1 ~1 y( X" u. A
  30. ;;; 教程+ M0 X- W/ g7 q: F
  31. (defvar *wechat-5x5-tutorial-rooms* 0- F8 i# F8 L/ N1 Q+ ]6 @5 _: S% O8 T
  32.   "The number of tutorial rooms")$ S& s' O" a7 c8 R% H

  33. % P- j1 t- d% z* f; R% c% m
  34. ;;; 简化教程的定义6 p3 O/ k+ v! m& u8 r  ?! V
  35. (defun string-last-line (content)3 K5 _: ^7 F) s! A4 M
  36.   "多行内容组成的字符串中的最后一行". ~# A8 q6 R& r" }/ P
  37.   (with-temp-buffer
    7 J0 L! E% l( u  l3 f
  38.     (insert content)
    . }2 D* S3 f3 [! ^9 L1 H8 H( w9 a. K" d- D
  39.     (buffer-substring (line-beginning-position)
    4 N+ v, S7 N7 X# c1 I# r
  40.                       (point-max))))
    $ a! A8 c( G. m+ A; S3 d& g
  41. ! ]& h1 W& _2 K% J" ~
  42. (defun def-tutorial-room (prompt)
    - s; Q9 z% N) N2 ^3 v
  43.   "根据提示语自动生成教程房间。
    ( R; z+ d+ B1 j( @$ _

  44.   D  z0 [: u( p8 a' H" {  d; c; v
  45. 1. 提取最后一行作为问题;
    + M8 `  j# C9 e- \) t% z- \% C6 ?
  46. 2. 分析问题,获取期望用户输入的内容;
    6 M& r; ]: c' E2 [+ _7 E  [
  47. 3. 定义教程房间和重复提问房间。"2 l, Y7 J# Y; e% B
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
      z" j) A( l) A8 e0 U& A; C
  49.          (repeat-room (concat room-name "-repeat"))
    5 E3 S1 T5 |4 @3 k9 e
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    . m1 q0 y( L' P# y# [0 d  e
  51.          (question (string-last-line prompt))
    ! U4 x4 R) \2 ?8 P0 e, G% J# L
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)! B/ k3 o# U1 Z, q) o
  53.                      (match-string 1 question)))
    * r8 ?. M- D- G4 T% d" E1 t4 h
  54.          (doors (if except
    # j1 z* W* _$ D7 B' m
  55.                     `((,except ,(intern next-room))
    * U+ Y& p, X# j; }0 @, x
  56.                       ("q" living-room)7 y9 f1 b& A5 R! N5 c. L" M5 b
  57.                       ("Q" living-room)
    ; ?  E0 B% }; ~% q( y( v
  58.                       (t ,(intern repeat-room)))
    ) Z% G5 g6 Z3 x* C
  59.                   '((t living-room)))))  g  n. t, c# K! ]) H  J. _
  60.     (def-room-raw (intern room-name) prompt doors)
    % b" O( R" Q* U$ @& V  V
  61.     (def-room-raw (intern repeat-room) question doors)))5 M( v8 j: j: j5 V! V! x
  62. ; }! O! Y: w4 R3 o
  63. (defun def-tutorial (&rest prompts)
    ; \& {$ X7 W2 b' t  a
  64.   "批量生成教程房间。"
    4 l9 I' b. c  {7 u7 [! U
  65.   (dolist (prompt prompts)
    0 M8 v# p5 ^( ?
  66.     (def-tutorial-room prompt)))  F* e. Z/ M, m7 {# `5 I' i
  67. 5 b& r  R6 p. G0 f$ D# Q& K
  68. (def-tutorial
    ! e; M# b, M) X' u8 |
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。8 f( |( R: Y; R/ g. {; I: U
  70. 1. 教程
    ( [& t, v. A+ O: A
  71. 2. 入门(3x3): m6 W; F/ V2 ]3 _, i5 F/ P2 E% p
  72. 3. 初级(5x5)
    $ g* D* |+ P- W0 X, R! ~4 y! \1 R
  73. 4. 中级(7x7)
    ( r( [5 }5 B1 }+ A; C6 p
  74. 5. 高级(9x9)9 `3 M& p$ M5 W# w1 a7 o+ {1 R" I
  75. 0. 关于作者& {) }' n' s7 t' `
  76. 请选择1-5开始新游戏:- x; Q0 v; j( X7 F( V, T% l
  77. 您现在正在游戏大厅里。" U1 l9 c/ U5 V3 @
  78. 请输入“2”进入入门级房间"$ I5 D5 D& X6 P' O
  79.   "  ①②③
    # x: o. r1 i8 F& a9 @, z0 d7 K
  80. 1 ■ 
    8 n  b( ?) N: ~5 r; }& C
  81. 2■■■
    4 C  y; C! B0 F5 h
  82. 3 ■ 8 w' h/ r" N: a* `2 n7 r
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!3 D! X* H4 x1 a$ d
  84. 请输入“22”来关闭第2行第2列的窗户。"
    9 G2 i( _( s8 L& m) G
  85.   "  ①②③
    $ H9 u. @# [( j
  86. 1   ' u* `$ h, j) q; }
  87. 2   
    , n+ O9 ], ~9 t7 h$ z0 u7 r; G+ }- k
  88. 3   
    & Y+ G4 I7 @9 o
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    / v% W) x: L* Y( g
  90. 请输入“11”,试着开启左上角的窗户。"( A: U5 o3 C* ]/ W" a
  91.   "  ①②③, j2 z5 J9 R) X
  92. 1■■ 
    & W  R5 i1 G# |/ W5 y& D
  93. 2■  
    9 a* V  O# v4 d- B  v
  94. 3   
    0 b& s: q5 f' n. b/ k3 d
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。7 c4 y$ z3 \$ Y$ y
  96. 请输入“13”开启右上角的窗户。"& }8 v2 `5 ^5 G& D" ^5 h  I
  97.   "  ①②③
    * n/ P# W1 ^, u  S5 n
  98. 1■ ■
    ' y% d5 X# S2 v
  99. 2■ ■: L. [, i. c/ e+ K8 [5 Y
  100. 3   
    " t/ Y/ s' {! f+ z" f& \
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。, t, R1 O1 _) u
  102. 请输入“31”开启左下角的窗户。"
    1 e- v! N2 R6 X! U2 l
  103.   "  ①②③
    $ S' Q9 Y+ i" P+ f. V  J4 w6 D
  104. 1■ ■
    7 I! O9 f$ a+ Q& k
  105. 2  ■
    ' j+ G  y  ]/ o- v6 J% t* H
  106. 3■■ 
    / E! \  }1 B3 n! D' p
  107. 此时,总共有5扇窗户被开启了。
    7 z7 s* K+ \4 W: D$ D
  108. 请输入“33”开启右下角的窗户。", v" k1 e( r2 l7 B) z, E" w
  109.   "  ①②③/ ?- G% n$ j  s5 }
  110. 1■ ■
    3 l3 Z# ], t  y  {2 W! d! \1 O; r4 d
  111. 2   
    / e* c0 H; d( [& `9 a
  112. 3■ ■
    % e' }3 v: d$ U1 A
  113. 现在,只有四个角落的窗户被打开。
    ( G0 ?: c/ E2 {' M
  114. 请输入“22”完成最后一击!"
    8 d2 q6 A% R) Q0 k
  115.   "  ①②③
    . Z0 _, Q2 m  j: u2 m
  116. 1■■■
    ; W# @/ h  t# c4 \- }& h. E+ z
  117. 2■■■
      U2 J( f8 d- |
  118. 3■■■
    ' y- L; ~0 p# Z0 _9 j
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    ( Z7 U4 S5 x+ W" `4 r  l& _5 `

  120. 0 l" p) v1 T0 @7 E% d
  121. ;;; 棋盘
    ) T9 v' W  Y9 L" X2 u; M, Y
  122. (defconst *wechat-5x5-white-chess* 12288
    0 w  w' I0 ^2 g
  123.   " ")
    7 y( C) O+ z% s/ E' D* T' q4 G

  124. , `! s* R4 p& c
  125. (defconst *wechat-5x5-black-chess* 9632
    % \7 B) Y5 G3 |5 m- A; b+ U
  126.   "■")" Q7 m8 M( H6 f" m1 Q

  127. , u) f! K8 u/ ]' q9 F
  128. (defmacro with-board (&rest body)/ J! N$ F6 C+ Q) E2 G/ w
  129.   `(with-temp-buffer/ y7 @% Z- S. w  k2 ?/ ^
  130.      (unwind-protect
    . g0 ~/ l6 [7 c& d3 r
  131.          (progn8 B. Z7 x- n, E- Z
  132.            (if (session "board")
    6 R! K# U2 P" R3 x1 n4 Q) p( M) z: d
  133.                (insert (session "board")))) Y" M4 U: U& h0 o- ^
  134.            ,@body)
    * C5 U& a6 U: Y7 A  n% Q+ C
  135.        (session "board" (buffer-string)))))  b2 G. o* L/ e5 l& J

  136. $ Z8 M! O% Z  i- P
  137. (defun board-init (size)' l) z0 W3 u0 s
  138.   (session "size" size)
    ; h9 b! c1 T, |
  139.   (session "step" 0)
    / g) |0 l( Y5 X: J9 S
  140.   (erase-buffer)6 u% d6 K# I' _- }8 X" d
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))% \/ w! ^- D5 L: J% k2 s- ~
  142.   (dotimes (row size)6 j; G* N6 Q* T1 ?4 d
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    1 |' e2 r3 P+ x
  144. ) e  v8 S- B4 q0 K6 l! |( G) B
  145. (defun board-contains-p (y x)& i# L4 o% X: d- A8 |* b
  146.   (let ((size (session "size")))
    4 B6 B$ t2 a" R, z6 l6 |' K
  147.     (and (<= 1 y) (<= y size)
    * @4 s4 k) W' r7 X+ c
  148.          (<= 1 x) (<= x size))))
    1 {/ B0 T7 Z' ~3 I

  149. + b5 A+ v  b# Q4 c$ Z
  150. (defun board-toggle (y x)
    * Q0 w+ n0 E& j- d, m! T& C
  151.   (when (board-contains-p y x)
    ; b$ z, n5 E  p7 {* N
  152.     (goto-line (1+ y))
    * [6 [8 N- M6 R6 I
  153.     (beginning-of-line)0 v& ^4 e* r8 }8 k8 z7 r$ y6 q; q/ P
  154.     (forward-char x)
    ( o. k9 }% Q+ Q. j. i) Y
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))) L1 `$ t$ Z$ V$ w4 H0 [
  156.                 *wechat-5x5-black-chess*
    % t5 Q, Y+ @. C5 O2 H- w
  157.               *wechat-5x5-white-chess*))
    - L7 Q& {- K+ `0 N
  158.     (delete-char 1)))
    0 ]) T" W4 l& b1 }: X) a

  159. ; x, ]" ^6 v1 d! l: R  E; S, k
  160. (defun board-put (y x)9 G# G2 g$ A& i4 w  `0 `
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    ! U, X) T/ B2 y1 K# \% w+ R" Z. y
  162.     (board-toggle (+ y (first dir))
    . C+ G/ C0 n7 r/ |0 L
  163.                   (+ x (second dir)))))
    1 H  G( m8 ?' C

  164. 8 C7 U: G/ v2 S( [5 ?4 a$ U
  165. (defun game-over-p ()
    ; l- u9 B; n7 x! ~
  166.   (beginning-of-buffer)
    : y$ I! `' V' ~# t+ L9 ~
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t))), n. X' F9 o- U$ O

  168. + z. Q! Y5 c# O; i; P9 w, ^# r
  169. (defun board-show ()
    . ?, E. e, N+ L- G; ]- o: E' r
  170.   (with-board2 h1 j+ c5 L4 Z5 q, ~* a
  171.    (concat (buffer-string)
    6 c; C9 K# r* Q
  172.            (if (game-over-p)' B8 Z+ P: c% \6 E3 @
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    % w6 g4 j  ^' V5 J
  174.             (format "第%d步" (1+ (session "step")))))))& P9 f2 S* ~& a* c4 p. ]! X
  175. 5 W; E$ @9 I; ~0 f8 h+ ^) m% z
  176. (defun board-position-parse (cmd)8 }) s4 }9 N1 i' G- b- @8 V; G/ r
  177.   (if (= (length cmd) 2)
      h- O% M7 }9 G/ o
  178.       (list (string-to-int (substring cmd 0 1))1 I6 I% d4 R, l7 ~: k( q
  179.             (string-to-int (substring cmd 1 2)))0 \; z; _* \- z
  180.     '(0 0))), {' W2 I7 `! |4 Z% }. T6 {  U
  181. 6 f$ U7 g+ Z5 u: h
  182. ;;; 游戏房间
    # \# y. q. i# P
  183. (defun game-room-init (cmd)8 _: ?, `3 N1 X5 q5 X5 \- s+ j- X
  184.   (let* ((middle (string-to-int cmd)). _2 T4 ?" Z2 R% Q
  185.          (size (1- (* 2 middle))))* `% ~) a! f" ]. q4 p( u
  186.     (with-board1 J6 e" `5 x+ G/ l& g- B. d
  187.      (board-init size)* q, c9 T# C8 j4 ]9 ?, v$ r
  188.      (board-put middle middle)))
    % x' u: H$ V& O
  189.   'game-room)# G8 p# A) ~. F( G9 e

  190. - ]( K2 W* S( _# R7 H
  191. (def-room game-room+ q; f- L* _, m! \
  192.   #'board-show
    4 A. W( V2 U) h+ D6 N
  193.   (t (lambda (cmd)
      d. {% w' b- w* W& g# a
  194.          (with-board2 U% E# h3 i( L  o3 B+ y5 }
  195.           (if (game-over-p)% W- m! q- d; x1 o9 N2 q8 G. q
  196.               'living-room( D% U9 n) p# ?* V" k9 ]
  197.             (destructuring-bind (y x) (board-position-parse cmd), Z+ L9 R0 Q$ a( @
  198.               (when (board-contains-p y x)1 i! c& n; o! M% }9 N
  199.                 (board-toggle y x)2 I4 M- F" }  z- s; f0 G" I, C
  200.                 (session "step" (1+ (session "step"))))
    5 g1 s7 U3 q- O5 o
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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