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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。+ L' M0 Q# o: Y8 E
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. 5 k7 V9 F# }+ B$ H2 K
  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;">;; 定义新的游戏地图; E% }# f3 ]7 M7 j
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    9 \1 }; s, Z( g, f" \4 o9 M1 Z
  4.   'tutorial-room-0)                     ; 默认的入口+ K8 M9 \; J" d/ A9 R: }

  5. + n) D" T/ V- M6 z" a: n: ]' l
  6. ;;; 游戏大厅
    , d# ^+ t% q7 O" m& V( |; g
  7. (def-room living-room6 S; Z, Z; _' S
  8.   ;; 进入该房间后的提示语
    0 K+ G! r, G5 f8 H4 Z: r0 b% j( x0 I
  9.   "1. 教程
    & h) h  x, ?$ Z5 r, \& t
  10. 2. 入门(3x3)
    % b, g( ]: g) z: S. ]# B" n
  11. 3. 初级(5x5)
    0 i) ]: V/ F1 S/ y" C
  12. 4. 中级(7x7)! B. h" P+ D8 p  W; b- x
  13. 5. 高级(9x9). ]* h' n( f8 g# V$ V
  14. 0. 关于作者  c! w5 e1 u$ g! R
  15. 请选择1-5开始新游戏:"
    4 m- S! w. F; N" _8 J
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名9 }* D; |! b. m# q5 H2 R
  17.   ("1" tutorial-room-0)
    ( C7 ?0 n/ t/ ^+ y6 E# `" \
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    - H* i7 Z; D" E8 c! Z, E
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    " |; v" I0 o' I! i6 a) o2 [/ o
  20.   (t living-room))                      ; 如果条件为t,为永真$ f$ P2 r; p/ Z! l

  21. : I0 s8 ]( X( Z) ^3 k; ^8 g. U- p3 f
  22. ;;; 作者信息
    + E  J: C- {4 n, \9 R
  23. (def-room about-room( ]7 u9 s& n6 K( r7 K5 J
  24.   "作者:redraiment
    # s! j6 @& }1 u4 N2 F
  25. 微博:http://weibo.com/redraiment) u8 E; ]* y& v! a, _0 S9 i
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    . @, K6 d' r  a6 z6 u: p) a
  27. 请输入任意数字返回游戏大厅。"4 v: _% [2 R1 C
  28.   (t living-room))2 E; a/ {) W6 h" w: o1 \7 p

  29. 2 s. J" B# q7 T' c- f1 @
  30. ;;; 教程
    ; R/ p3 z2 q1 `/ n& Q
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    ( j' j0 U& o3 S" {. V
  32.   "The number of tutorial rooms")# x( U0 H5 g+ w* I
  33. . n5 L0 O9 U" j4 z; Z3 Y$ ~
  34. ;;; 简化教程的定义
    % ]* G. q" ]' Q* C; I0 j( A
  35. (defun string-last-line (content)
    5 ^; S/ b5 E: n0 j7 N9 F
  36.   "多行内容组成的字符串中的最后一行"" s% r2 g( e: B
  37.   (with-temp-buffer' R1 q; y" r8 M* d' n) N7 I8 L5 D# A
  38.     (insert content)
      Y7 W8 }3 c# N* _' ]  f! f& c; C6 q
  39.     (buffer-substring (line-beginning-position)
    5 n' V* _# d) k( V' Q( t
  40.                       (point-max))))+ W2 s; ?, j2 I2 \" V
  41. 6 }' H1 }/ `* \) p! S2 n
  42. (defun def-tutorial-room (prompt)
    * i0 p* @, }3 n
  43.   "根据提示语自动生成教程房间。
    ) x2 G( r7 }0 x- d

  44. 6 W; ^2 P& T7 w( ~7 q( Q
  45. 1. 提取最后一行作为问题;
    - i9 Y& X8 y; |( w- [1 O8 |2 ~
  46. 2. 分析问题,获取期望用户输入的内容;3 h: ?8 @3 P8 |* C. d0 u9 y# b
  47. 3. 定义教程房间和重复提问房间。"
    1 O7 c* d2 g3 J
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    8 u1 s/ i; f" ~, Q$ m; g' }% b
  49.          (repeat-room (concat room-name "-repeat")). z" k& G# U+ u: s
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))9 B6 B- ]* `* b& S4 b3 A+ Q7 ~
  51.          (question (string-last-line prompt))% {$ n: x6 T$ S8 c  p$ f* X6 h7 U( F# U
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)9 M/ G, \: n0 H0 Y. d
  53.                      (match-string 1 question)))2 P5 N0 ]' m! a+ X/ k5 B; S
  54.          (doors (if except
    6 V; U# _" `+ h& C$ c
  55.                     `((,except ,(intern next-room))
    ' B8 P9 B, ~6 Q/ ^. m
  56.                       ("q" living-room)
    3 P+ _7 P* o" \; u2 z
  57.                       ("Q" living-room)4 {# ]2 w" i2 n- h) l3 J3 c( q
  58.                       (t ,(intern repeat-room)))/ }8 j% g7 j0 g& z
  59.                   '((t living-room)))))" j( o( V" a: A) f  V
  60.     (def-room-raw (intern room-name) prompt doors)
    ' _/ W% N. R! O6 ?( R# h6 y
  61.     (def-room-raw (intern repeat-room) question doors)))( u7 G- s5 H/ ?) F1 c! C+ G

  62. ) c7 f' |( Q7 r0 G7 Y4 y5 W
  63. (defun def-tutorial (&rest prompts)
    2 m" B/ q* f, Q. h0 s4 n
  64.   "批量生成教程房间。"  O) C+ }2 o7 _4 U4 |+ H0 K, h
  65.   (dolist (prompt prompts)
    ) v; X' Q; a/ T: |/ e9 m- t
  66.     (def-tutorial-room prompt)))
    6 r- n/ @7 o9 c/ b  d- v

  67. 6 X4 e: \) L9 C) O' |- p/ B- }
  68. (def-tutorial
    - M( c" ?( C$ I4 h7 x, [2 h
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    . Z9 b0 \9 @$ E- X" ^
  70. 1. 教程6 s% ]0 O1 }  H" _* @8 i
  71. 2. 入门(3x3)
    5 E' t' [1 N. E" ^
  72. 3. 初级(5x5)8 |& `+ s* u) {2 b2 u& Y; Y
  73. 4. 中级(7x7)" _) ?5 U2 A+ F. P! K  }) y- i
  74. 5. 高级(9x9)$ Y+ ~5 M7 u7 v) Y+ ^' @
  75. 0. 关于作者: Q8 N2 F0 W. I" N1 {1 i& Z
  76. 请选择1-5开始新游戏:
    + O# C7 p2 _6 o  V- y  {1 y& g
  77. 您现在正在游戏大厅里。+ w& z! \- d) h8 O$ Q
  78. 请输入“2”进入入门级房间"
    + `" w2 x5 E( B& _
  79.   "  ①②③
    0 v8 f9 B5 R, i3 j0 P
  80. 1 ■ / v( J" g% I* d
  81. 2■■■
    & N' d' Q1 c* [- L0 C- ^7 K/ s
  82. 3 ■ 
    3 o/ C( l+ J$ Z1 ~2 j9 n  I
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    , k4 K) U' J) N$ Q+ ?
  84. 请输入“22”来关闭第2行第2列的窗户。"8 m; R+ O% ]  l( V! o9 x
  85.   "  ①②③
    & S* K+ l$ E2 @3 a3 e0 R
  86. 1   
    ' O$ A6 s4 a% Q( f
  87. 2   : Q( S# ?$ k0 R1 b+ c
  88. 3   . _% o. d0 O( Z' E8 s8 i3 ]4 _
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    ( c6 w1 m0 J4 T3 J
  90. 请输入“11”,试着开启左上角的窗户。"
    6 {( _0 Q- k* N
  91.   "  ①②③
    : j. E- e2 y' R
  92. 1■■ 
    # v0 E2 v* ~. |3 a
  93. 2■  
    1 Y6 @: j2 X5 r- _2 r  k
  94. 3   " u8 ]9 k8 e  }3 h% Q$ [( q  h5 x
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。( D+ F+ @. {! o2 Y0 d0 c3 q
  96. 请输入“13”开启右上角的窗户。"
    ! m5 v' X: q' @
  97.   "  ①②③
    , I3 |  B6 ~9 f+ p# e2 Q6 I% A0 @
  98. 1■ ■
    " B3 x! l/ |& a' M" _4 P" e+ o
  99. 2■ ■  ^5 ]) |2 A# J; k
  100. 3   
    / g% U1 i2 ~6 N9 n4 g1 Q: `8 ^
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。1 |9 q/ y' O7 v- ^
  102. 请输入“31”开启左下角的窗户。"6 g4 ?$ Q4 L% o! X, ~
  103.   "  ①②③
    2 u7 R1 U, t0 n. w# g: m4 l& h
  104. 1■ ■
    3 s. I! ^9 K$ b- p& v; E
  105. 2  ■
    * i' U  O3 ]# h
  106. 3■■ 1 W. Q8 c1 X  ~+ W1 _5 h8 s
  107. 此时,总共有5扇窗户被开启了。
    ' L) X( k# K% Z) A/ [
  108. 请输入“33”开启右下角的窗户。"
    ! V! M$ f" D& Y8 R# ^
  109.   "  ①②③
    ' h, b2 Z' T5 `0 w& Z
  110. 1■ ■2 g; j* v8 }. N
  111. 2   + G* M& o7 Z  F1 J* ]1 r/ g
  112. 3■ ■4 D) Q% \2 k# q5 B" S
  113. 现在,只有四个角落的窗户被打开。. @! |' x; W, F) `
  114. 请输入“22”完成最后一击!"6 F6 U" j5 G6 h+ A9 V7 t8 H
  115.   "  ①②③
    ) r- |/ d  j" u, x& v
  116. 1■■■' Q# q* N2 y, u3 \
  117. 2■■■; N' S0 B+ k! w, U
  118. 3■■■
    ; q/ D6 c: D6 U' C1 Y) i( e, Q
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")  H' J. W/ ^. [' R+ s

  120. 4 B& P  Y# v/ Y0 u' r. Q& ~
  121. ;;; 棋盘, y; ~7 @/ G8 J5 q) @
  122. (defconst *wechat-5x5-white-chess* 122887 q% G4 W' ]  u3 w" A
  123.   " ")
    / {( d$ x: N' l  N
  124. 8 n, B6 G9 Z- G2 G" q
  125. (defconst *wechat-5x5-black-chess* 9632. D7 F& I5 n* x; S" T5 f- V
  126.   "■")
    7 @# t# {* b! e( H0 r; k1 c- c
  127. & {# f% q: S0 S8 Y" {) P1 C
  128. (defmacro with-board (&rest body)
    $ o/ D* J3 v/ b0 z" W( K. h, n
  129.   `(with-temp-buffer; Q% \1 l( {+ `
  130.      (unwind-protect# R8 U- Q5 d" l
  131.          (progn- ^! H+ m) l, z  l$ i" ]
  132.            (if (session "board")8 C* t% Q7 t+ g# @: t1 w2 z+ {$ r
  133.                (insert (session "board")))
    # w6 f3 I+ f$ M# Q' g9 B
  134.            ,@body)
    . V1 I4 I0 ^6 a! b" X
  135.        (session "board" (buffer-string)))))
    + V7 |: U: s3 U% H9 v, K$ G5 d

  136. 4 M- c' B( H  d) _. \7 o
  137. (defun board-init (size)
    / I) U1 S3 ?8 K. T1 k0 A
  138.   (session "size" size)# k0 U6 c6 ]: D1 \# J- l$ v$ U
  139.   (session "step" 0)
    ' r% ]. E% ~" i: ~
  140.   (erase-buffer)& L) ~- E( P  Z
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))' ^/ O  E7 X3 d1 q0 p
  142.   (dotimes (row size)) K0 B) |0 m1 J! |9 x/ ?8 ~6 a
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    6 d) \7 B# T) z0 T

  144. % d; _( N$ j! B7 F  t/ `+ G' H
  145. (defun board-contains-p (y x)' y7 t! D$ `9 E) K0 p
  146.   (let ((size (session "size")))0 ~+ y  L8 Q) U3 |: ~' K/ K
  147.     (and (<= 1 y) (<= y size)3 U1 {8 p& ^6 t6 y4 |9 E
  148.          (<= 1 x) (<= x size))))
    , I% E7 a  ~3 l* ^& N

  149. ; W5 W7 c- ]; ~: z9 z
  150. (defun board-toggle (y x)" ]5 C3 X4 E! N9 L* }  |
  151.   (when (board-contains-p y x), U- [' w) n' {) T+ ]8 \$ ?
  152.     (goto-line (1+ y))* L# [# A5 a! [4 }6 }0 |2 {: r
  153.     (beginning-of-line)5 G* z5 |+ K! l  A0 E9 ?
  154.     (forward-char x)' J4 k6 h3 M0 E# \, S& L$ ^
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))4 v3 r, J6 j; |; p
  156.                 *wechat-5x5-black-chess*
    ) @6 [# D# @; g( h% Q
  157.               *wechat-5x5-white-chess*))3 Y, e( W, K  h* @8 B
  158.     (delete-char 1))); k& Q7 q/ a/ i+ B' R
  159. : N2 t' A: u: g6 p, m
  160. (defun board-put (y x)' C- j+ m5 l2 t7 P/ [7 n/ ?) m
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    7 E  D/ f5 {# t
  162.     (board-toggle (+ y (first dir))
    8 @2 ?% n+ f) P7 m
  163.                   (+ x (second dir)))))) D! N  c5 M8 [8 a# P

  164. , I& T( J5 t7 \2 W8 p
  165. (defun game-over-p ()
    + G0 o* r5 q- q: X
  166.   (beginning-of-buffer)
    5 T" ~" o) Y1 M; n% n, v
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))8 H- r/ c( X2 y" y) G

  168. ) V4 j( e+ e9 L7 p& W  N2 O
  169. (defun board-show ()
    - P' l& _' u& w: @
  170.   (with-board" M! m, W- p5 Y& s- {- ~) c  F
  171.    (concat (buffer-string)
      O* n7 _& V1 g! Z3 m+ C5 R- f
  172.            (if (game-over-p)
      W4 |- K) G7 e* f  G
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))2 E' Z# G  S1 z+ X, ]; E
  174.             (format "第%d步" (1+ (session "step")))))))
    ; i5 {" Q8 j, p  Q5 \! [

  175. * c' B; ]* E% q! Q& ?
  176. (defun board-position-parse (cmd)
    8 i8 Q( N: M( A
  177.   (if (= (length cmd) 2)
    " N; }* f1 e* F! p
  178.       (list (string-to-int (substring cmd 0 1))
    * Y9 Q0 P6 O" W- n' t# Y7 {6 X7 X1 t
  179.             (string-to-int (substring cmd 1 2)))) m  X% d* x8 y+ e! G+ _4 u# @9 K& M0 B9 S
  180.     '(0 0)))
    0 e5 k0 E3 j/ @& ?

  181. + x% E) _3 u# w
  182. ;;; 游戏房间/ N& g; k4 M2 S; X( o
  183. (defun game-room-init (cmd)) t- l- C8 Y& k- b! a! k3 W( ]
  184.   (let* ((middle (string-to-int cmd))2 `0 ^% m! `0 j5 ~8 B
  185.          (size (1- (* 2 middle))))
    ' }; ~# j: E1 u3 \. D% f
  186.     (with-board; C/ ]6 |; S8 o) F7 {4 r
  187.      (board-init size)
    9 x% |; Q) W* T6 J5 x+ t
  188.      (board-put middle middle)))
    3 b4 w' p/ ]" d7 Q& i1 G" j; A9 j: w2 q( Q
  189.   'game-room)+ i4 Z7 d* m  a* t2 N$ m  ~, S5 Q
  190. ! N2 N, x& a+ ~$ G
  191. (def-room game-room
    2 `" \% \7 n9 [. v; H# F" \1 O& l
  192.   #'board-show# O% l8 @- j9 Q
  193.   (t (lambda (cmd)
    3 V. p; j* I# o3 b7 d3 d. N( ^5 ]
  194.          (with-board
    ) a7 }3 E0 Z( q0 Y4 K
  195.           (if (game-over-p)
    2 R! _& H7 T# O$ u
  196.               'living-room
    ; a6 {3 K' D7 |; o3 A& Y# ~! Y$ x) r8 W
  197.             (destructuring-bind (y x) (board-position-parse cmd)( [# T: C3 D! v, Y6 A
  198.               (when (board-contains-p y x)
    " d% @3 p2 C" s6 N4 I
  199.                 (board-toggle y x)
    5 w3 m) u2 S5 ]- |0 w: s
  200.                 (session "step" (1+ (session "step"))))
    " Z  @" J9 ~+ u6 g: C2 M* {* ]6 B
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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