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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
8 p2 R7 P! D) \: d$ g借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. ! I8 x; e" b0 |1 b* ?' m/ ?
  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;">;; 定义新的游戏地图
    6 p! |0 f0 f5 z/ \  s
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    7 u4 y9 v/ }; u# B' R7 \- f
  4.   'tutorial-room-0)                     ; 默认的入口
    / ^9 p! B1 y' e- _  {; Y8 S4 D! r
  5. " P. \! G* P8 V8 x
  6. ;;; 游戏大厅; A8 e9 x) n/ \7 V  L. }
  7. (def-room living-room( X0 i3 h) V7 C& d( n4 \! Y
  8.   ;; 进入该房间后的提示语; ]: H$ ]# N$ G0 v' f2 r
  9.   "1. 教程7 ?. n  u5 Q+ L
  10. 2. 入门(3x3)0 b" B: w5 R' L2 J2 g# z0 `2 @
  11. 3. 初级(5x5)4 z2 s8 z% P9 ^6 K' |, Z) n! Y
  12. 4. 中级(7x7)& r7 T6 e. x" Q. f6 Y7 d; X& R
  13. 5. 高级(9x9)% _9 F9 `/ V/ J8 L9 A
  14. 0. 关于作者9 j: v" V" `+ }. K# `3 s5 H
  15. 请选择1-5开始新游戏:"7 ^* t6 w# p9 W' ]" N  y( p9 P
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名4 ?3 e8 G4 _8 t% I( q; E; B' I6 e, R
  17.   ("1" tutorial-room-0)
    4 X: C1 G1 G* h* e' X' A  j. d% V
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配: d& x) P9 ?. s% h0 j! R2 x7 @
  19.                                         ; 相应的返回也可以为函数,动态返回房间名3 n- }: g9 \" C: s5 C; p1 g* N; c
  20.   (t living-room))                      ; 如果条件为t,为永真( d# n/ y0 }3 O  u2 T& E

  21. ; U9 t) s, o& {  \
  22. ;;; 作者信息$ m! {/ i, `+ p  v: W4 {# t
  23. (def-room about-room
    " F/ ]: c1 T5 Y6 B/ ?
  24.   "作者:redraiment! G8 [& Y5 _3 P+ f4 _, h
  25. 微博:http://weibo.com/redraiment  @3 K+ k1 N* d% ?0 \, P
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    8 ]9 D# O- n' Q0 R; t! P. T
  27. 请输入任意数字返回游戏大厅。"6 X7 I6 B( i1 L& t
  28.   (t living-room))9 J2 _, H; h' P1 @% t+ t
  29. & h" H; B: z7 j' M3 I% x
  30. ;;; 教程/ ~8 q: O9 F: z' H2 l* _9 K( }6 {2 E
  31. (defvar *wechat-5x5-tutorial-rooms* 0% a  c; l( X. l' i/ a4 x2 L) z
  32.   "The number of tutorial rooms")
    + M9 A7 z3 t' G" I8 p9 t

  33. 8 {6 ?# v; w( ^" V% U
  34. ;;; 简化教程的定义' A* X2 L) s$ D( Y2 n6 k
  35. (defun string-last-line (content)' y* o  \& Q+ j/ v/ {
  36.   "多行内容组成的字符串中的最后一行"0 B& F2 Y. N+ g$ S7 p4 r9 F
  37.   (with-temp-buffer/ d0 |' W- ?' w- w
  38.     (insert content)
    # j, V: i% g$ G4 X
  39.     (buffer-substring (line-beginning-position)0 F2 G) I) P, j! K9 w/ g7 C+ e
  40.                       (point-max))))
    & G/ Z; D; y* _5 o

  41. ! a' K! @2 U- j# S
  42. (defun def-tutorial-room (prompt)4 A5 v, ^% L6 j- t) n6 z3 v# v$ J6 o
  43.   "根据提示语自动生成教程房间。' J' b7 @: {. ~
  44. ) N2 t% R& ~$ k* i; `
  45. 1. 提取最后一行作为问题;" w, k  B- f; C. a, E- Q
  46. 2. 分析问题,获取期望用户输入的内容;
    ! z# S  V$ H) z% \
  47. 3. 定义教程房间和重复提问房间。"
    6 z4 \9 y4 i$ \5 \8 |8 s  L
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    & ^; u$ `4 U' i+ K7 V8 J. R
  49.          (repeat-room (concat room-name "-repeat"))  D; z0 u# N; P6 s2 ]) B: m4 X% t3 x# `
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))* r! g! q/ I0 o4 f
  51.          (question (string-last-line prompt)): b8 G4 H# q+ U* c* p
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    , X. M" J3 x1 ~: ~  M
  53.                      (match-string 1 question)))8 X' }( n6 h+ w" m1 D6 F# S
  54.          (doors (if except# g( Y+ [' q- |) i
  55.                     `((,except ,(intern next-room)); Q& m% Z" c1 T
  56.                       ("q" living-room)& a0 c- W1 R, J. s: s( t2 {1 i  D
  57.                       ("Q" living-room)  d% x+ P4 Q! R$ B
  58.                       (t ,(intern repeat-room))): u1 y* o) D3 E8 y9 u- i
  59.                   '((t living-room)))))
    * w3 K% u' l& L0 z9 b: Q0 z$ R3 z
  60.     (def-room-raw (intern room-name) prompt doors)
    " R1 r- G1 d" V
  61.     (def-room-raw (intern repeat-room) question doors)))% W/ }" ^2 N' |- ~1 z; }
  62. # w3 h/ i1 ]# U. f# g2 E
  63. (defun def-tutorial (&rest prompts)
    # ]9 x2 W. g. \5 f# v" P; |
  64.   "批量生成教程房间。"
    1 K4 b& q& k3 B; Z
  65.   (dolist (prompt prompts)
    $ K& h/ `  s. S  F( h/ V7 o
  66.     (def-tutorial-room prompt)))
    9 d6 E5 F2 I, ?5 O# |! o5 E, V) |
  67. # l  x+ S. q8 o( m9 R3 b- Y' R
  68. (def-tutorial6 c- h! E1 m0 d  t  r1 C4 I
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    , p) g9 k. a/ ^
  70. 1. 教程
    # k" F! h5 C& R5 K: t; i
  71. 2. 入门(3x3)6 P" b/ [9 d( K  P1 Q* S% M7 G
  72. 3. 初级(5x5)
    " y7 m4 h! e0 S6 Z6 ]) G% c
  73. 4. 中级(7x7)
    ; ^7 E- r( K, s. _" Y0 K. ?: c
  74. 5. 高级(9x9)3 }0 F" d, u. a' g8 `$ d9 r' X
  75. 0. 关于作者
    8 {  q; S. c# r" u% ?
  76. 请选择1-5开始新游戏:! \( t+ _, E1 l& H. ?0 Q7 v
  77. 您现在正在游戏大厅里。
    9 ]" {% y- R1 b- w4 O6 k
  78. 请输入“2”进入入门级房间", ^* w( {/ Y  b: Z- Y4 h
  79.   "  ①②③
    ( i; u. C1 _, H+ B! u* ~: L
  80. 1 ■ 
    % U7 J8 C( S0 @+ C% ^
  81. 2■■■
      p6 {) R) Q( M0 e
  82. 3 ■ 
    # n( O! [& \( H. G! ~8 D) x4 \: Z
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!  o9 c$ H) l% I: n- x$ ^. H2 H
  84. 请输入“22”来关闭第2行第2列的窗户。"5 Z( K8 O% [4 T( L: N. _8 g
  85.   "  ①②③
    0 W9 ?* C% {: z6 I& Y# e
  86. 1   
    1 c7 ?2 @( F2 t0 |
  87. 2   . `- F9 b1 d$ t1 p  _$ Y2 Q1 d! C
  88. 3   
    * ~- e" }; r1 b$ m1 f( ]
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    # A; h3 X, N, B  u" L
  90. 请输入“11”,试着开启左上角的窗户。"
    1 s/ [# \9 z- Z& R9 `
  91.   "  ①②③
    / X* h2 E. q  L/ N
  92. 1■■ 
    * G4 d( O. t" @$ R
  93. 2■  
    : A& h' p: z4 S& |" o% B2 Y. Y7 Q
  94. 3   4 f8 [; F. Y9 L
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    8 m0 K+ Y( c7 d$ q& X
  96. 请输入“13”开启右上角的窗户。"
    . x. g9 O- z. N  i* g7 ~8 `
  97.   "  ①②③
    3 ^, }0 z3 x) a/ B5 a9 O* c
  98. 1■ ■1 o6 ~1 h6 d* t) i3 I
  99. 2■ ■0 ]4 F! v- h: ]# l, a
  100. 3   ) S6 x5 J' B3 Z6 I5 e
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    ; m8 G+ y- M" r" X) l4 x
  102. 请输入“31”开启左下角的窗户。"5 U$ z- L, h1 V
  103.   "  ①②③
    ; B9 \3 `2 B, T. M; |% E4 Q! ]
  104. 1■ ■0 g* h! H9 J, G# Q' m8 E
  105. 2  ■: ~) x4 ^" ?" `% j* ]
  106. 3■■ $ C) E0 E9 Y5 D. J) O' N+ t
  107. 此时,总共有5扇窗户被开启了。) \5 R  A' i! z- S
  108. 请输入“33”开启右下角的窗户。", L; S" G+ i- \& N
  109.   "  ①②③
    ' W  D: e' z3 R, h& e* v
  110. 1■ ■1 ~* Q4 X; P( f6 \" w- z+ f2 \5 d
  111. 2   
    $ D  h8 a# z0 h
  112. 3■ ■# @; M( b5 U& `( z4 B7 d2 F/ r, T
  113. 现在,只有四个角落的窗户被打开。8 y3 m$ s( G: q8 W' R
  114. 请输入“22”完成最后一击!"
    2 X+ ?+ S1 U7 I; {6 i' l; O
  115.   "  ①②③+ ?; Z; Q" I& p1 Q3 \  G
  116. 1■■■
    ! t3 W/ G# ^8 u; z7 v
  117. 2■■■5 ?& `4 \- R) o5 x9 k( R  C
  118. 3■■■: C* t5 q9 m! p% K
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    8 e1 K+ Q: P- ?4 d4 d* K1 m

  120.   F0 W5 k8 V' m5 K+ E' Y2 I* k! ]
  121. ;;; 棋盘
    ' C' s4 l  _5 t" e9 }: @1 ]# h
  122. (defconst *wechat-5x5-white-chess* 12288; h# j: N# {# n" o( e
  123.   " ")1 c  r# ], _  {( E/ ]' @

  124. % P: }5 t' G  D
  125. (defconst *wechat-5x5-black-chess* 9632
    2 t$ H/ O1 x% R; ^9 |" L5 }- v2 @
  126.   "■")/ T) o4 c* D* H" ?& P! R* ^, t8 P
  127. 1 P- ~3 ]) T7 [+ m
  128. (defmacro with-board (&rest body)
    . ~% Y* j2 x/ K2 j" P. t) Z
  129.   `(with-temp-buffer
    ) R$ E- w8 T$ E4 Q8 ^1 L
  130.      (unwind-protect/ P, t& r4 w3 i7 B
  131.          (progn
    + y- @9 _2 c- I8 c1 c2 G! I. s* @1 W
  132.            (if (session "board")# w" ]8 Y: D  j* `5 V
  133.                (insert (session "board")))
    / m) K8 k7 r% l5 P2 K+ }, {% p. ~
  134.            ,@body)+ M. f/ v! `  k7 n5 H$ @& R7 |% }. W! P
  135.        (session "board" (buffer-string)))))
    9 Q( H; g) w8 z/ J
  136. & |( @+ B/ t$ q4 s9 W# X3 R
  137. (defun board-init (size)
      o4 M$ B4 [2 Q4 O
  138.   (session "size" size): d9 Q" Z* ^. t! B
  139.   (session "step" 0)( g0 a1 O3 a* \
  140.   (erase-buffer)
    + P) I$ f* L$ P+ K* p( Y
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))3 j& R7 z  T+ M6 v4 j5 k7 U
  142.   (dotimes (row size)
    & K7 H0 n0 z# P- a: \; ?/ Y$ {) f
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    ! i7 K2 n& c+ b  q

  144. $ m2 h' h1 d! _/ v9 b
  145. (defun board-contains-p (y x)9 G3 n% q  w3 u8 q/ S9 G
  146.   (let ((size (session "size")))
    1 V8 \9 C9 X$ r7 B
  147.     (and (<= 1 y) (<= y size)
    6 \  V1 t' D7 {+ \1 d  D/ w
  148.          (<= 1 x) (<= x size))))
    0 {$ ^% q: N6 ]  E

  149. * z; L+ I. n) v4 |/ s! [$ R( m
  150. (defun board-toggle (y x)
    7 q; Y* P. ?7 T% E- S" V
  151.   (when (board-contains-p y x)
    ) G+ `, j+ r& o7 O2 [% z' p
  152.     (goto-line (1+ y)), h% w1 f8 @- u) P- v: x
  153.     (beginning-of-line)& k. o/ k- G; k& ?4 \; e
  154.     (forward-char x)- \% B# {) M; w4 @  d( Y
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    1 I4 n6 U" t5 ~& N/ ^7 w
  156.                 *wechat-5x5-black-chess*- b$ F3 m' Q7 T2 R
  157.               *wechat-5x5-white-chess*))
    2 f& i" p. ^0 s/ N/ I
  158.     (delete-char 1)))  {5 j3 a( C1 a  O% m  U( O/ r

  159. 0 R& w0 G6 x/ q) m
  160. (defun board-put (y x)
    0 _0 h, J" [( L
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))2 u6 ~  E5 N, M3 ]
  162.     (board-toggle (+ y (first dir))
    . p( g5 z4 Q3 x% V& W9 o7 Q5 w$ ?
  163.                   (+ x (second dir)))))
    / w4 e9 b$ L* T2 O  q
  164. : ~: b& e  b( i  N5 S
  165. (defun game-over-p ()
    / B4 F' }8 L4 i* {5 T
  166.   (beginning-of-buffer)
    8 S, {- T( J, x, k# Q: p4 D* h
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    7 S+ p8 b; O: I# u! K
  168. ! ?2 q( u; v" J# F2 |5 `! C6 N
  169. (defun board-show ()
    & ^* Y* m8 a8 \
  170.   (with-board% d5 ^+ w: G! e( z  L' I
  171.    (concat (buffer-string)
    3 M* K$ Y) K' f7 @+ _% D
  172.            (if (game-over-p)
    3 o# L: G( Z; I5 \9 k4 S
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))# h. U2 I; J# I- X
  174.             (format "第%d步" (1+ (session "step")))))))
    % i3 U2 o3 \9 l* K2 u+ P6 T6 Q

  175. 5 K5 N# `4 l9 k! }! ~, A
  176. (defun board-position-parse (cmd)3 O3 \" `9 H/ q2 q! A) g  E8 _
  177.   (if (= (length cmd) 2)
    2 B+ ?# C  n, M/ m) K( C
  178.       (list (string-to-int (substring cmd 0 1))0 o# M' C! I% Q5 G
  179.             (string-to-int (substring cmd 1 2)))' r2 u: d( b1 t' T; h+ v8 u3 y
  180.     '(0 0)))
    9 J1 ~, A/ L) D9 C
  181. # K1 M/ \( I/ M0 {/ Q. |4 g
  182. ;;; 游戏房间  M# F6 A- m9 h  ], T9 c
  183. (defun game-room-init (cmd)& U/ U2 F, U: _& E2 Q# B" q
  184.   (let* ((middle (string-to-int cmd))& |; @, ]5 {( \) d0 A, L
  185.          (size (1- (* 2 middle))))
    6 l3 o# q0 N, T; T3 ?
  186.     (with-board! F- o! O2 l2 Q& t; D) e) _( b
  187.      (board-init size); f0 Y  J/ v! x9 D/ M" ?$ A# N
  188.      (board-put middle middle)))
    + S4 h6 i9 e9 w3 B- ~. ?/ i" e0 \
  189.   'game-room)
    7 |& U7 m1 {- g0 g. n
  190. & T/ [% D( ?/ v
  191. (def-room game-room& l) X! n+ B4 Q
  192.   #'board-show0 u% w# \/ i1 z& U8 Y! z3 u
  193.   (t (lambda (cmd): @1 I7 _% s: J# k
  194.          (with-board
    : X/ N7 K' y9 q6 ]
  195.           (if (game-over-p)
    & U' z) W, h& W$ ~* y; [' n  w+ l
  196.               'living-room
    " ?  a) z! L1 e
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    * ]# ]8 z6 l  ^9 Q
  198.               (when (board-contains-p y x)
    % F8 G. A% o; d' Y
  199.                 (board-toggle y x)( w9 p3 s, ~. j* \6 f
  200.                 (session "step" (1+ (session "step"))))
    3 i2 C% g8 V2 |0 L) r3 @1 Q
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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