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

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

  1. ) f3 t. x9 v7 e: `. p
  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 ~* w8 ]( {' v
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    ) F9 F: |/ N" M- A' P& `8 G
  4.   'tutorial-room-0)                     ; 默认的入口
    : v7 a0 c9 @7 d) ]
  5. # c6 N" F+ k  W6 H
  6. ;;; 游戏大厅- I! r7 W; I7 ~' H( c; [3 N
  7. (def-room living-room* V# b' v/ j+ j$ o6 K  c8 h
  8.   ;; 进入该房间后的提示语  ~! k& m6 D& G
  9.   "1. 教程! G( G9 Y/ R' l7 n) s, s
  10. 2. 入门(3x3)
    / I8 H; A! ~  u$ B7 M6 @
  11. 3. 初级(5x5)
    ; G8 C9 K5 ?. ]9 [8 ?$ O
  12. 4. 中级(7x7)
    ; i0 |: ]# n0 Q( k; e$ r
  13. 5. 高级(9x9)" s: H! d! b" G
  14. 0. 关于作者# g) `' I; E' f$ [( X. W: [
  15. 请选择1-5开始新游戏:"+ `) X- e6 z! C* o" y' l0 I" b9 |
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名' U4 f! b# [2 j" I7 x- g5 Q
  17.   ("1" tutorial-room-0)1 y3 f3 c) Q% d. f! d3 Y4 L. t
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配4 |2 n1 m' G* W& W8 r
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    ; ^7 @8 U8 B# F+ q. M/ t
  20.   (t living-room))                      ; 如果条件为t,为永真# i% \* G7 c: Z
  21. 7 H* |- a2 B7 x0 T
  22. ;;; 作者信息3 c6 G# U+ v7 C# g3 x  V: Y! V
  23. (def-room about-room
    ( S; w. v+ d; J" r( L
  24.   "作者:redraiment. L8 B; r% e# q( b6 h
  25. 微博:http://weibo.com/redraiment% z8 r- w. d9 o3 Y9 l
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    2 X8 ~1 b; E$ y' R1 \' H
  27. 请输入任意数字返回游戏大厅。"
    ) q( H; a5 N$ e2 D
  28.   (t living-room))0 o( Q! [$ a- c" e' T8 @
  29. $ K* N$ G: t: F  T0 H
  30. ;;; 教程
    1 N' z- h( Z2 O. e2 Y3 ]
  31. (defvar *wechat-5x5-tutorial-rooms* 0% s. z0 q0 I8 D- w
  32.   "The number of tutorial rooms")+ ?, }/ H0 S6 z5 A2 @& b2 c
  33. , A/ ~- H. W) y! e
  34. ;;; 简化教程的定义
    5 V# r3 z: F, \
  35. (defun string-last-line (content)
    / J/ m$ K! i. \( e" h1 m
  36.   "多行内容组成的字符串中的最后一行"& y5 p$ ]1 R. c! C$ C+ Q2 u8 H6 [2 t% ]
  37.   (with-temp-buffer
    8 A/ s& _7 c6 C8 `5 _& |2 k
  38.     (insert content)  k, x3 ?6 {, z
  39.     (buffer-substring (line-beginning-position)
    - u" X9 {8 u- K9 j- t- N
  40.                       (point-max))))
    0 o! g4 Z: U6 ~4 _
  41. 3 L4 W( Y, O- G5 b. y- m
  42. (defun def-tutorial-room (prompt)( z1 @+ y# |, w, r2 l2 a
  43.   "根据提示语自动生成教程房间。
    3 s1 N0 m& J" R  F! f
  44. " O6 {$ Q& `. }2 v9 C; F" J1 U; }
  45. 1. 提取最后一行作为问题;
    , @8 s( `; K3 ~4 x# Z8 N1 n5 W
  46. 2. 分析问题,获取期望用户输入的内容;
    & X& v% [6 N" @/ C
  47. 3. 定义教程房间和重复提问房间。"
    : m& D, t9 Z: [8 w
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))( _0 K/ h* A1 e7 {" a, B
  49.          (repeat-room (concat room-name "-repeat"))  ~+ O9 \+ S/ p; C
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    2 x) s- L6 {; Y- b
  51.          (question (string-last-line prompt))
    , l( i) S! C0 X1 r
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question); N0 l) k& K, ?. Q; h& H! A' Z% T
  53.                      (match-string 1 question)))
    $ b1 [. I& j) i3 F; h( S# m/ O! H3 i
  54.          (doors (if except' f, I7 v* ?+ z, Q4 S; M6 d  X
  55.                     `((,except ,(intern next-room))6 }+ s. M. z' c
  56.                       ("q" living-room)4 l1 ~  ~9 i# M8 l
  57.                       ("Q" living-room)1 ^, S3 t4 t8 z5 |$ a& t0 U5 q
  58.                       (t ,(intern repeat-room)))5 A1 q  I1 J* f! q1 ]' {; Q2 C& P
  59.                   '((t living-room))))); Y  Y' j+ C7 u1 O
  60.     (def-room-raw (intern room-name) prompt doors)
    ; h$ ?: `! Z7 m7 K
  61.     (def-room-raw (intern repeat-room) question doors)))+ a& m- U* a) r3 @8 Y/ ^

  62. : v1 Y  f" {- U1 M# c. a  }, W
  63. (defun def-tutorial (&rest prompts)/ ^  J$ G+ D5 S( X- {
  64.   "批量生成教程房间。"; J) a( a. s9 c0 P2 u( s) v: n
  65.   (dolist (prompt prompts)
    % O/ P! b7 @, {) D7 m) X5 Y. W! c" E
  66.     (def-tutorial-room prompt)))
    5 S0 N" t: y+ o- e! y" _7 O
  67. : U% S0 n! b! o' x% t4 g/ X
  68. (def-tutorial
    + ^- J7 m! @9 Q
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。) U& _- i7 r& F) n1 U0 N8 m
  70. 1. 教程
    ; Z3 h( y' V1 |9 j) _& r- n
  71. 2. 入门(3x3)
    # o! i* A3 i7 l+ f7 a
  72. 3. 初级(5x5)0 ^- C; P/ G  X" B
  73. 4. 中级(7x7)
    7 L# b5 n2 ~1 l
  74. 5. 高级(9x9); p/ K/ S- Y4 |7 e1 M- ]
  75. 0. 关于作者7 Q! d5 U" v. e
  76. 请选择1-5开始新游戏:- l+ Z. \- `; w& {$ {" ~8 i
  77. 您现在正在游戏大厅里。7 b: C7 R( R. H. [$ G; V
  78. 请输入“2”进入入门级房间"
    # O* @+ k0 `4 N, o
  79.   "  ①②③2 k/ _& k+ \1 @0 F
  80. 1 ■ 
    % f' o2 C5 I, f
  81. 2■■■! t8 e9 ~7 p+ {8 ^/ H2 t" ~
  82. 3 ■ 
    1 E$ S3 \. \; b$ F+ Q
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    " {) ?. \3 v3 F( H1 i
  84. 请输入“22”来关闭第2行第2列的窗户。"% ^' ^) t3 b2 J
  85.   "  ①②③
    ; N; m3 C4 r2 g, U; y4 l
  86. 1   
    8 l5 G- |& u  ~, p0 _! P
  87. 2   ! V' {7 s- n+ m2 Y4 p0 @0 B: f
  88. 3   0 m8 h8 {8 [- U, e
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。) k/ O, t1 C8 A* x
  90. 请输入“11”,试着开启左上角的窗户。"
    % W& N4 J& Q- R# ^
  91.   "  ①②③
    ) H* B1 |) W4 ^; [( O1 b
  92. 1■■ # }, {: J9 s2 t# `0 |$ @
  93. 2■  0 T/ K. E1 D6 j+ I
  94. 3   * x. A4 t6 {7 _* x$ m8 H$ P1 t" t* z
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。! i- \5 W2 c4 e- `, W' Q
  96. 请输入“13”开启右上角的窗户。"
    1 B; j+ w+ ^8 R
  97.   "  ①②③
    3 b! e$ m2 u9 E! p1 D9 j- u
  98. 1■ ■5 r2 W" d! W# q4 P* u. i4 B
  99. 2■ ■
    $ P8 f) W5 D- G) Z; U
  100. 3   
    6 e" V0 E+ Y, s( P' N8 w& R7 M7 l$ E7 L0 G$ U
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    # K$ ]& h* \% }7 Q
  102. 请输入“31”开启左下角的窗户。"
    - q) s) {! _  m) b, e/ b$ V7 k0 m
  103.   "  ①②③
    # z- h* W; `% X, b' Z$ ~
  104. 1■ ■* a2 t1 j8 U% P- f6 y' T3 c
  105. 2  ■3 e! H" u( B( }' J" E" L, N/ M4 P
  106. 3■■ 2 q! _" a1 O1 o* Z: e
  107. 此时,总共有5扇窗户被开启了。
    2 X# n; @/ [& g+ m, B1 K
  108. 请输入“33”开启右下角的窗户。"3 h0 |9 j' m0 u/ K
  109.   "  ①②③
    8 K+ J7 b1 L5 g4 b
  110. 1■ ■
    4 x% f) X) y6 ]& T0 ?2 n0 N3 h
  111. 2   - J: }( Z9 s4 y9 ?5 C4 _, ~
  112. 3■ ■
    3 F# J4 ^% @' y+ t' b- w* {
  113. 现在,只有四个角落的窗户被打开。" Q0 E$ f$ `8 ^+ k- K6 N( {. l1 u
  114. 请输入“22”完成最后一击!"
    0 ~: f0 g! k( M- {( C) i
  115.   "  ①②③& e% v: V9 d+ F, j- T
  116. 1■■■( _7 w- I+ L, X  B% K. r
  117. 2■■■* e/ D0 q0 T( v
  118. 3■■■- A4 f9 |. Z+ E6 ]# ^# o" O
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!"), s6 |7 y2 `- t, k7 t- @6 X: ^# Y) b

  120. $ E  x6 R( W8 \* V" @3 H( D
  121. ;;; 棋盘
    % ?, ]3 X! ^; ~/ K2 N& H2 h
  122. (defconst *wechat-5x5-white-chess* 12288
    6 c2 @; F7 N& h1 [, d/ H3 `0 G
  123.   " ")
    % f3 R4 V8 I# T- Y* |! A; W$ ?

  124. 0 d' ^$ W6 {* W! }. S# y1 R
  125. (defconst *wechat-5x5-black-chess* 9632
    - `) E9 I2 S. b
  126.   "■")
    ( T+ }* \# A; h3 S% t4 ]* m

  127. 9 p' c" a+ |0 e
  128. (defmacro with-board (&rest body)% \& G5 U7 C4 w$ j3 ]; P' e8 r  z
  129.   `(with-temp-buffer
    5 ~6 b; N3 `6 x4 O! f5 X7 p' w
  130.      (unwind-protect
    ! C6 d/ D' _( ?# _) c# Z( T% {, y
  131.          (progn- \. ~3 L& C- r5 u# w( ^" O
  132.            (if (session "board")
    * ~- C+ O, g( ~
  133.                (insert (session "board")))2 C3 ~4 j$ a5 V' \$ K
  134.            ,@body)% a4 N/ n) w% q
  135.        (session "board" (buffer-string)))))
    " s& g4 N" v. d! Q  v

  136. 0 i' r2 A% R& \- y" R
  137. (defun board-init (size)1 g, L% r3 |* S" K' U
  138.   (session "size" size)* P0 a% t2 t# ?3 P7 n
  139.   (session "step" 0)+ P" a7 _" e! j" F! \
  140.   (erase-buffer)' `/ F# B- }5 n
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))& w6 f0 s2 m7 W
  142.   (dotimes (row size)
    & g6 G$ U7 e* N
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    7 u( _) C0 u- h# C1 A8 J! @4 W

  144. ; W8 }8 _- |& i/ j% P
  145. (defun board-contains-p (y x): X8 R) H9 t! q( c' S
  146.   (let ((size (session "size")))# W, G) K# Q" s8 I
  147.     (and (<= 1 y) (<= y size)
    # \! o" O, X, t7 ?# ?- i* _, }
  148.          (<= 1 x) (<= x size))))& j! N  G1 e9 G) }3 g+ _

  149. 1 k& d4 K  p- i
  150. (defun board-toggle (y x)
    " ~, e$ e, h) Z, [) N, Z% @
  151.   (when (board-contains-p y x)( l1 N9 \9 g- M8 m' F' g
  152.     (goto-line (1+ y))
    4 w* _- }. r/ S  Z. o6 n. D
  153.     (beginning-of-line)- ~+ S( p- U. m2 _* x0 G* H  U
  154.     (forward-char x); N& i; H2 h* P. a& }
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    0 o- g  v1 L8 J; ~: ~
  156.                 *wechat-5x5-black-chess*( D/ c8 {* y$ N" R
  157.               *wechat-5x5-white-chess*))$ ^+ c% m' ^2 g: _
  158.     (delete-char 1)))# Q' t$ G' e* H" O! ?- ]" J
  159. , v1 ?6 H% V7 J3 o9 ^1 f3 f
  160. (defun board-put (y x), L4 I, m2 z( a# l9 R; t& a
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    5 d0 d& l2 U3 B' A0 \
  162.     (board-toggle (+ y (first dir))( }( e* c% g. f9 u- v! g1 B
  163.                   (+ x (second dir)))))3 C4 k' ?$ q; [4 F- {

  164. 1 L" L- T' J/ I; B) ^$ z  U
  165. (defun game-over-p ()
    ( t+ |6 b( R0 Q9 n
  166.   (beginning-of-buffer)
    ) M$ D8 y6 C' q5 A8 V8 e7 Y7 h
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    3 p/ W9 a& f* z0 }) A% ?! e+ U! a' R

  168. - p  J  J, l& V$ d3 q2 S
  169. (defun board-show ()
    # O: \6 C/ O% ^" ^# w0 J& R
  170.   (with-board0 W, I) g1 x9 J- d1 {
  171.    (concat (buffer-string)
    + n% M* ?8 K  ^! X1 K! p
  172.            (if (game-over-p)
    1 a, h- n6 \6 B/ z
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))2 c# F8 A. O* `9 ^; T8 M: P
  174.             (format "第%d步" (1+ (session "step"))))))); i$ C6 r5 x/ v5 b* N1 {
  175. . w/ y- Z+ P+ O( E- @2 V" ~9 x
  176. (defun board-position-parse (cmd)
    + H- m; I+ f/ a) ^" e3 m( A
  177.   (if (= (length cmd) 2)
    : V' _6 s% a, W+ |" f0 L3 q' Z* l
  178.       (list (string-to-int (substring cmd 0 1))4 G# Z4 W+ i4 w2 @+ `6 l) T
  179.             (string-to-int (substring cmd 1 2)))
    2 ^# G% ?4 W' o# D0 U& P
  180.     '(0 0))): ~1 |3 L: v6 |* O. S- y9 V7 Z" x  C; z

  181. 0 }& f1 Z( q8 x& f
  182. ;;; 游戏房间0 c" M  ?2 i9 b; Q  K& [; |! O
  183. (defun game-room-init (cmd); e0 ^5 [$ q8 T, I+ T) E
  184.   (let* ((middle (string-to-int cmd))
    ; D9 S  {. T* o' D+ }0 k2 Z
  185.          (size (1- (* 2 middle))))
    8 K  a2 m; C" e5 D$ b) F7 t& g
  186.     (with-board
    , }: c% T3 s% X# H
  187.      (board-init size)1 x) L9 ^6 h: B1 k" p9 H" N
  188.      (board-put middle middle)))
    - L+ Z1 `7 a1 x4 E7 X$ U
  189.   'game-room)
    7 v+ S' s& B! l8 e7 F: {

  190. + q& B9 C! B0 F$ N7 F+ P8 x9 L
  191. (def-room game-room
    5 {* V4 R/ d8 C4 B. _6 L" I8 W0 u
  192.   #'board-show
    $ K8 t5 t- ]  z7 e5 h- W6 n
  193.   (t (lambda (cmd)4 y. F& o3 }6 C3 ~8 n
  194.          (with-board
    9 Y7 H. j7 I. a, ?7 y
  195.           (if (game-over-p)
    / y/ T( }, y9 j5 C2 {( d9 V
  196.               'living-room
    5 O! N2 g  ^8 N8 T2 d
  197.             (destructuring-bind (y x) (board-position-parse cmd)$ z7 T) c# B2 [3 H
  198.               (when (board-contains-p y x)3 E, y! j$ V, n: h' e' O
  199.                 (board-toggle y x)
    ; a; Q) H) f# `1 g9 f1 `
  200.                 (session "step" (1+ (session "step"))))! P6 k$ f- L  g/ w0 E. N- t" J: }! I
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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