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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。+ {( {& W' B2 ~
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. 4 ]7 Q7 V9 t3 O# ^/ p" 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;">;; 定义新的游戏地图
    3 S  o; y  h" p" ^
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    + {7 ?5 e& ^# [6 X
  4.   'tutorial-room-0)                     ; 默认的入口
    . P. O8 i" }+ W0 c& J1 ]" [

  5. ) U$ t3 S+ `( Y& a0 r8 N
  6. ;;; 游戏大厅
    ' C" l1 @" C0 H! c$ f
  7. (def-room living-room
    ) D" m' T: W, f/ Z& j( p' q/ S
  8.   ;; 进入该房间后的提示语
    # b" v; h. e& Y# y2 c8 c
  9.   "1. 教程
    + l; e* q! ]4 Z3 G) t9 R
  10. 2. 入门(3x3)1 J! z$ B' ~% O) \1 k3 w7 D
  11. 3. 初级(5x5)
    ; e* C8 I: @1 t0 `% ~$ C4 T- I
  12. 4. 中级(7x7)% n2 A" N, t1 n2 @/ L5 {/ ^* h) [
  13. 5. 高级(9x9)
    ' u& Z+ ^% T' b
  14. 0. 关于作者# L! ^- `& v  `, o. k
  15. 请选择1-5开始新游戏:"
    ! j4 B- l. h; X, u" i5 J' S! V
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    4 Y& H5 W, l+ S/ F
  17.   ("1" tutorial-room-0)
    1 x& K8 E/ e% z+ L2 Y! C
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    9 H6 u9 _7 C, G! N7 {
  19.                                         ; 相应的返回也可以为函数,动态返回房间名; B: B$ q: K* f4 c, M
  20.   (t living-room))                      ; 如果条件为t,为永真  B, L$ \, X8 b5 U+ I
  21. ) y/ [. G1 Y& S* x, C
  22. ;;; 作者信息
    : [; B9 N- v  F' q! A+ {1 P: y
  23. (def-room about-room3 O1 v4 s$ m5 F! T; k8 _! i
  24.   "作者:redraiment
    0 z5 q1 m, Z, I! d9 M1 T  X
  25. 微博:http://weibo.com/redraiment/ r! I; s! x( p7 W5 o: r
  26. 有任何建议,欢迎在微博或微信上联系redraiment。. D' o, o- {3 `9 O4 o3 }. E
  27. 请输入任意数字返回游戏大厅。"9 `) J( n6 u4 K
  28.   (t living-room))
    ' M$ D3 Z! F# j  g/ m, C- M

  29. $ Z/ }$ ?( j8 u7 V" s( Y
  30. ;;; 教程
    & C# _$ L; J3 G9 x8 [: U) U- P! W
  31. (defvar *wechat-5x5-tutorial-rooms* 0+ {; C7 h# D  {) K/ N; e% _. r6 W
  32.   "The number of tutorial rooms")0 s+ u# T6 ^) _

  33. . m: t5 B6 ~% U% V( ]; Z
  34. ;;; 简化教程的定义6 e# Y. }; s6 C
  35. (defun string-last-line (content)
    ; h) ^  E9 X/ R/ i3 d' E) T
  36.   "多行内容组成的字符串中的最后一行"' c- i+ I# v/ A
  37.   (with-temp-buffer
    1 Z3 g) `& ]* R' }' m) g
  38.     (insert content)
    ; F) B3 M! d8 Q& i" D
  39.     (buffer-substring (line-beginning-position)" Y0 q; Z& K1 h% [# j1 L! _
  40.                       (point-max))))
    + O" v/ b* |! ?
  41. 1 Y7 m+ @3 r$ ~( [
  42. (defun def-tutorial-room (prompt)7 D; o/ |6 K7 A1 N# k
  43.   "根据提示语自动生成教程房间。
    7 M+ q, K6 U# K1 O* ^- \

  44. 8 Q! @5 y5 x6 O/ X: n8 ^
  45. 1. 提取最后一行作为问题;
    6 J8 |- l# n2 o( Y. g, w$ o
  46. 2. 分析问题,获取期望用户输入的内容;
    ! j3 @# ^7 p" l6 q6 v6 [
  47. 3. 定义教程房间和重复提问房间。"
    - Z" J; `7 a' w# ~' P# n1 _
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    - S8 R4 ?* M! H- b7 N
  49.          (repeat-room (concat room-name "-repeat"))
    3 C; e* i8 U8 V& |/ a, m' N
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    $ }4 O5 r* z" p0 _; _2 n0 r2 d+ i
  51.          (question (string-last-line prompt))
    $ S- W, ^# Z9 N7 P
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    ) v2 c& u) ?0 k5 [, _( n! ^
  53.                      (match-string 1 question)))
    ' e; D$ V8 [4 O2 l# c
  54.          (doors (if except: S( m; I- U7 l, W" M
  55.                     `((,except ,(intern next-room))
    ' N" G$ C7 ^. b; K0 A$ \
  56.                       ("q" living-room)
    # x; {1 n. [" G  ?4 q5 @/ T6 Z3 z( w
  57.                       ("Q" living-room). i. s1 p. T! V, W, C! r( ^9 K
  58.                       (t ,(intern repeat-room)))
    + m( i6 e6 @4 I* j. Z/ n
  59.                   '((t living-room)))))
    & s# z. {3 v7 M, l
  60.     (def-room-raw (intern room-name) prompt doors)
    & T! T) Y% r0 C5 A* O! G
  61.     (def-room-raw (intern repeat-room) question doors)))
    # E% M$ C) D5 v% F
  62. ' q) }8 j* Q' E5 Q- h, C6 ~# t
  63. (defun def-tutorial (&rest prompts)
    8 o& |) H5 M. ?
  64.   "批量生成教程房间。"7 U9 N& S2 C  N9 b+ O
  65.   (dolist (prompt prompts)
    2 j% p, p) e: h4 u" ^+ o
  66.     (def-tutorial-room prompt)))7 N) ?: `" T! i# P! O; h
  67. 3 X2 I/ [8 P! Q) t* e& j0 S
  68. (def-tutorial& K* S  j; W$ p7 }5 G
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。1 p+ w6 y; a9 |& a& j9 N2 s% q& _
  70. 1. 教程
    + d) {( _- D5 _( ~4 L
  71. 2. 入门(3x3)" }! W' ~% b- f$ v' D. e8 F, N
  72. 3. 初级(5x5)- E! n9 x8 J: S) }
  73. 4. 中级(7x7)" Q4 s- Y% l' N- C5 V
  74. 5. 高级(9x9)) t% X1 ?9 c6 }+ S* E+ I( S+ k- x/ d
  75. 0. 关于作者
    # H& a, V" e+ V) ?, Q" V4 x
  76. 请选择1-5开始新游戏:4 O8 T0 b; x9 W/ f: ?0 Y% m- \  d
  77. 您现在正在游戏大厅里。
    % k+ y( }; A3 U: g& U2 y
  78. 请输入“2”进入入门级房间"- |7 b# a& R" o
  79.   "  ①②③. |& W" ~7 n2 O& c5 K# e' ~7 u
  80. 1 ■ $ P" r- i% h0 K" c9 E
  81. 2■■■( X: l! b, j, s
  82. 3 ■ ) b  c  L7 u. k$ t+ p+ @" G- K
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!( Y7 T" G/ W# Y- ], w) \
  84. 请输入“22”来关闭第2行第2列的窗户。"0 t* S2 n6 I9 _. A" M9 j1 L' H
  85.   "  ①②③
    0 p6 \, n' Y$ K
  86. 1   
    1 D8 P6 F( I( {6 x
  87. 2   4 Y; a8 @) S" W6 k% e
  88. 3   
    * K6 _' K* H$ j. H
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    9 g; {' ?8 r: _; }
  90. 请输入“11”,试着开启左上角的窗户。"8 a1 P# h) b# j( J: b# Q5 k
  91.   "  ①②③( R* H8 i) B% b. Y6 @1 L1 z
  92. 1■■ 
    " ~" W% Y9 x! S+ ]% z8 I7 J1 w
  93. 2■  
    * ?& `  q* [' `. M# [
  94. 3   ) J; b7 _: G- g' w7 }
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    ) T" ?4 w; d9 A$ w& Q- n
  96. 请输入“13”开启右上角的窗户。"! Z# S7 S7 f/ m/ J
  97.   "  ①②③; F! f  w7 t: b8 P6 a
  98. 1■ ■  s* h. Z* |9 _" g5 g
  99. 2■ ■
    9 c7 W% x6 v. Z. W
  100. 3   ! x! S, R, d( w1 D" V( [  ]
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。5 m; s$ f+ f- z: C+ D+ ]
  102. 请输入“31”开启左下角的窗户。"
    , ^" q" Z* {7 M" p  X
  103.   "  ①②③1 X! |! U' X/ Q1 H8 `
  104. 1■ ■+ r* H" w4 Z+ t7 d: m# @* {
  105. 2  ■* N% I) t& L- G& |8 U3 C7 g; k
  106. 3■■ 9 Q; a$ t( X7 f$ z- o' A3 z
  107. 此时,总共有5扇窗户被开启了。8 a9 ?9 X2 v! R/ Z2 J$ ~, U  M1 V
  108. 请输入“33”开启右下角的窗户。"5 \  E" u0 n( b* ^8 z! F5 g
  109.   "  ①②③
    ' l; Y5 ]6 H; t0 m
  110. 1■ ■) L+ j# ]2 J) B2 i/ f0 u
  111. 2   % X/ f% i( `; R, ]- c
  112. 3■ ■
    7 s# V) ]2 M; g5 l# ^$ U( P$ @
  113. 现在,只有四个角落的窗户被打开。
    7 b: h+ a4 ?# w- ^8 s6 G( b
  114. 请输入“22”完成最后一击!"
    " {. d8 h  g0 ]( `/ ]& Q3 M
  115.   "  ①②③
    , `; D- o3 u) r; V  k# F
  116. 1■■■
    + J1 n1 ~; O' u5 d
  117. 2■■■, Z" ~0 R+ m2 j2 V- b: a$ r
  118. 3■■■
    6 V" n7 D( n& R' h4 e" C/ \
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    ; O* u6 H4 n) p+ G' ?* Q

  120. ) q- N7 j9 h, g; k. r8 n& J
  121. ;;; 棋盘  p/ |% m' T) Y9 X# T5 i
  122. (defconst *wechat-5x5-white-chess* 12288
    4 F: }. r, N# G  V' q7 K2 b3 v
  123.   " ")
    8 Z( U. i, o9 w- {/ y
  124. + O6 F1 h' R# c
  125. (defconst *wechat-5x5-black-chess* 96324 V5 ]" Q, I1 x7 H% }
  126.   "■")+ |- Q/ W. v/ @  k: [

  127. / e  l: f; K) e- ]
  128. (defmacro with-board (&rest body)5 @; k& _9 L- g& o
  129.   `(with-temp-buffer/ N" \  f' l& o" a! k& L
  130.      (unwind-protect# c- Z' H: z% M$ v) A! ~
  131.          (progn* P9 W& ]4 M: U
  132.            (if (session "board")
    6 y# K7 D/ @1 S, G* r+ x
  133.                (insert (session "board")))* T) K- y2 d6 D( [3 c5 w$ @- k2 j
  134.            ,@body)6 ^4 @9 Q( V" U3 }) z6 ^: k# m# y
  135.        (session "board" (buffer-string))))): K* R& D$ z0 d- b. V+ F

  136. 1 \2 X, G' U: {( W* F0 {+ s
  137. (defun board-init (size)
    ; L) M% z( l- o* y
  138.   (session "size" size)
    4 ]; C5 P/ F, q, g" H/ d. u
  139.   (session "step" 0). [9 n+ K2 z0 V( O: I9 ?# i7 d( s0 b
  140.   (erase-buffer)& c9 c5 g) ]) d+ ?
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    * P) K( S" B2 O0 Q$ h( x0 b
  142.   (dotimes (row size): r: `6 K. a8 H: X( H
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))4 X# k' b$ w9 c5 u4 D
  144. 3 l6 B8 Q  a' d' i! G
  145. (defun board-contains-p (y x)
    8 M/ x+ D% }. o/ g) T# a+ A
  146.   (let ((size (session "size")))
    6 m/ b0 j; c: @: A3 m( F
  147.     (and (<= 1 y) (<= y size)( X: j5 R$ t/ F0 Y. U& j& C8 G) f. V
  148.          (<= 1 x) (<= x size))))
    2 }4 k2 v, H/ \/ G2 |. H5 O6 V

  149. / U! ~6 k1 j* N2 S. {
  150. (defun board-toggle (y x)
    8 F8 c2 G0 S! U0 u
  151.   (when (board-contains-p y x)+ z3 ~. B& I5 B. D% `! m
  152.     (goto-line (1+ y))
    % ^* K1 e$ Y9 F2 J, s
  153.     (beginning-of-line). @" r# m. A) z; U- O9 y( ~
  154.     (forward-char x)( ~2 S0 ?& U$ A) {! `8 w
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    ! `3 H! E  r5 \# y5 U
  156.                 *wechat-5x5-black-chess*0 h8 Q7 w8 F8 Y# g: C
  157.               *wechat-5x5-white-chess*))
    4 ?! e# ~+ Y& a. x0 M: n: s% I
  158.     (delete-char 1)))
    - U6 i5 g1 a- n2 @6 _' U

  159. % E) A& p3 d1 b4 n8 }0 }$ D. T1 |- V5 [
  160. (defun board-put (y x)
    + ]* o; |$ }( s( ^
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    # A# J! y6 A& ^- o( q3 l
  162.     (board-toggle (+ y (first dir))0 T, v8 w$ H1 L% j. G1 \) |3 }& ?; o
  163.                   (+ x (second dir))))). E) n$ z9 n3 U" J% R+ Z2 l! N

  164. ) \/ c( d& M* E! ~! C* _: t
  165. (defun game-over-p ()% O3 B1 s7 ~$ e
  166.   (beginning-of-buffer)# i; {! {' f0 D) v3 n' o# W; W, K
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t))), Q5 s% k* d+ i# E
  168. # h  f- E9 M$ D3 e4 U9 ]
  169. (defun board-show ()  t2 H# A4 K' U& j5 ~: o2 Z& b
  170.   (with-board9 z: [+ C  }: Y! x
  171.    (concat (buffer-string)
    / a$ N, ~1 B! k0 V
  172.            (if (game-over-p)
    9 |2 s6 z  l- D% h/ f
  173.               (format "共%d步,输入任意内容返回大厅" (session "step")), G  d5 c8 q8 F1 b, ]
  174.             (format "第%d步" (1+ (session "step")))))))
    8 [+ G" D; F& u) n4 n, _9 X

  175. : q' s3 N' w# O( a
  176. (defun board-position-parse (cmd)
    ) D- t* q. v' X" R4 L
  177.   (if (= (length cmd) 2)
    1 ?$ U: K7 j9 C5 j! [9 _, E
  178.       (list (string-to-int (substring cmd 0 1))
    , D0 E  h' w" V
  179.             (string-to-int (substring cmd 1 2)))2 |2 s8 W  ?' u
  180.     '(0 0)))
    % X& W2 l; `6 _2 U& ~. m3 C
  181. . o4 ^4 b2 P  T1 q1 D- E+ |
  182. ;;; 游戏房间
    7 l( {0 N- q3 s
  183. (defun game-room-init (cmd)
    5 F! G! M9 `" ?
  184.   (let* ((middle (string-to-int cmd))
    ' o9 h) h: ]/ m4 f) `
  185.          (size (1- (* 2 middle))))& B3 i4 h1 l( J
  186.     (with-board# W" N4 H4 y6 c9 w. G) @
  187.      (board-init size)
    , D! W9 P9 z2 W/ G& u5 o  U
  188.      (board-put middle middle)))2 i* g0 i  Z6 x
  189.   'game-room)9 g$ L5 O# \2 |1 Q9 _' ?
  190. + m0 s+ {& p1 d+ V6 _' z, s' o
  191. (def-room game-room
    3 X4 _; f. s4 Z, w8 W
  192.   #'board-show
    ) Z2 ~+ ]1 E) g# a3 D* I3 D1 k
  193.   (t (lambda (cmd)7 D# l+ G1 K, P) Z# A, {5 W2 Y; \
  194.          (with-board
    : j" E: i, S2 T$ |, Y8 b
  195.           (if (game-over-p)6 y) |" I4 d' q4 u
  196.               'living-room# [2 a7 {3 P$ K9 U+ c, c
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    * {* }/ B& x- H, I
  198.               (when (board-contains-p y x)
    7 b! r  C5 P7 G7 q! K# b8 q
  199.                 (board-toggle y x)
    $ Y0 ~  R/ {5 n/ T  M4 v# K
  200.                 (session "step" (1+ (session "step"))))* C, Z/ N$ b' X4 t: h7 q
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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