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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。; [- s$ e2 z3 n
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. ! N) b* m7 X1 h# W3 P* I
  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 j2 N( o- o4 v& A
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL' o9 ]6 ?( Q- ^8 m: s
  4.   'tutorial-room-0)                     ; 默认的入口
    & O" M3 Q( j# U& T& V3 o

  5. : B! `4 X7 o2 O$ _% @' k& {
  6. ;;; 游戏大厅+ U0 c/ G: T+ J( n9 ?! @9 B
  7. (def-room living-room
    7 L5 d" z4 v. Y4 _- d
  8.   ;; 进入该房间后的提示语
    / X. R. z' Z" H6 U
  9.   "1. 教程0 l! O, N% e" y/ P' W& ~( v9 O
  10. 2. 入门(3x3)
    7 Y) }4 ~- P3 B8 c0 B2 o
  11. 3. 初级(5x5); @% V0 E& ^- ~
  12. 4. 中级(7x7)- @: y" L& W, a0 e7 }) B5 N
  13. 5. 高级(9x9)
    # X" L# Z7 I( C/ q6 r* j  B4 T! p7 T
  14. 0. 关于作者' S4 t4 ~5 Q5 Q  M5 W
  15. 请选择1-5开始新游戏:"
    1 z  M+ c' H: C8 v
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名7 n$ N4 ~. n% |9 u* O
  17.   ("1" tutorial-room-0)
    1 G! B5 y: _* r- G& r
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配$ d" k8 [1 I& Z5 ?0 E
  19.                                         ; 相应的返回也可以为函数,动态返回房间名, N4 C& H6 Z2 [1 j; U( U, d6 l
  20.   (t living-room))                      ; 如果条件为t,为永真3 k8 ]' _9 S, ?! k# g) t* H, I4 K

  21. 4 p( Z6 z+ {7 b* R/ c8 k9 f
  22. ;;; 作者信息
    $ R. x# S& Z7 l: I, b- `
  23. (def-room about-room9 W* }" |/ a6 i
  24.   "作者:redraiment0 Q$ `0 \) `. h) Y, x- t. m; F
  25. 微博:http://weibo.com/redraiment5 v' U* Y0 }: r/ N& T
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    & p. Z  n# t) `) L
  27. 请输入任意数字返回游戏大厅。"$ e! Q0 b: D% Y. v9 q- E
  28.   (t living-room))1 v1 ?, I5 F0 d/ l2 [" w- O

  29. * C2 D; m- ]1 Z0 L. v3 I
  30. ;;; 教程. O+ D+ ]7 E5 ^+ D: D9 t1 x
  31. (defvar *wechat-5x5-tutorial-rooms* 0  j& m( }( k0 \& T, W$ t& j
  32.   "The number of tutorial rooms")1 U8 K0 y, p6 c2 h, \! z+ i

  33. $ o. U. u" k2 a9 y9 |% s6 U! d  J
  34. ;;; 简化教程的定义
    - m/ v  Z- j3 P# {
  35. (defun string-last-line (content)8 \* r* i4 [% E" X. C. d% ?
  36.   "多行内容组成的字符串中的最后一行"
    / G9 z' _' Z/ E( _3 j9 X2 E1 T# e
  37.   (with-temp-buffer
    3 b4 ~, ?, N# ]) T) r2 f
  38.     (insert content)
    ! s5 O# M7 A/ G% z7 W
  39.     (buffer-substring (line-beginning-position)
    2 {# I# h1 l# V2 m6 Y
  40.                       (point-max))))
    9 w. V% X' h) R( M) p0 \

  41. ! v) q. W& ?; V: \: Y
  42. (defun def-tutorial-room (prompt)+ D8 k6 z. j' `8 B0 l  _- m
  43.   "根据提示语自动生成教程房间。  }+ u9 s. G4 T! T' P

  44. , B/ Q8 P6 v) s
  45. 1. 提取最后一行作为问题;
    7 R) v/ |' c" Y& d& S9 B  w/ {" f( k) `
  46. 2. 分析问题,获取期望用户输入的内容;
    & g- h" U# S" _2 @
  47. 3. 定义教程房间和重复提问房间。"+ M2 O  n6 v! r
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    ( d# U7 H& d' A! {
  49.          (repeat-room (concat room-name "-repeat"))( W3 W; ~# F0 P# W
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))) s5 X, L+ V' b7 S
  51.          (question (string-last-line prompt))
    2 c1 {/ W7 r* q: \& l
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    3 f8 I1 y2 @$ _
  53.                      (match-string 1 question))), z* i) p' R0 }! o" i% y
  54.          (doors (if except
    1 r2 m# G, u, _
  55.                     `((,except ,(intern next-room))
    6 B2 Z+ Z' r' G
  56.                       ("q" living-room); r$ N5 I) E( d5 J4 N% w, I+ Q7 n
  57.                       ("Q" living-room)
    : k3 l9 M% l) Z( K% `; g
  58.                       (t ,(intern repeat-room)))6 u, T. }) f9 C
  59.                   '((t living-room))))), t' w' s! P* y" @& y
  60.     (def-room-raw (intern room-name) prompt doors)
      i) Z" S, Q4 A1 z' i' H! d, ^
  61.     (def-room-raw (intern repeat-room) question doors)))% c7 C* y' N" k' t8 D* x

  62. & X* z9 J; H$ j
  63. (defun def-tutorial (&rest prompts): ^9 [9 J+ v2 f: x6 l" S  z
  64.   "批量生成教程房间。"% L8 {6 Q' J, q# p1 q; y* b# Q
  65.   (dolist (prompt prompts)* C, @" i4 |* q# b: }' h1 [. ^  l3 t
  66.     (def-tutorial-room prompt)))$ i/ ^: h7 {, D: m: j$ ^2 a

  67. 3 m4 c7 r' t8 S6 n
  68. (def-tutorial+ p" [* r, g! |8 W, @
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。7 V- F9 U* H4 t8 b
  70. 1. 教程
    - H8 h8 J3 z0 k
  71. 2. 入门(3x3)* y( T7 Q4 G1 ~6 s3 _1 Z' V; p$ G
  72. 3. 初级(5x5)$ F! c* L4 P; W, @: U
  73. 4. 中级(7x7); x# p: h5 P% w1 ]- y6 c
  74. 5. 高级(9x9)9 u1 y/ _/ S" E" j# f3 x' F
  75. 0. 关于作者' P# r( d0 s/ _# \' R, D8 v0 c
  76. 请选择1-5开始新游戏:
    % t$ A  ^2 v) p- o8 L' W
  77. 您现在正在游戏大厅里。+ i) }* S/ L! r" q4 u
  78. 请输入“2”进入入门级房间") V; I! B$ I2 D  a& U# C4 X0 `
  79.   "  ①②③; `& E/ f0 e8 U1 d$ J4 m' [9 L! d
  80. 1 ■ - U/ M. v8 w# Z
  81. 2■■■2 @3 R7 \8 V% g  |5 E, @
  82. 3 ■ , J4 A3 a0 j" E; y( Y# Y* W# [
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    2 r- E, H& x  ?8 P1 r6 L8 X9 X& `& P
  84. 请输入“22”来关闭第2行第2列的窗户。"
    4 n# p1 q4 t4 q. |1 H7 S
  85.   "  ①②③
    5 H  [& R3 r. h' O
  86. 1   4 M( F8 W! i- P- ?9 h6 I( _& n
  87. 2   
    6 H+ O( H* r/ x4 M
  88. 3   ; b6 P  {  l0 s, ~
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    ; d' r8 {3 c' s
  90. 请输入“11”,试着开启左上角的窗户。"
    # t) S7 c0 d5 ]
  91.   "  ①②③0 j: _5 o6 [7 Z8 \+ @
  92. 1■■ 
    ! O* h) r- K0 ]3 M% J+ M
  93. 2■  
    $ y- @/ @' n7 d- B
  94. 3   1 A# |/ _% y' O+ A# U  J/ T% c
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。$ j! U* Z( Q( e5 T+ \& m
  96. 请输入“13”开启右上角的窗户。"; }$ ?, u" z5 Q# O
  97.   "  ①②③
    ! _5 {; J/ g- W2 [0 r  k* J) _- [
  98. 1■ ■
    ; Y9 h/ t7 w2 V( b1 H$ D
  99. 2■ ■; p8 ~7 U/ F  s# E- V
  100. 3   + T, w0 l; f' b
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。6 P5 V: e, N3 |7 _  X- l
  102. 请输入“31”开启左下角的窗户。"
    * p0 R! \' l# Y6 J/ V; ~1 e
  103.   "  ①②③
    0 X; j$ G+ i& H6 f, \8 [
  104. 1■ ■
    * I0 u  l# [; k- h! n: H) c
  105. 2  ■
    4 Z, [9 d! n6 z# E
  106. 3■■ 
    + d2 h  x5 `# H1 c; A1 L8 l4 L% s2 N" l
  107. 此时,总共有5扇窗户被开启了。- ]+ F2 v* \8 H4 G6 N
  108. 请输入“33”开启右下角的窗户。") ~5 Q8 i$ }6 ^- P- E( Y  k& J* Q
  109.   "  ①②③$ s1 {( g; @, R  C
  110. 1■ ■' U5 F' r; u( f
  111. 2   
    * s2 ?# ?/ x# _
  112. 3■ ■" m& j. V1 y3 a" z* _
  113. 现在,只有四个角落的窗户被打开。
    5 [$ N5 k1 S8 A+ U
  114. 请输入“22”完成最后一击!"! n7 p6 o, _0 v
  115.   "  ①②③- h7 A. g! O5 m$ l7 _6 B$ _! o
  116. 1■■■! I: ]; a- d: H$ c/ P' l2 E( B; L! p
  117. 2■■■
    - [7 B* @( F6 s* f( v! L
  118. 3■■■
    5 r7 T! p- Z' k! K: V
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    " P$ i; p- b3 x2 ~% q0 y8 h

  120. 2 r# D: A0 H+ Q9 Q1 v) E" P& M
  121. ;;; 棋盘6 v% [! s# D' O* ]5 u- l/ \" D
  122. (defconst *wechat-5x5-white-chess* 12288$ }' D& ?+ U2 D& Q
  123.   " ")
    , h2 n% I( Q/ G; D% }/ M$ _
  124. : N/ f( ^2 j! U$ c' k
  125. (defconst *wechat-5x5-black-chess* 9632
    4 x1 U0 z& b* J4 N" a6 F2 u* {
  126.   "■")
    7 v& p; z9 P8 b% A8 D" M' d9 O6 {

  127. 6 j1 w( q0 `  F# F) s- x1 f  E
  128. (defmacro with-board (&rest body)
    ! J6 q% A' a2 E; X
  129.   `(with-temp-buffer
    ) [) U7 c  q7 v6 x0 [% t1 J
  130.      (unwind-protect6 H! o3 @, U: D! |6 _$ _9 g# |
  131.          (progn1 R' k$ s, j5 c( v" q% o
  132.            (if (session "board")1 ]" H4 k" s- l
  133.                (insert (session "board")))3 s* Z/ O" a* H( k, A5 Y& c7 z
  134.            ,@body)1 Q- D7 r1 M( K8 C1 S9 I3 p* y
  135.        (session "board" (buffer-string)))))
    , Z2 F6 F/ n( n' Y
  136. : m6 p% S! i6 |$ H1 y9 K3 U
  137. (defun board-init (size)
    ' Z# ^2 j9 d: `4 A5 N
  138.   (session "size" size)
    $ H# x( M( w2 E/ h
  139.   (session "step" 0)$ G  i/ P& T2 X1 a7 Z
  140.   (erase-buffer)
    & h: t! U) I3 @1 H/ p- f# S3 K
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨")); b- d* I" ?: e: U1 h
  142.   (dotimes (row size)
    7 f( V0 x6 |2 j% Q
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*))))), V' D0 o5 V# F) Q& B0 G
  144. 9 I) N0 W$ k' l- v' r  B# B
  145. (defun board-contains-p (y x)( ^0 |- L# _  M! x$ j
  146.   (let ((size (session "size")))
    # [) W) {/ J6 ^8 Z) B
  147.     (and (<= 1 y) (<= y size), s* L- H- g+ ~
  148.          (<= 1 x) (<= x size))))
    ! W' w/ Z5 z( d% c* M' ^, J" r# G
  149. ; B* O% }+ t: t; [0 [
  150. (defun board-toggle (y x)
    : V& a! D) T6 j: h
  151.   (when (board-contains-p y x)1 G( C! b) J; n& K$ e1 u
  152.     (goto-line (1+ y))
    ' k, X4 ^/ q: J+ h* Y% q
  153.     (beginning-of-line)# D. M7 k- j) r  {4 T( R$ O9 Q  O
  154.     (forward-char x)
    * I4 D5 X7 o, |& g2 w9 j
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))9 n# i5 A, Q) K$ W
  156.                 *wechat-5x5-black-chess*" ]! O) {' e, Z' Z/ v! H' L
  157.               *wechat-5x5-white-chess*))
    ( e) L9 Y& F, \! \$ z- E  o
  158.     (delete-char 1)))
      ?+ G3 b# w: E+ D4 B0 M

  159. 9 {6 g' T0 F1 j5 Z+ ~$ d
  160. (defun board-put (y x)
    * U" j. F3 E7 C. T
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))- m) @" L) M' [
  162.     (board-toggle (+ y (first dir)): n  f4 A) P" C' i
  163.                   (+ x (second dir)))))8 }! h2 M4 u( M& Q: u. L9 a

  164. & U0 [) s# F( i& M
  165. (defun game-over-p ()
    " u5 r1 r0 L5 K: |: b. Q* a" I
  166.   (beginning-of-buffer)
    $ T0 u$ d) V) C' h3 X+ N/ s
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))& [. b0 [" [. n; C% d+ q1 |

  168. 2 u  n3 v4 R% P3 k; B  f) z/ J
  169. (defun board-show ()4 s: C" S$ v( q; r. g$ \, S
  170.   (with-board8 t% d( f: d% r+ P! W
  171.    (concat (buffer-string)
    7 _7 s7 o6 o1 G/ r' z
  172.            (if (game-over-p)
    ' }; l# `: C, E3 \, f
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))+ o0 ^; s% x3 k) [/ P. Y- W
  174.             (format "第%d步" (1+ (session "step")))))))  o/ j  `  B/ T5 E+ [4 M
  175. % c# {% H0 g& K! Y
  176. (defun board-position-parse (cmd)0 z) N. U1 Y& e9 q" m
  177.   (if (= (length cmd) 2)! A& m2 i  F6 M" C7 o% ?  z3 z
  178.       (list (string-to-int (substring cmd 0 1))
    2 ^6 Z6 M, W4 x. B, \
  179.             (string-to-int (substring cmd 1 2)))0 c( H: x5 ]* z2 X4 w! D3 _, I
  180.     '(0 0)))  g* `, U" Q8 j' ]# Z- R  W
  181. , z0 \) A* J, p+ C6 t, O9 U: j
  182. ;;; 游戏房间
    7 \# d% a4 ^: _
  183. (defun game-room-init (cmd)
    * V, m1 M  j, [6 N
  184.   (let* ((middle (string-to-int cmd))
    + `7 B2 c  o2 C2 d
  185.          (size (1- (* 2 middle))))
    1 n3 M( Y. z& @1 h
  186.     (with-board
    1 w3 R" M8 |2 S9 R: P6 j# W9 s3 W
  187.      (board-init size)9 O5 F2 L  Q. F/ J" g
  188.      (board-put middle middle)))
      l+ Z' q2 ?4 X3 ^5 o/ i
  189.   'game-room)* H4 m) b; K, c% a, W+ K
  190. , n' d. E; I1 Q, p/ \& s$ e
  191. (def-room game-room$ Z9 S' R. \8 W  H
  192.   #'board-show  g! ]- V* ^# {$ z/ c
  193.   (t (lambda (cmd)/ c* A# i4 y# i% a
  194.          (with-board
    7 J0 D4 T- e' D
  195.           (if (game-over-p)* @$ t4 t( ]1 o' C! o5 d
  196.               'living-room
    4 U( e* s0 @- n7 E. y3 g: q
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    $ {: i0 U6 h8 J1 |
  198.               (when (board-contains-p y x)5 U* l7 T9 s- W6 R; ~+ ?6 M' w
  199.                 (board-toggle y x)4 s" o% W9 l# q6 D, T3 D& B$ n' k
  200.                 (session "step" (1+ (session "step"))))9 d2 v9 @9 Z+ a# T2 U
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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