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

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

  1. $ O' Y1 c2 T( D1 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;">;; 定义新的游戏地图* ?* n% _9 w4 K( Q) u- r. e% F$ j
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    5 l/ c$ e5 |2 ?3 ?& G. T! [
  4.   'tutorial-room-0)                     ; 默认的入口
    * ]: |. t* l; i
  5. 6 \7 f) W- ^6 G; |1 i
  6. ;;; 游戏大厅3 g! V2 X7 a# H" F& h0 ~
  7. (def-room living-room
    0 o9 t# Q' k, J3 C6 O7 \
  8.   ;; 进入该房间后的提示语
    % w& E4 v8 Z" u9 M2 x8 Q* x. }9 }
  9.   "1. 教程
    1 L5 g3 C0 U6 ^
  10. 2. 入门(3x3)
      T: s& J0 i( D% p8 _3 {7 u
  11. 3. 初级(5x5)4 B, j; R( I: E* E- K7 c, H3 \
  12. 4. 中级(7x7)2 J) }" G9 @; c" u6 h" O
  13. 5. 高级(9x9)
    2 V: V: @5 V) ~& v% w
  14. 0. 关于作者
    1 M$ k$ B% p* r3 C
  15. 请选择1-5开始新游戏:"% ?( n, r: M9 G/ j
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名9 o2 d& v1 d" n3 A9 L/ v
  17.   ("1" tutorial-room-0)
      C. m$ C0 k6 \2 z) T9 y) M# ^
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    ; o. |; E/ l/ U6 C' |! u# j
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    $ A2 Q' r1 y% _
  20.   (t living-room))                      ; 如果条件为t,为永真4 j) I$ s0 I" r5 E
  21. 8 z  M- ^8 X8 n4 P( c8 K2 x
  22. ;;; 作者信息! }# Q, p& K! z
  23. (def-room about-room1 r. x% v% L7 L9 G1 \! h1 d1 R0 |0 L# W6 i
  24.   "作者:redraiment
    % A1 a( \  y$ H* Z1 `; n
  25. 微博:http://weibo.com/redraiment
    . T) s" L+ Y! a2 c6 H
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    $ [& ^/ O# E2 B0 W; j; x- Y
  27. 请输入任意数字返回游戏大厅。"# i& w0 h+ X5 A2 B& I
  28.   (t living-room))
    5 y& z8 \7 V8 J7 d7 w$ q) k

  29. ! G: X1 `: p! y) e6 a. G
  30. ;;; 教程
    . ~) o. y6 c3 N9 B0 i4 T* R
  31. (defvar *wechat-5x5-tutorial-rooms* 07 Z3 z; _4 g, M& ^* S
  32.   "The number of tutorial rooms")
    # s& ~9 y1 Z: h. T' W" y) M

  33. ! O- h5 `) Y+ X" }8 W
  34. ;;; 简化教程的定义& l/ o' p- b2 _! t
  35. (defun string-last-line (content)( a& A( T9 b7 ~" [" ?# T# ?6 O
  36.   "多行内容组成的字符串中的最后一行"
    % A( |8 D0 P4 K2 r. ]
  37.   (with-temp-buffer
    , y5 _1 A2 L* P$ Y
  38.     (insert content): `! d" [  i9 g
  39.     (buffer-substring (line-beginning-position)
    ( _9 g, e9 x$ _; _  I" J) c7 E0 h
  40.                       (point-max))))
    . H( e/ ]" I4 T! u- _

  41. / w" U/ w  {6 D+ a( s3 d
  42. (defun def-tutorial-room (prompt)
    5 b0 Y; c8 t$ F( u
  43.   "根据提示语自动生成教程房间。
    # @0 @5 d* M$ W& Z: |

  44. ( r! D: r2 \( I% T! W& ]
  45. 1. 提取最后一行作为问题;
    & z4 l! d( Y* [8 q& u
  46. 2. 分析问题,获取期望用户输入的内容;
    . f7 L( d$ k" [1 [& S$ l+ m" c
  47. 3. 定义教程房间和重复提问房间。"
    + H4 ~7 u# C8 b$ D2 f' K. K$ {; ?9 G
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))0 p$ d. _3 h+ C. _2 G
  49.          (repeat-room (concat room-name "-repeat"))
    # ]+ H% \" a4 X6 I, e7 w
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    1 V2 J% c# e' B1 y1 Z
  51.          (question (string-last-line prompt))
    & ?) v. S0 C" [% u5 v1 [( z" y2 T
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    $ Q/ j* y) r. f7 }0 K" |4 E! q
  53.                      (match-string 1 question)))3 Y3 F. W7 a8 ?, {/ A5 F. N
  54.          (doors (if except( t7 _% d2 |) t7 K( A9 f0 B. D
  55.                     `((,except ,(intern next-room))
    . q: ~  d; j  I8 y
  56.                       ("q" living-room), u8 K8 Y. a- O
  57.                       ("Q" living-room)
    + _$ W( f8 `9 R' s
  58.                       (t ,(intern repeat-room)))% s3 w( T9 y1 y2 r+ M
  59.                   '((t living-room)))))- ]+ J: w1 t) G9 O! n1 n
  60.     (def-room-raw (intern room-name) prompt doors)0 n+ I1 X0 f; r% V- R
  61.     (def-room-raw (intern repeat-room) question doors)))8 j0 f) A- f8 @/ s( Z/ x

  62. ! D5 q5 k2 [9 I5 k
  63. (defun def-tutorial (&rest prompts)
    6 u* e1 ?2 T% e1 l9 ?! ?
  64.   "批量生成教程房间。"; r# Q" {1 y  [/ t6 |
  65.   (dolist (prompt prompts)$ X1 T- a* U  y9 r/ k' I* y
  66.     (def-tutorial-room prompt)))
    * t' j; z3 m/ ?$ s

  67. + ?) p6 O2 }4 u- W7 ~
  68. (def-tutorial
      g4 k$ [2 X7 @" A
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    " P0 O5 m# N1 |+ Z" r  E: w
  70. 1. 教程$ R. T3 [% K7 U& B  V
  71. 2. 入门(3x3)
    , [. ]' I; B. a( N  Y
  72. 3. 初级(5x5)
    2 M: X& A- u; F7 c$ N9 ]) s
  73. 4. 中级(7x7)8 P! \! x! W; S2 u
  74. 5. 高级(9x9)
    7 P, a  \6 m. ~$ w# O9 L& ]% y
  75. 0. 关于作者
    % [6 `9 S  i8 L4 \+ i
  76. 请选择1-5开始新游戏:) p( K: n9 |7 c4 x7 C' e, Y
  77. 您现在正在游戏大厅里。
    2 V! s" g  x$ {  P1 _) ]8 N
  78. 请输入“2”进入入门级房间"( e2 F2 _+ F5 E5 U! D# l. {9 A  l
  79.   "  ①②③$ _$ e/ A; z4 ]( J8 O$ j) R4 `0 q
  80. 1 ■ 
    1 r/ v. ]3 H) X* M5 a
  81. 2■■■
    9 p# W  \! p- b6 _4 S" c% O
  82. 3 ■ 2 I8 {0 P% C- J
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!6 J5 z6 Z* G3 s. _5 W
  84. 请输入“22”来关闭第2行第2列的窗户。"% e; N% J) y  W2 r* b! x$ s2 N
  85.   "  ①②③
    - g. t7 e. D5 X& A0 l3 R
  86. 1   
    & P$ @4 l0 l% f
  87. 2   
    ' t. K9 o8 F, v, x
  88. 3   % b% h) P% \0 |: {/ T
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    * n+ M  U; M+ K. q" W
  90. 请输入“11”,试着开启左上角的窗户。"
    1 }. i4 k7 y: f* o2 P7 W
  91.   "  ①②③
    " H) J/ @- a4 G1 R- ~
  92. 1■■ ) L" K1 h. ~8 n% w$ r: _" E% `
  93. 2■  ) S6 R: n3 n* j
  94. 3   
    ! g1 b# i( v. p0 ~5 R3 @
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。: V/ `6 {2 m; W4 `0 f) }5 Y0 c: B
  96. 请输入“13”开启右上角的窗户。"
    ' w0 O; a6 D- p4 P8 {: q4 k
  97.   "  ①②③7 h8 ~5 N) q  a
  98. 1■ ■
    0 e( v$ I1 ~3 E  G
  99. 2■ ■
    ; H0 p6 r+ f+ G$ w
  100. 3   & I# i; V/ B5 s! p
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。8 R2 G+ J6 k: Z
  102. 请输入“31”开启左下角的窗户。"4 F( n5 _9 `) Y
  103.   "  ①②③
    7 K: n# R! J7 s; a4 ^
  104. 1■ ■
    $ o1 p5 d9 q  f! S2 ?
  105. 2  ■
    ' J' A; d. _0 ^; {7 D
  106. 3■■ % t4 Y9 x' ]( r! M! j, m; u+ n
  107. 此时,总共有5扇窗户被开启了。
    : D: V3 F# `5 _6 g8 p9 z& b7 G
  108. 请输入“33”开启右下角的窗户。"
    # m9 `; A7 f9 X4 }) _) L; U7 \
  109.   "  ①②③; Q1 [5 m* P4 V; ^2 M9 Z8 p7 y
  110. 1■ ■
    # l: M  j- M6 U* d  Q+ _& R, H
  111. 2   
    4 I0 @" f+ d. x6 i) a0 m# x
  112. 3■ ■+ c8 z+ q8 s0 F9 `
  113. 现在,只有四个角落的窗户被打开。
    9 J" D5 T! i. X7 `
  114. 请输入“22”完成最后一击!"
    9 \9 f% e  e" G  x7 p: B
  115.   "  ①②③
    - I& r+ s+ q* [2 Q6 y3 b. K& h8 U+ X
  116. 1■■■
    & j3 F5 P( o! o0 _, Z
  117. 2■■■
    6 K6 i9 D$ z/ I
  118. 3■■■
    9 a* N) y: {- K4 f7 X" k- D1 a
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")' K9 ^1 @% L8 D$ y8 S+ \) \
  120. 4 B' X* ~2 \9 g6 \' i5 T. r
  121. ;;; 棋盘$ b) r, _9 i( o& `( j
  122. (defconst *wechat-5x5-white-chess* 12288
    5 a. D& o* T# M% z; k
  123.   " ")
    7 J# x' u0 l# d2 ~  M; W( N
  124. 1 J: f* i9 |- ]4 R
  125. (defconst *wechat-5x5-black-chess* 9632
    5 c5 U) u1 o0 a7 V0 j
  126.   "■"). _! t" m1 m* W6 q: ^, n$ i
  127. ; c, m$ w; [5 R  }8 ~
  128. (defmacro with-board (&rest body). q; \, L6 u6 P8 X* {, [( X3 Q: i9 u
  129.   `(with-temp-buffer
    ! C/ u; S! e' L" w! n( S) A  i/ h
  130.      (unwind-protect
    7 @1 N1 z3 z% U6 h# T5 n
  131.          (progn( g" s" d7 z/ a
  132.            (if (session "board")$ Q% T  f3 q- Q+ F) g
  133.                (insert (session "board"))); D' K9 v4 N% J& d, {5 N7 L
  134.            ,@body)- A& a/ Y) ?& s) Y$ a4 L; \$ W
  135.        (session "board" (buffer-string)))))
    : a' i0 b/ Q' L% G5 g2 |

  136. / U, s& T8 ?8 V% x# C7 ^: Y
  137. (defun board-init (size)9 C- j+ W, m! r" m$ F0 A
  138.   (session "size" size)
    ( g) _) r7 q' G! X4 S: w6 g% _" y
  139.   (session "step" 0)8 t8 e! U8 @: J- c7 e9 q4 a
  140.   (erase-buffer)2 |# S% ^% t: u5 a5 f5 G
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨")). s2 p+ t% ~2 Q8 ~
  142.   (dotimes (row size)
    2 r2 Y/ ~; b7 d2 {5 C/ U& Q
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*))))). U# j5 U. K/ G7 [4 W+ G& ^* w
  144. % s) D& O/ K, e; @, E
  145. (defun board-contains-p (y x)5 D6 \; a1 R4 O1 _. h
  146.   (let ((size (session "size")))
    ' _: m# c2 S" H: T2 n
  147.     (and (<= 1 y) (<= y size)
    ( a3 S) A8 u9 r7 h% ~4 ]
  148.          (<= 1 x) (<= x size))))9 s- |9 L$ h( {/ b; ~. P2 S5 w
  149. . f/ Q: U) r. k; w' Q: R# U6 I
  150. (defun board-toggle (y x)
    4 I; A7 o4 z) s6 E/ p' n  @
  151.   (when (board-contains-p y x)/ w" Y$ t7 t- `3 T% i7 x/ j
  152.     (goto-line (1+ y))2 v) x- h- o7 T0 q5 @% M- W
  153.     (beginning-of-line)
    : b/ p5 Y( Y% O. i/ ~" @
  154.     (forward-char x)& O  C) v$ z4 G5 x
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    " d8 M4 X. j2 {% n
  156.                 *wechat-5x5-black-chess*
    0 q: V% f2 x* n
  157.               *wechat-5x5-white-chess*))
    * O" ^4 c; m5 a2 h0 h9 ]4 K7 }
  158.     (delete-char 1)))
    4 R! P6 V+ B2 e9 P2 l- I
  159. ; @9 V1 X% j5 e: E
  160. (defun board-put (y x)
    ) A; D* p' A% ^! i. U
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))) r! i# m+ L5 I+ k* y' k# G0 Y
  162.     (board-toggle (+ y (first dir))
    . z. T( ?. M& @  {. k
  163.                   (+ x (second dir)))))
    . a9 V' F/ @* s4 H
  164. 3 ]$ J- v5 w0 B, G
  165. (defun game-over-p ()
    + ~3 L& A7 ]# I' ]" t! _2 h
  166.   (beginning-of-buffer)
    $ Y# Y. F: ^+ N3 Q: _* r
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))" O  L$ h7 n( R; Y8 v* p/ d
  168. - @9 U1 D+ d- _% g' n
  169. (defun board-show ()' O9 |4 ]$ B( X( c# @) j- V
  170.   (with-board+ L$ V9 J( B/ W' _; V
  171.    (concat (buffer-string)1 I' B( v6 O! w
  172.            (if (game-over-p)
    / `# S9 P& d4 X$ j
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))& }1 Z+ b$ X* O2 y4 ~9 `
  174.             (format "第%d步" (1+ (session "step")))))))7 X* O+ s8 Z' A: F, \  W( z$ {1 d

  175. 6 h1 d: U$ k4 B9 B
  176. (defun board-position-parse (cmd)% s& S+ s7 u7 Q2 Q" \& ~$ D
  177.   (if (= (length cmd) 2)/ A- h. G# i. I
  178.       (list (string-to-int (substring cmd 0 1))% f- \, P3 Y1 m4 Q$ J
  179.             (string-to-int (substring cmd 1 2)))/ [; T$ S. R- q$ \& Y& T
  180.     '(0 0)))5 c- Q: W4 x1 \9 M* p# N% i
  181. ; Z, F! f4 A8 k: D0 ~
  182. ;;; 游戏房间
    / A& ]- w( P/ v$ S. L/ _
  183. (defun game-room-init (cmd)# W$ Q& O) p/ F# [* u) p9 u
  184.   (let* ((middle (string-to-int cmd))
    * X! E7 {' Z% A7 T' I; M5 ?
  185.          (size (1- (* 2 middle))))1 |/ V. t1 Z# L& f# ], e
  186.     (with-board
    , M& p" @, W5 i3 v- O/ s2 `6 d
  187.      (board-init size)- F% Y( o, f% V# Q
  188.      (board-put middle middle)))
    " q' P' O  A% d9 v
  189.   'game-room)! J! n. e: p8 E7 m* \$ }

  190. % X, U% i/ j  ]- |1 q7 b- j
  191. (def-room game-room
    ( r4 z, H/ }3 K; I. B6 r
  192.   #'board-show
    5 B! e6 v, E4 ?+ [, W! `
  193.   (t (lambda (cmd)
    , c; K8 J$ O) S! Q) [( k/ a
  194.          (with-board4 K7 `# c$ V# F2 W) P' T% @# @
  195.           (if (game-over-p): {0 w& U* |- u- L  v5 k
  196.               'living-room2 d3 |, C" R$ L  V9 m3 U4 U
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    ( G) |, W% Z0 L' ]# c
  198.               (when (board-contains-p y x)
    2 t/ A3 P  e) w5 D' d6 _
  199.                 (board-toggle y x)4 S9 Y' ?4 [9 G4 f7 E6 W3 ?
  200.                 (session "step" (1+ (session "step"))))6 w4 T$ K1 d: P! b+ z& j
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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