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

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

  1. 1 z5 T' H7 G9 ]4 T$ j- C
  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;">;; 定义新的游戏地图
    $ s# q* Y) S2 m+ n* K3 f$ |
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL& n) a  V0 {( m- b9 K3 H
  4.   'tutorial-room-0)                     ; 默认的入口
    : Q+ g$ v! F: p

  5. # d7 r1 M" b" A7 g
  6. ;;; 游戏大厅( }2 J) Y9 H. ?
  7. (def-room living-room& b) h. Q& L$ {2 v0 X* W
  8.   ;; 进入该房间后的提示语2 X6 ~. h' p) V
  9.   "1. 教程
    1 X; W& _4 T! D. J2 ^
  10. 2. 入门(3x3)
    . \: a) _, Y4 w% ?- d. x
  11. 3. 初级(5x5)
    % q# C$ ?5 k) G  }$ O
  12. 4. 中级(7x7)
    6 D3 A% M0 v: L( w  |. J1 h+ \
  13. 5. 高级(9x9)
    % q- m* a+ h+ P  j1 z5 H$ Q
  14. 0. 关于作者
    " @2 N5 d: {# A& m7 v) z
  15. 请选择1-5开始新游戏:"( ~0 \. m# y4 D) y5 y7 `
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名& T* m- g, |' Z0 [( r
  17.   ("1" tutorial-room-0)/ p# F. n0 [* Y0 p8 s8 p, z
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配) J% d  h5 b9 `7 [
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    ! f; ?8 w! v+ m( b2 c, B9 J
  20.   (t living-room))                      ; 如果条件为t,为永真$ j, X$ d* C( e- C5 B
  21. * d. K8 Q" W9 a/ y! V* Y
  22. ;;; 作者信息
    : f5 B# q! Z# H/ I$ Q0 u
  23. (def-room about-room4 g7 g' }% O* F, Q. g- I
  24.   "作者:redraiment9 l' ~3 k! {1 Z
  25. 微博:http://weibo.com/redraiment9 t) ]  v: l2 w+ I7 s
  26. 有任何建议,欢迎在微博或微信上联系redraiment。* U* C0 f. F* j+ B8 {3 ?- S
  27. 请输入任意数字返回游戏大厅。"3 G$ B* k3 d! Q7 Y/ I
  28.   (t living-room))# u% r$ V* W1 W! I7 a
  29. 2 n% Y0 n7 v) w* k! a) x, i
  30. ;;; 教程
    # g6 w2 u& r3 B
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    1 |# n4 {! ^9 p' g
  32.   "The number of tutorial rooms")
    / j  v, i+ r3 C6 ~6 R3 ~/ V, h
  33. : u! W: j% ]2 R$ K
  34. ;;; 简化教程的定义
    5 ~: [* @; X" u% K' y) V( j) ~
  35. (defun string-last-line (content)
      [2 h3 @, M9 S& e: ?
  36.   "多行内容组成的字符串中的最后一行"' `% M4 B% M2 Z
  37.   (with-temp-buffer6 v+ k- |2 y3 [) \& q, B
  38.     (insert content)( `) q0 }6 l1 A
  39.     (buffer-substring (line-beginning-position)
    . S8 |5 h7 u5 O( O
  40.                       (point-max))))
    8 e- E$ N4 i& [3 Y- |
  41. # g1 d2 D+ n0 @) Z2 u0 f1 R
  42. (defun def-tutorial-room (prompt)
    ' j+ s5 F8 X9 F5 u2 F
  43.   "根据提示语自动生成教程房间。# s1 u) @3 T# {0 q+ n* r

  44. / F; G" h) g/ f. N, f) l; P
  45. 1. 提取最后一行作为问题;. K& ^8 C- j/ i% p
  46. 2. 分析问题,获取期望用户输入的内容;
    6 n4 {6 d7 P2 `( q/ D4 ^2 W( M7 `
  47. 3. 定义教程房间和重复提问房间。"
    9 x& X# e6 w$ `- m3 u
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))5 A+ J! Y  V* `& K+ ?
  49.          (repeat-room (concat room-name "-repeat"))
    ' D9 x7 d: @' ~( y
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))" W2 \5 M0 `; j2 Y' k  K3 S
  51.          (question (string-last-line prompt))
    ; R# `3 C6 M- A* w! i1 T+ T3 Q, u
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    - w( ]2 |. I* ~% N4 P8 T
  53.                      (match-string 1 question)))% V$ q: t) [  n6 _+ `4 D
  54.          (doors (if except
    * Z# J2 P+ G+ P# N8 ]  H
  55.                     `((,except ,(intern next-room))/ ~, L' O$ ^0 |! J3 E! N
  56.                       ("q" living-room); N( r$ E2 q' S: B+ p  [& x
  57.                       ("Q" living-room)* @/ G' r. g. h/ X
  58.                       (t ,(intern repeat-room)))
    5 u& |: B6 ^0 \2 n  c( |5 d
  59.                   '((t living-room)))))# p# S5 o7 f5 y# e
  60.     (def-room-raw (intern room-name) prompt doors)
    # z/ }. J7 r+ ?& B$ P  z
  61.     (def-room-raw (intern repeat-room) question doors)))7 i4 {2 e# j  z- U2 M3 |0 q5 {

  62. / m5 G  K  V! x2 U2 ~4 m
  63. (defun def-tutorial (&rest prompts)
    4 {* |" D1 k  E' d
  64.   "批量生成教程房间。"
    ) J0 O* ^- Z( i& q7 R
  65.   (dolist (prompt prompts)
    / O6 F7 ~# l) F9 @9 Q& L
  66.     (def-tutorial-room prompt)))- }' Z9 s. D: a4 T( q/ @' \( z  H6 M

  67. 6 `+ ~( {( ~1 ^9 n
  68. (def-tutorial" [+ H( L* @9 U: O3 K
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    0 D8 h0 B6 J, w4 T/ Z2 r1 C
  70. 1. 教程
    / |+ E2 n) ~' p6 k" Y* a9 c- S
  71. 2. 入门(3x3)
    6 s3 L: S. t  x8 y+ A6 x7 O
  72. 3. 初级(5x5)
    ! H& d$ T. ^+ L1 U8 E3 d( H' z
  73. 4. 中级(7x7)
    / M6 f* N* U4 U4 t* L9 c
  74. 5. 高级(9x9)
      ]% x/ O+ l  J! t! q/ P
  75. 0. 关于作者) @& I/ W' B. ^, M. v
  76. 请选择1-5开始新游戏:' q; G& H* B6 ~& Z
  77. 您现在正在游戏大厅里。
    ' Q# l! q5 ~! e/ }' S% a$ u
  78. 请输入“2”进入入门级房间"
    ( ~6 \; o, P/ a: u9 ~/ g9 L+ l" d
  79.   "  ①②③
    4 I( P2 H4 `- v/ J4 n, F0 b
  80. 1 ■ 9 R' H# M5 [; l$ X: |" g
  81. 2■■■
      Z8 z- n5 g# B& I8 ^+ r
  82. 3 ■ ) Y$ k8 ]  }: C& ~- N5 W9 x6 ?
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!" L6 x- l/ I6 t3 V
  84. 请输入“22”来关闭第2行第2列的窗户。", K0 ]8 k/ s  [5 m9 g
  85.   "  ①②③7 t5 |; }9 M+ {1 |$ U2 O
  86. 1   
    0 j3 b$ H+ K8 W! O: x- ]
  87. 2   
    8 x! T% u, P0 m; V$ w; F0 I+ I
  88. 3   ) o+ K  a3 ~; }8 a
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    7 j+ R) \* a' L& o- `& Y# U
  90. 请输入“11”,试着开启左上角的窗户。"
    , j6 Y  I+ X- m
  91.   "  ①②③6 I8 p6 Z1 _, e: [! c; E
  92. 1■■ - {' ^& q! J# M' ^8 Y
  93. 2■  
    4 V0 ^8 @) n; P, J3 C
  94. 3   
    1 ~/ N6 I; v# w. E
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。: I6 C7 e3 i4 B- h' }- o
  96. 请输入“13”开启右上角的窗户。"
    * _. Y' @: I- D, f/ D
  97.   "  ①②③4 S! m9 T" k8 Q' w
  98. 1■ ■6 T7 B8 K+ R6 Y+ w% b& G
  99. 2■ ■5 L" p, f# k2 N0 }* z' j$ Y
  100. 3   
    ! m% |7 c7 G9 x- z6 }+ O3 X
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    1 v0 l* G' V5 V, v
  102. 请输入“31”开启左下角的窗户。"
    % N9 `* @" S$ D6 O/ ~3 n7 r- m
  103.   "  ①②③
    1 s( w& v/ X* d0 z9 X
  104. 1■ ■% l9 @( T+ y$ U( F8 \. G. H) X3 ]
  105. 2  ■& @: h5 p* O( d; e' |* p6 p, N5 k8 {8 p
  106. 3■■ 
    ( R  r1 E& U/ T! `; W2 B( C
  107. 此时,总共有5扇窗户被开启了。
    8 y) v) u6 g& h2 F0 Q. H
  108. 请输入“33”开启右下角的窗户。"
    / Q) {" |* L, X) n% K/ C" d! Q2 s
  109.   "  ①②③
      x8 I* B, T+ U+ ]8 m% k. G
  110. 1■ ■: ]% B2 n# f3 @. ~8 F/ g. S/ M! t
  111. 2   
    + s5 V; V" Q8 J& s
  112. 3■ ■& v8 F0 O; F0 U6 x
  113. 现在,只有四个角落的窗户被打开。: D4 ^/ L. X0 c
  114. 请输入“22”完成最后一击!". g2 w: f! Q' x6 F& w1 t; d6 ?
  115.   "  ①②③  ^/ L  \& U/ l$ |, A
  116. 1■■■& I* i$ I4 I) l9 `% S7 `
  117. 2■■■
    & V# P; P" L( f9 y4 Z/ E
  118. 3■■■3 O" ]$ i! w) y: D. z
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    8 Y6 P4 X/ D- b. M0 s; ^
  120. : _$ _8 w' E& M4 P1 e
  121. ;;; 棋盘# T4 _4 G' ?& R, N6 r5 S
  122. (defconst *wechat-5x5-white-chess* 12288& Z+ Y, U5 J. |& {7 X2 y
  123.   " ")
    ' {8 B4 k4 n4 o& c( X2 u8 @
  124. + I7 F$ `* ^; H  w' u& R
  125. (defconst *wechat-5x5-black-chess* 9632
    0 W& k+ @' P2 i6 @
  126.   "■")# y4 X% p. y: `, G) J6 a

  127. 2 S8 ]/ |. t$ K0 W) ^/ L
  128. (defmacro with-board (&rest body)2 M6 v: _' b; I& B, w
  129.   `(with-temp-buffer* U- d7 Z) |( E1 a5 f
  130.      (unwind-protect
    ) v$ ^7 P5 Y3 R4 a
  131.          (progn
    1 ^3 `  Y8 J/ y  p9 \2 T
  132.            (if (session "board")
    ) C) s" V: |1 {1 H
  133.                (insert (session "board")))# F/ }5 Q4 I+ u9 o' ?3 d
  134.            ,@body)* ~/ i6 ?. T1 ~* H: ~
  135.        (session "board" (buffer-string)))))9 Z6 x% x3 O- B. ]

  136. & p2 o6 |( l7 E- f( E3 x: [9 z0 M
  137. (defun board-init (size)
    8 r! K8 A9 m- y% P+ N2 U
  138.   (session "size" size)
    ( `! K7 d# R; }1 F9 q, U! g
  139.   (session "step" 0)5 n/ I; O3 K8 _( W7 A: u4 a
  140.   (erase-buffer)( l! u- l2 E. _9 B' B( Q/ X* h
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    3 i  R5 B3 E6 U/ U9 @  J) K
  142.   (dotimes (row size)5 ~6 L9 r8 j9 d  N  Z* |
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    # _4 U3 w: o- @" B/ j" {1 B
  144. . S  j# d2 r! B% P$ s+ Q7 d
  145. (defun board-contains-p (y x): V: S, t: z* W3 I! U( T  @5 j$ |
  146.   (let ((size (session "size")))
    * j0 \5 M- n  H2 L, C3 B3 k
  147.     (and (<= 1 y) (<= y size)
    $ }' s. s: e% }1 T9 D
  148.          (<= 1 x) (<= x size))))
    , t" |+ F1 b4 I/ f/ z5 d
  149. 5 l  O; E* R) u9 U% ?
  150. (defun board-toggle (y x)
    3 }5 S3 O1 ^0 C
  151.   (when (board-contains-p y x)1 R& H& M$ _7 p2 M& G" a" j
  152.     (goto-line (1+ y))& |! }4 a$ d# g9 h
  153.     (beginning-of-line)1 F  w4 r) p" |! R
  154.     (forward-char x)- {! M/ G. D- x& P5 F5 `/ H$ ?
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    . i& ]% G1 R$ Q% d' ~6 K) U
  156.                 *wechat-5x5-black-chess*8 W2 s& A: i4 X2 b0 r) y7 H% E9 S* i
  157.               *wechat-5x5-white-chess*))
    0 k; W0 ]( J/ R; u+ ^1 t
  158.     (delete-char 1)))# b. [; ^; Y) G' s3 @, u- \& p

  159. 6 Q& U( L7 W  h
  160. (defun board-put (y x)
      v! c4 U5 Q4 V/ n  N6 f* e
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    4 d9 I# t0 m9 k' a
  162.     (board-toggle (+ y (first dir))
    $ d2 T  n5 K, y+ j7 y6 c' p! L
  163.                   (+ x (second dir)))))
    * B$ Q  c0 e) U! P: m# S( A. o
  164. : e- W* d2 U' _* z
  165. (defun game-over-p ()" {  D, L, d+ i- c
  166.   (beginning-of-buffer)
    + m: `& j4 l4 o( k& z8 M
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    0 G4 c/ u9 Y- J+ b4 x
  168. - u4 a$ j! F) j2 A; @0 s
  169. (defun board-show ()5 f. |8 G3 |  @, E' J
  170.   (with-board
    6 ]# @5 N( O( R) z
  171.    (concat (buffer-string). [% @8 E8 n+ p& N
  172.            (if (game-over-p)# K- q9 e* w  K% S- C: l
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))- H* \7 t  {" j" H) [! S
  174.             (format "第%d步" (1+ (session "step")))))))7 z( ^, j/ g, a, P- A7 u8 v+ ^* }
  175. . @- S( k7 D% u5 m$ x( W0 w* b
  176. (defun board-position-parse (cmd)+ T2 x, i9 K; ]7 u1 k! U; \0 k2 v
  177.   (if (= (length cmd) 2)
    1 h6 h+ U- o& q8 h( x* W3 h9 G
  178.       (list (string-to-int (substring cmd 0 1))2 [- D7 ]% R, a  ]
  179.             (string-to-int (substring cmd 1 2)))7 {. B& S/ J' Y) L# l$ x2 m7 c0 |
  180.     '(0 0)))
    / Y8 C8 U) ^) G. w; }; S# c

  181. : ?9 t7 z; z2 I5 d$ x! \
  182. ;;; 游戏房间  J  H! G/ u- @& C8 n& ]
  183. (defun game-room-init (cmd)4 ^; ~, E; v0 V* k. c9 e
  184.   (let* ((middle (string-to-int cmd))5 z# k  s9 k: t5 V
  185.          (size (1- (* 2 middle))))
    ; d$ f! Z3 O- B8 p
  186.     (with-board4 E; g- }6 d$ b' @# M8 L
  187.      (board-init size)
    . E6 m6 r1 G- E* a3 A
  188.      (board-put middle middle)))9 r2 U8 I! p# I" v! \( Z! N3 t
  189.   'game-room)9 f4 t0 Y2 ^0 K0 J% C' S$ P

  190. 6 D8 Y' @- B6 L3 [! r: C. K
  191. (def-room game-room
    : ]. w! m0 J8 {; q) m) ?
  192.   #'board-show
    $ v0 R. t; a/ |. \2 a5 u
  193.   (t (lambda (cmd)
    2 `. }  T7 b% q0 X" y
  194.          (with-board) D- J* q, P. x. Z0 T$ i* b) p: c- L
  195.           (if (game-over-p)
    2 |- c! A+ ?; ]7 \" R2 T! v
  196.               'living-room" x* X* N9 S; K
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    + `8 W0 w: ~$ k2 p8 k
  198.               (when (board-contains-p y x)! o9 u# X0 n' V; i1 U
  199.                 (board-toggle y x)
    ! }5 T- c& w- U* f* P9 V+ y
  200.                 (session "step" (1+ (session "step"))))
    ( v! m* D: p  T* R. p5 s
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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