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

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

  1. . Q6 }! u+ q/ z# P& V9 J
  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;">;; 定义新的游戏地图4 q$ q# R  G' ?5 G8 k
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    ) O( O0 P# Y( j! M9 G6 _. `
  4.   'tutorial-room-0)                     ; 默认的入口
    $ m$ B7 b/ J- r6 Z' X2 p
  5. * q2 W" X- C7 `* D) c$ W8 V
  6. ;;; 游戏大厅
    5 |) D8 w; m# O3 p0 A
  7. (def-room living-room2 E' E) k( U4 J/ R0 y6 ~8 o
  8.   ;; 进入该房间后的提示语
    # {- S! o8 g+ ]3 k  V1 R
  9.   "1. 教程
    + q! u( S8 P" B8 X9 j2 g
  10. 2. 入门(3x3)$ `& V3 ?& E: l+ i% Y& n3 N
  11. 3. 初级(5x5)
    3 O) s2 ?- J3 u( x$ e+ e+ f9 `& G5 b
  12. 4. 中级(7x7)
    5 p. Q8 @$ O! ]
  13. 5. 高级(9x9)
    , M& k' F7 F3 m2 A! r. ?
  14. 0. 关于作者
    1 T1 v1 b7 C- V. B$ p% G9 g' R& c
  15. 请选择1-5开始新游戏:"8 ^) M! X" ^9 Y3 X0 F
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    # |! G  Z( _5 h- _: W
  17.   ("1" tutorial-room-0)
    9 S$ B, r% T6 A/ R- N* Y: `
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    4 v% _5 m; I# R0 w. E, i* }7 [  z4 I+ _
  19.                                         ; 相应的返回也可以为函数,动态返回房间名$ e5 ?5 P' @% Z4 p3 v+ w2 ?5 l5 I
  20.   (t living-room))                      ; 如果条件为t,为永真
    6 d- f1 T: Y6 ]& O/ q
  21. 7 K# P" K, T( o! U2 p0 i! t
  22. ;;; 作者信息
    1 L7 ]$ R. c% a1 A4 j& {" J
  23. (def-room about-room
    9 L; Y1 d$ ?0 B& t
  24.   "作者:redraiment/ V/ x* K; P* C, g6 u3 G' k6 J/ a5 n
  25. 微博:http://weibo.com/redraiment
    + _* a# }7 [  f1 M
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    6 t2 F' B9 g1 X! Y, N0 Y- F+ j' E
  27. 请输入任意数字返回游戏大厅。"
    $ E$ d- T5 K7 g1 D5 b! t0 c% d: q$ @
  28.   (t living-room))
    1 d% c" c: i* }% {

  29. " V6 ]' E& o2 b
  30. ;;; 教程
    $ H- M. D1 r/ U
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    5 E2 r1 \  X) U: [8 K! O
  32.   "The number of tutorial rooms")
    % \8 {* K  C8 ~1 P% y% c

  33. - n0 I7 b9 m8 w- {
  34. ;;; 简化教程的定义( p$ H4 m& s: H7 _# n* L' G
  35. (defun string-last-line (content)) \  j( a% g5 b( o* ^' i
  36.   "多行内容组成的字符串中的最后一行"
    0 ~8 y- \5 Y" _
  37.   (with-temp-buffer
    * M/ l' i0 `( f5 i0 N
  38.     (insert content), k: }. k/ j$ f# M# }" ~4 \
  39.     (buffer-substring (line-beginning-position)# E4 A( H8 R; n3 a$ d! ]+ @
  40.                       (point-max))))& M& P9 O( k0 p: p+ q- [) x& v: I
  41. 2 y, t& O% W5 E9 z
  42. (defun def-tutorial-room (prompt)
    0 Z- T% A1 l" o  z" N5 Z
  43.   "根据提示语自动生成教程房间。4 a  {* {' b" @, ?! V7 Q
  44. - V: t: ]. B  ?+ v' Y) F; y" x
  45. 1. 提取最后一行作为问题;
    / L6 U; s' |8 U, C5 V' m" P
  46. 2. 分析问题,获取期望用户输入的内容;
    - a* o. P! v4 a" k+ Z  z/ J" w" ~5 ^
  47. 3. 定义教程房间和重复提问房间。"3 p' d# Y# _) q- C0 n, S# M
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))9 J$ O# _% c7 Z, p- Z7 k
  49.          (repeat-room (concat room-name "-repeat"))
    # B: S' {8 l7 n- P8 {2 D# C
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    ! U# f- o  b2 S) A6 H3 j4 V) F
  51.          (question (string-last-line prompt))
    " c& }; a  r1 d
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    - t& m; l6 A" N+ q* Q9 ^# n+ s
  53.                      (match-string 1 question)))
    8 q4 _, n" h3 R7 Q) Z. R; s
  54.          (doors (if except3 h+ E' q; N- m! v% H9 q+ Z
  55.                     `((,except ,(intern next-room))2 w: B% p% P; ]9 |0 w) G
  56.                       ("q" living-room)
    0 G+ [# C% B5 y5 _
  57.                       ("Q" living-room)
    . X) V/ f1 S1 Z4 _2 V0 |" H/ l
  58.                       (t ,(intern repeat-room)))3 C( C3 c; X" G0 O0 P
  59.                   '((t living-room)))))
    4 F' J# d& h4 U5 e- Y
  60.     (def-room-raw (intern room-name) prompt doors), R7 X9 E" a+ U" W: t' a
  61.     (def-room-raw (intern repeat-room) question doors)))
    . x6 R5 S3 s8 A3 {0 j# B

  62. 0 z6 D9 ?. C- C  D+ n
  63. (defun def-tutorial (&rest prompts)
    + w! u9 q/ }" x' t7 o% Q) C
  64.   "批量生成教程房间。"7 T* H" c- o: F: N
  65.   (dolist (prompt prompts)
    , ~+ x$ p' [' _4 f
  66.     (def-tutorial-room prompt))); q6 `0 |5 q# ~6 C

  67.   P3 f1 a- l& x/ q% |+ D; F
  68. (def-tutorial$ q9 @# k5 u! j1 r$ [9 F2 z1 M; i
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。" Z* E4 ]/ T8 ?- {# {
  70. 1. 教程0 X/ n. n4 e' g" K
  71. 2. 入门(3x3)1 q: ?5 I' C( l0 {
  72. 3. 初级(5x5)
    ! p$ n3 j% ?/ _' F! x. L( k
  73. 4. 中级(7x7)) W/ o% B  Z/ V/ X
  74. 5. 高级(9x9)
    # ^# S4 \) F: ~# q) a5 D: g
  75. 0. 关于作者& Q0 V$ g0 ^! k; D: A8 t7 i1 g
  76. 请选择1-5开始新游戏:
      a; H8 z/ k9 k( m& [) E- N1 V8 a
  77. 您现在正在游戏大厅里。
    / k# L, T# K/ O; ~
  78. 请输入“2”进入入门级房间"
    / t& V7 s( \, B
  79.   "  ①②③
    : v) y% _3 E; @; B+ z
  80. 1 ■ 5 _4 S* O! E  i3 S
  81. 2■■■
    " k4 A/ A2 r$ v8 X' o, f! S# p
  82. 3 ■ 6 W* }  S; A' h- x! e: r% P8 g
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!: }" U( y( ^  y
  84. 请输入“22”来关闭第2行第2列的窗户。"
    * T$ a: l, C! w. f  P0 |2 m
  85.   "  ①②③- d; c$ q( x. S& Y& {
  86. 1   $ f) ?6 S% h9 K7 K. l# }$ V
  87. 2   
    1 d% v1 F! M6 W8 U3 {
  88. 3   " {# Y6 b, y; L9 X- `! \8 k
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    . {) g2 }! A4 I2 P4 r' z5 U
  90. 请输入“11”,试着开启左上角的窗户。"3 T0 N) M* s% `: j! q( I+ i
  91.   "  ①②③4 [' t: p9 z5 y: K: R
  92. 1■■ 
    % K: e7 A5 }# `: S/ j: p, `$ g) R& l
  93. 2■  
    ( J4 U: J. _2 [
  94. 3   ) o. c1 C& d; w' u
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。8 J/ Q- b/ P2 c5 u' N2 A5 X
  96. 请输入“13”开启右上角的窗户。"
    ' p6 q/ K+ P0 F0 f
  97.   "  ①②③# b- ^/ [) H# c
  98. 1■ ■
    2 e8 K$ H; F5 @
  99. 2■ ■
    : [9 M8 _6 y4 I; T0 o, H" B# g) j; N; l
  100. 3   $ B/ G0 r4 r  H! C- b5 r: R
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    ( B0 l5 l. H) @4 M
  102. 请输入“31”开启左下角的窗户。"0 Z2 I  b  a% B, q& d9 x
  103.   "  ①②③
    , M) }: O  Y3 A- n2 w
  104. 1■ ■8 P' j  i. y" N
  105. 2  ■# C# ]; n0 ~9 \! e5 U/ U! ^% ]
  106. 3■■ " S# _5 z9 A" u8 M# Q1 v
  107. 此时,总共有5扇窗户被开启了。& G1 m+ F, N8 ~( y0 V( e8 b1 ]
  108. 请输入“33”开启右下角的窗户。"
    & E+ @0 L: t( f4 `, a& f+ _
  109.   "  ①②③
    + t* g8 j9 |, G. Y) ^( M
  110. 1■ ■, i: `4 V  J$ H' S5 S; _. R; f0 I
  111. 2   
    - \! G: Y: d1 Y3 S
  112. 3■ ■' `! ]) t1 R+ z1 h7 Z
  113. 现在,只有四个角落的窗户被打开。
    ; C' r  e! M' Y1 H- \
  114. 请输入“22”完成最后一击!"8 e  v% \) q; m1 R5 }
  115.   "  ①②③
    2 A9 f: G" T# M5 A, s: N3 y
  116. 1■■■0 y( X4 y3 t* B/ @' `$ h
  117. 2■■■; y; t& a0 H2 T9 S0 }/ \3 n: @6 B$ ]+ P- ?
  118. 3■■■; v- _& ?' f7 z2 k8 J: h/ q9 w
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!"), q: L4 w* `$ w6 z

  120. 0 O7 ~% E% s0 X: g2 i
  121. ;;; 棋盘
    $ |$ y4 k6 Q- s! _3 Q" S0 T: z
  122. (defconst *wechat-5x5-white-chess* 12288$ [: h  W, S+ e2 e5 p
  123.   " ")  n7 O& A  z1 p1 i) `! p6 E+ Z$ C
  124. + b5 S+ H% E# T# M! O6 Y  q4 l
  125. (defconst *wechat-5x5-black-chess* 9632/ ^% U- v* w; w! W
  126.   "■")
    % w% w) x/ K2 R3 p

  127. 8 e- }8 h  q2 |% R
  128. (defmacro with-board (&rest body)
    6 B" g1 d) k$ ?) F' W5 U
  129.   `(with-temp-buffer2 n- l4 D% u( S( v
  130.      (unwind-protect
    6 B" E2 Q* t( ]% q
  131.          (progn
    0 \9 K- j; k- {* J$ T- d3 ^% y
  132.            (if (session "board")
    6 i& ^4 Q9 X9 ?
  133.                (insert (session "board")))
    ) x/ ^% Q" L+ i# b/ c. |! \, I
  134.            ,@body). s0 k1 E. z, O& ?$ c; u/ [7 y, u* u
  135.        (session "board" (buffer-string)))))3 ~! ^3 B- c- h! c  Z4 M+ R% r
  136. * R# j3 O0 Q# \8 [$ M
  137. (defun board-init (size)
    ( [+ F1 J" S8 l) F1 ]2 \, M
  138.   (session "size" size)7 e! F" z) {/ J
  139.   (session "step" 0)
    + |0 v2 F' W0 t! p
  140.   (erase-buffer)
    ( j& b  W, B! p: e& a
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    ! l9 M) \9 l8 c6 l6 l2 _. {
  142.   (dotimes (row size)) p% s, e: w3 z# a0 I) U, ]1 Z& x
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    * T) f0 o5 M% y8 Z" T
  144. ! w* \; m" X- r4 U' C$ I
  145. (defun board-contains-p (y x)
    0 b. Y" F! e, \. B$ v, U5 r
  146.   (let ((size (session "size")))& E+ s: I/ j% c3 ?9 F$ F& R, s
  147.     (and (<= 1 y) (<= y size)
    ' Z: s( b" T; t  \# r# |, r
  148.          (<= 1 x) (<= x size))))
    1 r( |$ ?' J- l5 V- [
  149. 8 F7 s" P& n" i1 H# k# F
  150. (defun board-toggle (y x)5 A  p; y3 ]5 u6 Y, N0 w
  151.   (when (board-contains-p y x), R8 G- c1 C1 a" t
  152.     (goto-line (1+ y))
    / b0 P2 l* K5 T1 P1 L' B1 e+ W
  153.     (beginning-of-line)! g- M; T) h  e
  154.     (forward-char x)1 B+ [' f' w3 U
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))( F2 u, S" g3 g1 M  X: l
  156.                 *wechat-5x5-black-chess*1 \6 A! K8 g8 y) P' y
  157.               *wechat-5x5-white-chess*))
    % J  N, M, Z, m9 H" z# k/ t
  158.     (delete-char 1)))
    " ]$ F; x3 S) T% w. ?' z7 `' p- U
  159. + F% K2 O2 {/ m- H" B
  160. (defun board-put (y x)
      J: M, ?- m, y# @5 M
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0))): P- {) s# ^- i: Z& d7 G; h
  162.     (board-toggle (+ y (first dir))- X2 U# o& i3 K$ q
  163.                   (+ x (second dir)))))
    ' ^( d2 f4 K* a1 \. f& k

  164. 1 i* j: f  t# w8 d, L$ h
  165. (defun game-over-p ()
      D& U# q. q8 P8 W$ m
  166.   (beginning-of-buffer)' R. }2 [. y5 L% B3 _1 B$ @
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    ' }% O9 X  Y& Q

  168. # P4 C$ s0 Z; O2 O+ _0 |# q
  169. (defun board-show ()8 c- R, }, N+ r/ }0 J( S
  170.   (with-board1 V1 F( P! @9 D* F0 }. B
  171.    (concat (buffer-string)# K  q% R5 n) O
  172.            (if (game-over-p)
    . n' u* B* _9 e9 _8 ]9 f9 Y
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))" t$ k! W: J7 s4 v1 o
  174.             (format "第%d步" (1+ (session "step")))))))
    2 T" a8 s+ u1 s1 f/ L

  175. , W; U* I: y1 l1 e9 }
  176. (defun board-position-parse (cmd)$ |5 G8 ~% D1 A5 ?: V
  177.   (if (= (length cmd) 2)
    % v4 f3 G3 x. K
  178.       (list (string-to-int (substring cmd 0 1)). h2 l) @0 S) m0 C0 Z- H5 T
  179.             (string-to-int (substring cmd 1 2)))( `+ _5 A% C6 M/ p
  180.     '(0 0)))$ R2 ^8 I* x* b. ~6 x7 u) _

  181. ! P( V" v6 P. R" X* [. |; ]. `
  182. ;;; 游戏房间* `0 Y6 E1 }2 x+ W
  183. (defun game-room-init (cmd)
    6 e+ f* ~, `: P& X# @( o" h
  184.   (let* ((middle (string-to-int cmd))7 e) n( W- m$ }: S
  185.          (size (1- (* 2 middle)))). I9 i1 L8 s# t9 x/ j
  186.     (with-board, I/ C: f9 ?& R* b
  187.      (board-init size)
    , c0 l' f% d9 w
  188.      (board-put middle middle)))
    " Q1 A5 x" p& n: \, O9 D- B' T# }4 }
  189.   'game-room)
    ! F: ?4 l, \- [
  190.   Y6 }: g- x( ?: b* c" _- [! X$ t/ S" t
  191. (def-room game-room
    ) c- J) {" j. H- Y$ `! c
  192.   #'board-show2 H/ z% K; J1 d
  193.   (t (lambda (cmd)
      X- P: w3 f; q  d2 g
  194.          (with-board
    0 \1 [5 N  F  K  D/ C3 \4 P. p
  195.           (if (game-over-p)  O( r8 v5 ^& a3 I0 C* t' G
  196.               'living-room
    / `; ^9 h5 g" b6 J3 l: L
  197.             (destructuring-bind (y x) (board-position-parse cmd)! r: ?& ^# Z8 v+ X5 {
  198.               (when (board-contains-p y x)0 i! ?9 U' l& k: b. e
  199.                 (board-toggle y x)2 C. \" [! j7 t* |5 L
  200.                 (session "step" (1+ (session "step"))))
    ' v. j' ?, O0 ^3 P" W# g9 m! S( r
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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