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

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

  1. 4 T% G: n6 r1 O
  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;">;; 定义新的游戏地图3 H- u# e9 L' L3 P3 N4 L8 z
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    7 m; N' b! y. Q$ D( j/ p7 N
  4.   'tutorial-room-0)                     ; 默认的入口( S" G8 a0 Q% C& M

  5. 3 p* E& f) y4 n; |! X2 v
  6. ;;; 游戏大厅; D& v3 s6 r1 f$ x- \
  7. (def-room living-room8 Q# n7 h* m- s+ N" |
  8.   ;; 进入该房间后的提示语
    & W( \% w% F, E" Z. F
  9.   "1. 教程  ]2 A' G- Z% [! |
  10. 2. 入门(3x3)1 u5 N; `# Q" ]8 v# C
  11. 3. 初级(5x5)3 J* {- ?! [6 F, l
  12. 4. 中级(7x7)& n5 }! q, T* h" p4 q
  13. 5. 高级(9x9)
    , z. k1 E3 S4 P
  14. 0. 关于作者2 [% H3 |! s# J+ f
  15. 请选择1-5开始新游戏:"
    5 ]- w- G: z/ E" P; T+ Y( D* U* i
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名$ y  w! Q, t0 s' t$ P. W" q
  17.   ("1" tutorial-room-0)
    3 _7 z! t( k& J
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配5 C* F& U2 q, X+ L
  19.                                         ; 相应的返回也可以为函数,动态返回房间名; ?+ O. ^& L: _$ n6 z6 M
  20.   (t living-room))                      ; 如果条件为t,为永真: U3 A, |0 [5 q. t6 x/ b

  21. + c* A1 d9 `$ e/ v, i
  22. ;;; 作者信息
    ! z8 N5 t0 B  m
  23. (def-room about-room  u" p" }  k& n* }- k# N
  24.   "作者:redraiment
    2 T+ `. l$ L2 N3 M
  25. 微博:http://weibo.com/redraiment
    2 q$ `" c+ ?$ _( H
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    $ e+ m$ H4 T; p8 ?6 v: W
  27. 请输入任意数字返回游戏大厅。"
    : ~5 L& B. i% _# _9 ]
  28.   (t living-room))
    & @2 ^3 _* i6 u7 y8 q

  29. ; R# Q4 o9 g8 H- [! o. ^: R0 Y0 e
  30. ;;; 教程
    2 i. z2 M+ F) j- E3 ^6 H- i
  31. (defvar *wechat-5x5-tutorial-rooms* 0: u  P, c1 R/ `% Q8 V0 l
  32.   "The number of tutorial rooms")
    % [2 P. D) i) s# i1 B6 ^# R- d5 B

  33. ; [  g9 s1 D1 [: {- d# ]% h
  34. ;;; 简化教程的定义
    0 K$ t3 W/ O3 @; j+ `
  35. (defun string-last-line (content)
    9 U2 i  ]4 R( z" q* D6 `$ U! S
  36.   "多行内容组成的字符串中的最后一行"
    ) Y2 Y- n$ p0 r0 i- t) X6 t: g! Y
  37.   (with-temp-buffer* a! q# S- s, ^  @0 `5 [3 y
  38.     (insert content): A9 B' W8 v) E. X' \# r/ s
  39.     (buffer-substring (line-beginning-position)
      U' {1 f  C( z4 W) G& g
  40.                       (point-max))))
    8 X3 G2 R; j5 V/ P6 J% r$ J
  41. ' _  V5 p" z1 b+ ]) q0 {
  42. (defun def-tutorial-room (prompt)
    % h: ]* ~+ H/ a, r
  43.   "根据提示语自动生成教程房间。
    & x! |' x: n& b) \

  44. * T- z4 g1 A! D: T( L) J" x5 D0 z  f
  45. 1. 提取最后一行作为问题;# F. y% Y( U! V* B, y2 \+ f8 m
  46. 2. 分析问题,获取期望用户输入的内容;
    ! q3 O: z8 E- {" S# }
  47. 3. 定义教程房间和重复提问房间。"/ a9 G& m& w2 _* f: {6 v
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))$ i& j* e/ q% q$ {5 K2 A  J
  49.          (repeat-room (concat room-name "-repeat"))
      i7 W9 {; B" b3 ^2 d6 H
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))7 n& K" v- M# H# w
  51.          (question (string-last-line prompt))
    : Y+ ]5 l2 k* W4 B' f! Y3 ?
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question); n0 d! X9 |: w" I- w* I  b) O
  53.                      (match-string 1 question)))4 C+ b  s7 o1 u& k$ N$ m) s8 e8 Q
  54.          (doors (if except% [4 |' t. w) W# {! w4 v
  55.                     `((,except ,(intern next-room))2 y' e. j! I/ i- h; v1 @/ z
  56.                       ("q" living-room), v7 u: i  ?* P' k2 I7 U
  57.                       ("Q" living-room)
    - d3 i1 O) P3 m. ]- l5 Q
  58.                       (t ,(intern repeat-room)))7 a' C6 n+ l% D& C5 g) T
  59.                   '((t living-room)))))/ h8 c3 Y" I: o% [
  60.     (def-room-raw (intern room-name) prompt doors)# n9 a3 ~8 b( w
  61.     (def-room-raw (intern repeat-room) question doors)))
    + k8 L6 {" t7 f7 B8 f3 F* E  i

  62. : H$ O9 z  H6 E# }0 d
  63. (defun def-tutorial (&rest prompts)
    # x9 H; _  U! l, g2 U
  64.   "批量生成教程房间。"
    % o6 B  h9 M, h
  65.   (dolist (prompt prompts)! E9 ~" y9 Z: x) j  j; H
  66.     (def-tutorial-room prompt))). r. L( H1 z" ]" z

  67. 7 u! n6 N0 Q! J3 s
  68. (def-tutorial
    % w5 K: ~: f! N% k! X, h
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。7 t0 f) ~7 Z! L1 t
  70. 1. 教程
    ; r8 H* l6 v1 y5 G& E7 [+ ^* ?! d
  71. 2. 入门(3x3)6 e% v. d- Y" j
  72. 3. 初级(5x5)
    - X& f  ^, m: n& h8 f
  73. 4. 中级(7x7)% K# G% T* W( f5 u- y/ T
  74. 5. 高级(9x9)5 _  e# G6 G" w7 F" Q
  75. 0. 关于作者
    4 j9 O. ~4 g0 ]* Z3 y
  76. 请选择1-5开始新游戏:
    3 h2 ~( z* e! G+ D3 }& f( @+ N
  77. 您现在正在游戏大厅里。
    * M5 g+ l. m; F8 K& t# ~2 H1 r- D8 ?
  78. 请输入“2”进入入门级房间"
    # g% e( t: n3 x& \1 R  C/ T6 A
  79.   "  ①②③& e8 n: L" }+ V3 e- s% A
  80. 1 ■ 7 k9 O1 M) o4 k# }0 d6 ?) \
  81. 2■■■
    % x% l. W& I! n! b6 R
  82. 3 ■ ; F- a6 o& E" T. |$ o; A+ i
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!2 Q2 f6 i4 B' q3 j. i
  84. 请输入“22”来关闭第2行第2列的窗户。"
    . t+ }' d4 ]- ~5 s6 y
  85.   "  ①②③
    + t, n' W* ?* Q# f7 N; E" F
  86. 1   # y. t; X( m9 q* C/ X/ ~7 I) N9 d
  87. 2   
    : S0 v8 v' N5 Z4 ^* c
  88. 3   2 d+ P6 p+ x2 n( m# p( p
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。: u" |9 L1 @6 C, F- s3 |
  90. 请输入“11”,试着开启左上角的窗户。"
    . z4 u2 U: q# U& f
  91.   "  ①②③
    - Q* b$ B3 Y1 G: O" T
  92. 1■■   r' L% |8 c/ q% p
  93. 2■  
    & Y8 i- l! T$ M. w$ \& ?
  94. 3   " n$ w/ X( f0 S2 \: A9 j
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    $ B' P6 K0 E4 B3 L8 E/ J
  96. 请输入“13”开启右上角的窗户。"
    3 ]5 Q* b& x' Q" v) a& L
  97.   "  ①②③( m3 `0 T! ?2 e9 v6 y0 C3 B
  98. 1■ ■
    2 S5 c6 |- Z& l/ ^$ W( l
  99. 2■ ■$ [) D% t( A  D! [" H
  100. 3   
    : m" r# Y/ m; c. X8 R
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。0 m( g+ C; M0 d" \. r6 w6 ^
  102. 请输入“31”开启左下角的窗户。"
    4 A8 \6 d5 O4 Z7 g
  103.   "  ①②③, u* p/ q5 R  h3 o  q/ g5 t7 B
  104. 1■ ■, A0 O, c+ h- s
  105. 2  ■1 a: S. ^$ {: o0 A" ]
  106. 3■■ ( N; E! j7 ?, L. k( ]; V; j) e8 J
  107. 此时,总共有5扇窗户被开启了。
    # _" u. v# Z$ ?5 L. k6 H
  108. 请输入“33”开启右下角的窗户。"
    ; c. l0 m+ i7 K. J9 q
  109.   "  ①②③
    / A" U. R$ ]" G1 \- n3 j
  110. 1■ ■
    ) X) R$ K' D' }  s
  111. 2   . W9 e& j( }. z3 D, ^. Y  ^6 y
  112. 3■ ■
    " i/ M9 r/ E* L9 j$ B0 y5 [
  113. 现在,只有四个角落的窗户被打开。( `, x/ X9 r% N
  114. 请输入“22”完成最后一击!": y. ^/ Z" ^( z3 E8 D0 A3 N, h) F
  115.   "  ①②③
    5 G2 L0 K( Q8 F& D" j( X. {
  116. 1■■■$ F, N6 F" t+ h9 m$ v/ ]
  117. 2■■■
    1 T, S! t3 M  L, Z! R1 V
  118. 3■■■
      a7 b+ Y, b6 Z# N, K- L6 B
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    & u! U$ S! R3 [( y% E( A) B

  120. 0 q6 O, {% j3 N0 m1 @
  121. ;;; 棋盘% p# h7 ?4 _' z7 `. ~+ M* u& {
  122. (defconst *wechat-5x5-white-chess* 12288* }7 C# N0 \6 E, N. J* u0 M" j% K
  123.   " ")
    $ `# |" F- G/ a, l2 H
  124. 6 Z) f8 g. t! g1 v0 Z5 b
  125. (defconst *wechat-5x5-black-chess* 9632
    ; P: z0 V( \+ g4 v! @  n
  126.   "■")
    . F% `3 k" L: q  K
  127.   S6 W0 x) {& H$ D! x
  128. (defmacro with-board (&rest body)
    - v# e! d/ a' v+ U5 N' i
  129.   `(with-temp-buffer( P8 q: l- f9 z( L
  130.      (unwind-protect
    % Z; |& m. o" b0 q
  131.          (progn. |( G% p0 p' P3 Z
  132.            (if (session "board")! @4 L# y# g- O; {' p
  133.                (insert (session "board")))4 U' c. I! _) D# @7 B! R" M. D2 P
  134.            ,@body)$ f5 n2 I( p% t( q3 c' ?. U6 [
  135.        (session "board" (buffer-string)))))# s3 [& s1 D# v* H! _  E
  136. / B- _- l# K) l. m& f. k# l# V
  137. (defun board-init (size)
    , L7 S9 M6 e0 ^; Z
  138.   (session "size" size)
    . [/ h. W7 |: H1 X: A
  139.   (session "step" 0)
    ! |' k- Z% d% y* A
  140.   (erase-buffer)
    ! e! t$ Q2 _) p! v. O
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))* N7 p: M: f% _
  142.   (dotimes (row size)
    ; Q% ?& |# J' a5 b; p& E
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))8 z7 A- R) c; d/ t% b" ?( J; @) Y
  144. , o8 S5 \) V( U6 _$ _) x/ {" l0 z  Z
  145. (defun board-contains-p (y x)- I/ C3 ]1 b) p" S& v
  146.   (let ((size (session "size")))
    4 z! }0 h( S/ l8 L) d2 _
  147.     (and (<= 1 y) (<= y size)8 L3 p6 S  O6 x. j! k
  148.          (<= 1 x) (<= x size))))
    & G4 K9 H6 Q9 N4 E3 y4 ~! r
  149. , a9 ~5 A( d# O# ?' ]( H; {
  150. (defun board-toggle (y x)
    7 B& C% f3 T3 T6 y
  151.   (when (board-contains-p y x): t& q- h2 {8 H2 W' q  A7 D6 R" u$ ]
  152.     (goto-line (1+ y))
    8 G% y7 Q3 |5 M2 c# x' R+ _
  153.     (beginning-of-line)) o0 G$ e1 I8 f  B  B
  154.     (forward-char x)
    4 s( r$ `7 L; b( Z& I$ N
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
      M$ D2 A8 _1 U% G/ }
  156.                 *wechat-5x5-black-chess*
    0 S8 c. f3 J& p% Q; j+ l# t7 z2 f
  157.               *wechat-5x5-white-chess*))! a7 f9 b! j" ~# ]
  158.     (delete-char 1)))0 n3 M1 q4 H$ ]+ ]
  159. . A- y7 }& i) s
  160. (defun board-put (y x)
    ) M; }. X$ I4 v. v* S3 Q
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    2 ^6 f. k7 i3 M. |# q" F
  162.     (board-toggle (+ y (first dir))# V& r* S) A6 ]) W
  163.                   (+ x (second dir)))))
    " ]/ B/ Y! ^/ {7 O% ?
  164. $ S) e* ^0 p# V
  165. (defun game-over-p ()  {  d1 k% E- y3 u& h& i
  166.   (beginning-of-buffer)
    ; F/ j* H  [' x2 D5 F9 J/ A
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))5 ~7 R3 r: j; R6 X+ y
  168. + ^( W; y* U0 ~; w$ m3 J  {
  169. (defun board-show ()
    7 H* g9 E9 s# u3 ]( j% \* g& u: j
  170.   (with-board6 B# Q! Y8 Q/ z. u
  171.    (concat (buffer-string)
      g1 d/ x) X4 Y/ O
  172.            (if (game-over-p)
    1 ?+ v4 g/ v, _, w# t2 S% t
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    % ?1 |+ D3 g; e, L) B
  174.             (format "第%d步" (1+ (session "step")))))))( H" u4 e, \$ [( c$ c+ n1 R4 z

  175. 3 t4 f% b  T, F7 ^
  176. (defun board-position-parse (cmd)8 K8 f. ?. Y0 \/ D+ [3 u) a) w
  177.   (if (= (length cmd) 2)8 e$ M. ^% t2 o2 h; |
  178.       (list (string-to-int (substring cmd 0 1))( [/ U- U1 G6 f" N7 L; B  w9 L
  179.             (string-to-int (substring cmd 1 2)))# Y- @2 ]0 C  L3 ?3 k' P
  180.     '(0 0)))/ C6 b: d8 i5 u: |" A* d
  181. / F" r* ~- K/ @9 S! {3 k
  182. ;;; 游戏房间& U6 O) j3 g% E) K0 _
  183. (defun game-room-init (cmd). Q: b( }3 N/ R0 b
  184.   (let* ((middle (string-to-int cmd))
    ( A  M3 K/ W! D" q
  185.          (size (1- (* 2 middle))))
    ( x; P# [9 A- q5 v3 |- {
  186.     (with-board
    6 ]" M5 j6 E5 B. ]4 K
  187.      (board-init size)
    , X' m% c+ u+ U# r# @' O0 `4 D
  188.      (board-put middle middle)))
    ( u; q% n* k( b3 |1 l
  189.   'game-room)
    1 u' T! C3 s: H) |

  190. - }: V1 N7 Z* r# [
  191. (def-room game-room
    % ]4 W3 g, |6 k4 D5 B7 n7 C
  192.   #'board-show/ ]3 }  N" O: W+ F; w8 `" r8 c
  193.   (t (lambda (cmd)2 m$ k+ I" l5 {
  194.          (with-board
    # V$ C& N7 A! q: q2 h& }) B
  195.           (if (game-over-p)
    , A) ^$ L; `, @- W* W0 {
  196.               'living-room
    . B' x8 D# ^. q. O% u1 m: B- |
  197.             (destructuring-bind (y x) (board-position-parse cmd)$ K% @+ C4 K0 D: N/ b
  198.               (when (board-contains-p y x); e2 S) n$ m$ A
  199.                 (board-toggle y x)
    / W/ ~% u& C  G" |3 C* ?2 d7 v4 G
  200.                 (session "step" (1+ (session "step"))))
    ( ~# g3 p  k5 @# i0 g
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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