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

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

  1. # n2 b" v) f: m% 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;">;; 定义新的游戏地图% e* b/ h" Z" Y) A
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    9 Z; o8 B3 X* X( u2 V$ V+ V1 v
  4.   'tutorial-room-0)                     ; 默认的入口8 B1 x) J) F  }  S+ n

  5. ) Q- l1 G1 d  v4 u
  6. ;;; 游戏大厅+ N* l7 @4 L$ ^! o9 n
  7. (def-room living-room
    - }  ^4 D. z* |- K
  8.   ;; 进入该房间后的提示语( j( Q. ]; N1 r- T  O* \. Y
  9.   "1. 教程
    / W" N  ?3 w/ h7 M3 O+ v6 B
  10. 2. 入门(3x3)
    4 g; K1 |' V* d5 ^' G, x5 K5 f0 y
  11. 3. 初级(5x5)
    ) P) U+ e- G# X& a$ u7 g! E% u
  12. 4. 中级(7x7)5 ?, B" j& x4 Q8 I  |4 J
  13. 5. 高级(9x9)
    , m0 e' G, t, D
  14. 0. 关于作者  {' `+ g+ X0 ~! i# {  \$ u
  15. 请选择1-5开始新游戏:"! E8 L  n' C* j& A# ]% L% w+ S
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名9 }  Y1 c6 g6 z
  17.   ("1" tutorial-room-0)
    0 D$ E* r7 u) B. g/ }# D% e
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配# c+ g; L4 L6 M3 D9 }% U
  19.                                         ; 相应的返回也可以为函数,动态返回房间名/ m% k1 k, ]* l( ?  ?
  20.   (t living-room))                      ; 如果条件为t,为永真* Z% z5 Z+ s7 \( w1 K  D

  21. 4 ^" j' i9 e3 F6 P9 W' D3 b
  22. ;;; 作者信息
    7 E+ y8 t4 b0 s: b% s
  23. (def-room about-room
    4 k1 B" i0 d+ z. y. w6 r
  24.   "作者:redraiment  r4 [) T  v  z- z% n' d5 f. l0 s0 s
  25. 微博:http://weibo.com/redraiment
    ' u" V7 n7 |4 X5 X4 T  g/ H# x
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    / h# l1 B8 @% O
  27. 请输入任意数字返回游戏大厅。"
      x& n( N' h( j* {0 C
  28.   (t living-room))
    + y0 S0 B& O# b& ]1 H

  29. ) l2 }( _& B+ O1 \( d5 {* ~
  30. ;;; 教程) }& D3 W8 x2 _% m' I( }
  31. (defvar *wechat-5x5-tutorial-rooms* 0* K% _& c" |! R
  32.   "The number of tutorial rooms")& L) b$ h" ]4 f) C! _% L

  33. ; d+ N% l1 N$ m7 m- v; O& g" U
  34. ;;; 简化教程的定义4 N$ l8 i9 e2 A' H4 C& N7 s4 [
  35. (defun string-last-line (content)
    ( `) f6 C, n% |3 k/ P
  36.   "多行内容组成的字符串中的最后一行"' t9 L; l' G6 S5 Z/ r2 J
  37.   (with-temp-buffer
    ; S1 n5 D5 t, K: W" z: f0 q
  38.     (insert content)( c$ ~4 |  K5 V, w2 S
  39.     (buffer-substring (line-beginning-position)
    . v0 m! O& D' d% ^7 L
  40.                       (point-max))))( g' Y. u" N6 Y& f" l8 ]- [

  41. 2 p" y3 @# c/ b! |; B% j
  42. (defun def-tutorial-room (prompt); j" M/ A" j' H. e- I7 j: Y7 F! p5 B
  43.   "根据提示语自动生成教程房间。
    ; r# K. l! i) `; }5 b
  44. " G* @4 A: x( V9 w! {( j2 g  Y( W. d6 c
  45. 1. 提取最后一行作为问题;
    " e% F0 w7 f- O: h3 X/ ?0 j
  46. 2. 分析问题,获取期望用户输入的内容;
    ; I, b8 k3 ?* u/ ]( p1 r
  47. 3. 定义教程房间和重复提问房间。"2 O% S1 ?0 u: y* B1 r! X1 w+ \7 C
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    1 M8 V# m" @9 R/ \* W
  49.          (repeat-room (concat room-name "-repeat"))
    # _4 ~4 x2 |! ]9 k% t' p3 M
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    $ d  w. c& m5 ?6 Q% Y8 B7 A
  51.          (question (string-last-line prompt))2 C  z) T. u$ X4 ~: R
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    " m7 d' _7 l; b% ?/ w  t! p* {% J
  53.                      (match-string 1 question)))8 R2 C/ N, F6 \" ]: r! V
  54.          (doors (if except- F* [6 P0 ?, r4 A- J0 ?. y! P
  55.                     `((,except ,(intern next-room))5 c9 p: w3 f1 G1 k( `
  56.                       ("q" living-room)
    4 g8 H5 ^3 N) c6 P
  57.                       ("Q" living-room)4 x4 ~7 X" O  v% Z, r
  58.                       (t ,(intern repeat-room)))
    * f- U8 n/ @6 @! t' C/ R! K! D
  59.                   '((t living-room))))); h, ?. G: z+ @) l; C$ g' y+ c
  60.     (def-room-raw (intern room-name) prompt doors)
    2 F9 v. ^6 c" X; `0 V4 w
  61.     (def-room-raw (intern repeat-room) question doors)))
    ) H6 t- C# O, J, `0 T* N  B

  62. $ @, e* S2 @: _& M
  63. (defun def-tutorial (&rest prompts)
    0 b1 t4 O! p4 U' ~
  64.   "批量生成教程房间。"3 x# e4 ~+ V  d2 G) ^1 D
  65.   (dolist (prompt prompts)
    1 X4 @4 i5 \( ~' h/ i
  66.     (def-tutorial-room prompt))), a2 Z2 @* w' g! S
  67. , I0 Z& _0 V$ w! [- Z' R
  68. (def-tutorial. D) S5 t8 a" C  d- c! N
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。% V5 L* U7 }+ E, P0 F, b; U* S
  70. 1. 教程) H# _( M$ \  c
  71. 2. 入门(3x3)
    % Q' Y" |* o& W5 s3 [
  72. 3. 初级(5x5)/ B$ J* S4 D4 M$ \7 {
  73. 4. 中级(7x7)0 p* g5 n) Z% W! T
  74. 5. 高级(9x9)8 f( ^  k) @5 D. H
  75. 0. 关于作者
    2 w8 Z) C! }/ b5 X9 r6 _! M
  76. 请选择1-5开始新游戏:2 [4 N) g7 v- c6 w  [
  77. 您现在正在游戏大厅里。) _! w# t9 d+ p4 @$ a0 n
  78. 请输入“2”进入入门级房间"
    / j- `0 K/ u* X: e6 }2 U
  79.   "  ①②③
    5 f" Y- B' N. q! z, v; N, g. [
  80. 1 ■ * |# K: j% e7 y/ l! o: B/ f# M. B
  81. 2■■■
    ' f) Z. u* p3 b0 L& K
  82. 3 ■ 
    / T. N/ U$ J  v1 Q! m' n
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!; N& S0 G: _4 m4 Y6 j9 b7 v( H1 E
  84. 请输入“22”来关闭第2行第2列的窗户。"" T4 K: F' W, c0 l- r, ]
  85.   "  ①②③9 x1 v! ^8 V& @* |9 O) w& F) [* ?
  86. 1   ) `. r! d. m4 T2 q. Y8 I9 O8 `0 @
  87. 2   
    $ ]7 `( ]0 x( c3 K
  88. 3   * R+ Z9 [; q  j6 n4 F+ H2 C1 F
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。) ~  T  ^; d+ a% m
  90. 请输入“11”,试着开启左上角的窗户。"
    6 {6 L# x# y- c! \) G
  91.   "  ①②③: V% a4 F" b9 H
  92. 1■■ $ O, z3 ^! W- {: G8 I
  93. 2■  . D, c  I7 V/ l4 @
  94. 3   . b+ ?/ v9 R2 b
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。! C* h' Y& U. D4 t/ X* u
  96. 请输入“13”开启右上角的窗户。"! X5 n% C7 U: t, B; b7 a9 x
  97.   "  ①②③, D8 M/ P! B5 P- i+ v6 I
  98. 1■ ■
    , j3 G5 u/ v. E/ `% h
  99. 2■ ■6 U1 X+ ?* Q! K4 Q1 M- m( E* q
  100. 3   
    0 B% X! V9 v& K+ a- A1 U. [  f
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    , f' `, P- a5 D5 h% L9 U- u
  102. 请输入“31”开启左下角的窗户。"
    # }9 C4 y+ W1 e  T9 I1 R- X7 E* l
  103.   "  ①②③
    4 @4 U- P! w0 q, A: W
  104. 1■ ■$ ^- B  Q, H+ m8 U- A, U% y. ?. _+ p2 v
  105. 2  ■$ E5 s0 ~/ p% h& |
  106. 3■■ 
    2 u  ]) J& K) h" U7 j
  107. 此时,总共有5扇窗户被开启了。" l- R8 a+ t/ |, g7 F9 J
  108. 请输入“33”开启右下角的窗户。"
    0 }# S, ~& X+ S
  109.   "  ①②③# b: {8 h5 U" \; c
  110. 1■ ■8 D% |/ B9 A  y& l$ s1 z
  111. 2     v9 o* K4 d2 n- t1 [2 K6 ~0 ~
  112. 3■ ■
    0 X* e6 i2 U! B+ k; I8 g, ]
  113. 现在,只有四个角落的窗户被打开。
    ) B+ S/ `: [* M3 n7 |6 _
  114. 请输入“22”完成最后一击!"
    # u1 e; }5 D  K5 h
  115.   "  ①②③; r+ s( w0 J% t3 T7 U) s8 G
  116. 1■■■
    . H8 S) N0 ^4 v, ]. M* T" q
  117. 2■■■+ \: y! t9 L$ G4 _
  118. 3■■■* O" U" ~, |- C) n% U
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")* v5 r8 V2 N9 L; K) q5 m
  120. 9 _* g% ?9 y1 o0 ~7 B) y" d
  121. ;;; 棋盘; L, k# }" b8 J1 p
  122. (defconst *wechat-5x5-white-chess* 12288
    : m1 f; Y, z1 q( a, S3 l
  123.   " ")
    3 j$ I. N! e: G. t
  124. $ Y5 Z) U# S+ I
  125. (defconst *wechat-5x5-black-chess* 9632% h0 W5 ^" ?0 Q  {+ X% C
  126.   "■")
    ' h# [) k+ Q0 c+ H% \
  127. 6 o/ R" o) G' A- o! {+ ~
  128. (defmacro with-board (&rest body)
    ' T0 I" S1 _6 S9 `7 Y5 |: ]/ R
  129.   `(with-temp-buffer
    % I2 ^7 L: }2 _) F
  130.      (unwind-protect8 ^8 V# R. e' ^
  131.          (progn4 z! G4 e; \: ?  h! t& U- L
  132.            (if (session "board")
    1 L; L/ l  N, b: \! G& f3 \
  133.                (insert (session "board")))
    9 M1 F2 R6 k! D, X7 F
  134.            ,@body)- @* X; ~9 Z; v) L
  135.        (session "board" (buffer-string)))))
    ' `7 E7 d# F) m0 S! {; T& j. t

  136. , ]& ^+ C, Q, y* J$ m
  137. (defun board-init (size)
    3 |2 q- O) X+ e, I3 |" `6 {
  138.   (session "size" size)
    ' ~* S8 W- _& Y5 q( O
  139.   (session "step" 0)7 s( r  F% V/ d) o( x5 a1 f
  140.   (erase-buffer)* f/ N  L0 }8 A
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    8 }7 f5 u9 a3 {9 \) w  Q0 C
  142.   (dotimes (row size)! f4 z$ Z6 M8 u* s. V
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))$ u' T0 t6 n% A# z  n

  144. 1 U' C  I( l# V, \" [2 n8 {9 M
  145. (defun board-contains-p (y x)
    * _, A$ m8 n2 v  e
  146.   (let ((size (session "size")))& ?/ o& G7 F  l8 {) V9 q
  147.     (and (<= 1 y) (<= y size)
    ; k8 D. u# v* h/ _+ B
  148.          (<= 1 x) (<= x size))))7 W: ~2 w; \9 L& O0 n$ r, l4 E

  149. 0 X: {* E( b( ~  W+ Z+ R
  150. (defun board-toggle (y x). ]7 |& d. m+ f+ O& Z6 l
  151.   (when (board-contains-p y x)
    3 |; q9 K1 }+ F6 J3 ~8 e' Z8 v% V
  152.     (goto-line (1+ y))4 j% a3 \3 b" M( J: ^( M$ g
  153.     (beginning-of-line)
    6 u9 I) r. p; p5 m4 G! L
  154.     (forward-char x)+ s0 X8 ^  X+ @+ O6 Z0 t: W1 C" l% Q
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    2 U) P4 M* ]' U$ b3 E' A$ m+ f& W
  156.                 *wechat-5x5-black-chess*; ]9 p4 ~& C! U9 [  A
  157.               *wechat-5x5-white-chess*))& ?4 X) i' L( Q: h, U, v9 C7 H% }& [
  158.     (delete-char 1))): F& P/ w  Z! R* U
  159. & `; I/ x: q% N% z' J- o
  160. (defun board-put (y x)" m' a6 t7 e1 r) {5 A) Y8 j( X
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    9 u! L  w. X! Y, N# q( `; k
  162.     (board-toggle (+ y (first dir))( b" m! w8 c- s" ?% A. C
  163.                   (+ x (second dir)))))
    + g8 W+ q0 b' S1 C, L! J3 P& _

  164. 7 q' p0 o5 _1 l6 F+ d6 j
  165. (defun game-over-p ()
    $ S# B, N3 \1 R4 S
  166.   (beginning-of-buffer)
    : W2 G/ t* T& D7 s
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    , E; K4 F; z, G. [- F

  168. ! ^/ ?0 I$ m4 x  L# J
  169. (defun board-show ()
    ) u/ D* m( P, \7 U8 x
  170.   (with-board" A1 T  N5 x; n' y4 j- A: P
  171.    (concat (buffer-string)
    $ N! J9 a0 e+ b2 b( A7 k
  172.            (if (game-over-p)0 h( l' `; |* L' G8 [* |  Q
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    ! }+ F2 d. K& `; b2 F
  174.             (format "第%d步" (1+ (session "step")))))))
    7 j% N9 S/ Q2 \3 I, K
  175. ' {4 H: j6 }" ^+ @5 m& [
  176. (defun board-position-parse (cmd)
    0 ~( H5 E4 R' i$ m; h) E
  177.   (if (= (length cmd) 2)! o/ E& y! }5 S+ m
  178.       (list (string-to-int (substring cmd 0 1))3 j- u2 k. l( z  y" h* T
  179.             (string-to-int (substring cmd 1 2)))
    ; o  I0 p: D/ x" A( l4 \% G2 N
  180.     '(0 0)))
    0 N' A' w8 @4 }7 q3 P  R- f
  181. 5 c2 Y# r; J. i
  182. ;;; 游戏房间. F" W; D# a$ @$ C3 s* ]5 I$ T
  183. (defun game-room-init (cmd)
      ]( p) A1 Q) o) W
  184.   (let* ((middle (string-to-int cmd))9 n0 C8 m: R# a: p7 v3 l
  185.          (size (1- (* 2 middle))))
    & ^' C& }4 O$ y- _8 O
  186.     (with-board
      a& W0 S" [. ~$ U$ a% f* k* L+ u& y
  187.      (board-init size)& z9 c* H4 J+ a- a! s4 V
  188.      (board-put middle middle)))1 P1 F( C1 h2 \
  189.   'game-room)
    , A+ g# [0 Y, r* y* u

  190. 2 J  N6 K: Y7 A- f2 }, {
  191. (def-room game-room
    : {1 N8 j% s5 }/ K7 w; |6 w
  192.   #'board-show# K% d, n+ {  W1 s
  193.   (t (lambda (cmd)( ]" S! U) ^4 @0 P7 f. ^+ D
  194.          (with-board
    , P  v$ |. W5 m6 [
  195.           (if (game-over-p)
    : I, `. a1 I" D" S0 a
  196.               'living-room! b; J) m6 k4 N5 w* y, J( x! E9 v
  197.             (destructuring-bind (y x) (board-position-parse cmd), }) l( p2 ?. t/ Y. ^2 ^. u/ a
  198.               (when (board-contains-p y x)- ]- s, Z/ Y! U! f1 j5 e% u
  199.                 (board-toggle y x)& {* S% P2 F3 ]4 O; w4 l( A
  200.                 (session "step" (1+ (session "step"))))
    ( s& ~$ J% s, _0 f
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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