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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
" u/ ^, h& y2 l& y4 T! Z4 ^& O借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. 4 B1 c0 n! ^" x7 e
  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;">;; 定义新的游戏地图# e3 c1 n: U( d4 Y% W/ |) R" J6 G
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL& Z$ t$ i5 ?  v- s& G
  4.   'tutorial-room-0)                     ; 默认的入口
    4 K9 E$ u- Q: P" z
  5. ! X7 N& s3 \. m+ a( R
  6. ;;; 游戏大厅" `2 L8 ]1 R1 b4 W
  7. (def-room living-room7 R: s3 z" @% R; y0 Y) x
  8.   ;; 进入该房间后的提示语3 X: k  v) I7 b) l3 y% v! |1 v
  9.   "1. 教程
    # k- @" V, m5 Y, T8 B: i$ s) k; t% _
  10. 2. 入门(3x3)
    & ?3 {4 L/ c0 [5 J; Z" M' J# r
  11. 3. 初级(5x5)# c2 A! L: _3 C8 ]
  12. 4. 中级(7x7)% m9 X# C3 O; ~' Y0 m! g
  13. 5. 高级(9x9)
    ; c! T" K: X' L! w
  14. 0. 关于作者
    , O  w1 z2 Y; _+ D) W
  15. 请选择1-5开始新游戏:"
    & k4 J* k3 L0 y( |& t
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名( l8 J0 f0 @6 `$ O4 q( Y
  17.   ("1" tutorial-room-0)! W- x2 m8 Y2 l2 g. ?# h$ R
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    $ N7 {. ]! T2 ~1 D
  19.                                         ; 相应的返回也可以为函数,动态返回房间名2 q1 y; s1 n5 R' C1 @; |
  20.   (t living-room))                      ; 如果条件为t,为永真
    * F# j+ w6 B4 o) ~5 I5 G

  21. : `" G, S- z+ V' h( S/ w
  22. ;;; 作者信息- @, L  T1 d; W0 _
  23. (def-room about-room
    ! y2 x/ K2 t8 K2 F. _+ T
  24.   "作者:redraiment0 S: S1 R8 d9 y; S6 J
  25. 微博:http://weibo.com/redraiment
    8 v- h$ h1 K9 i- J2 V1 L+ |) I/ Y& O
  26. 有任何建议,欢迎在微博或微信上联系redraiment。) k6 k* g) J0 E2 o9 f! @
  27. 请输入任意数字返回游戏大厅。"
    9 R4 T6 p: ^6 U3 H
  28.   (t living-room))
    - ~2 \; f) D) |# Q" G
  29. 7 S# F5 w: Y( O" Y6 g! e- P) S
  30. ;;; 教程
    " W% Q' Z# b" a% i
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    0 _6 _! c3 D+ i
  32.   "The number of tutorial rooms")1 @) I( @* K& w, t
  33. # e, e# V# K- z( r9 N' {
  34. ;;; 简化教程的定义
    6 q$ D6 W% x8 [# H. ]
  35. (defun string-last-line (content)
    6 j+ {- B% i; o. O; c
  36.   "多行内容组成的字符串中的最后一行"9 H7 E  R! b/ X: w' H, e: F
  37.   (with-temp-buffer
    0 Q3 I$ N  \  y3 g
  38.     (insert content)9 J0 }) K/ r6 G  X9 G
  39.     (buffer-substring (line-beginning-position)4 N7 Z3 @, q9 n( j7 @
  40.                       (point-max))))" ~8 v  }; a2 F& y& [8 y

  41. $ @: j. F% {/ ~5 g
  42. (defun def-tutorial-room (prompt)* T2 k; i% S4 E# U' }
  43.   "根据提示语自动生成教程房间。& M! E9 c+ ?) ]) ]+ j4 d6 |

  44. + t6 E( Y, C( V% H% c( \$ S3 Z
  45. 1. 提取最后一行作为问题;
    & y& s9 }& m- w& D) m4 ?) e6 j2 U; w
  46. 2. 分析问题,获取期望用户输入的内容;
    ' v0 p1 ]) r/ g! Q4 D# r
  47. 3. 定义教程房间和重复提问房间。"
      E. _$ h. s' a/ D4 a
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    0 x9 u% ~, Z) K( u/ i
  49.          (repeat-room (concat room-name "-repeat"))
    , ^/ H" e) ~  u; ~$ t* E. a
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))# H  L- x2 @" D- P$ W3 \
  51.          (question (string-last-line prompt))+ u0 |. }; B5 w  V$ B5 {
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    & M  A. t- Y: A& O9 h* u
  53.                      (match-string 1 question)))
    & s$ G+ M2 r1 p6 h
  54.          (doors (if except$ i* e& Q7 @2 l5 B
  55.                     `((,except ,(intern next-room))
    5 E, U& A& v& m" k
  56.                       ("q" living-room): S7 x5 }# ]$ m- C: f) V3 V
  57.                       ("Q" living-room)  N8 h  o" b$ l1 W9 i9 @
  58.                       (t ,(intern repeat-room)))7 ~: p$ W1 P" p8 b
  59.                   '((t living-room)))))
      q" J, b8 q, ]2 G0 N$ ]
  60.     (def-room-raw (intern room-name) prompt doors)9 Z0 }5 Q" j- _
  61.     (def-room-raw (intern repeat-room) question doors)))
    ! g* S/ g- w. h; C8 H9 f
  62. 1 F! a9 y7 n- k) [9 b9 K
  63. (defun def-tutorial (&rest prompts)0 _0 Y" i# }7 t" a. ?0 i
  64.   "批量生成教程房间。"
    1 M3 W# C$ ^' N2 D
  65.   (dolist (prompt prompts)
    0 C1 k1 S. [1 W$ M
  66.     (def-tutorial-room prompt)))) [$ e: t$ H$ j9 R3 z
  67. ( E" @% f& l8 C% ^
  68. (def-tutorial
      e  Z) H, J! x
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    $ f% O# m7 e9 z
  70. 1. 教程* }% S, I6 Z7 D4 F
  71. 2. 入门(3x3). ^# S7 h  S" d7 |+ J* v
  72. 3. 初级(5x5)
    - F, _/ F3 o# t( C$ A
  73. 4. 中级(7x7)! J! j; @1 a: N
  74. 5. 高级(9x9)3 j( w7 Z' B$ Q
  75. 0. 关于作者7 F+ _: }; d- `' m( A+ X' N7 J7 y# x
  76. 请选择1-5开始新游戏:+ X9 Z- Q( ]! v2 a: k* r
  77. 您现在正在游戏大厅里。6 `2 p2 W% S. u9 K$ I: n, o
  78. 请输入“2”进入入门级房间"! k7 u6 k' x& H; i# p' ]2 g9 y7 S
  79.   "  ①②③
    # R, \; F8 h; ]& k4 T) e
  80. 1 ■ 
    8 ?- g$ W* E. D( d$ O
  81. 2■■■
    / M5 G: L! a+ x% n; }
  82. 3 ■ . S: g2 b  a3 @+ G9 V
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!  i6 D3 w9 n0 M# i% ]
  84. 请输入“22”来关闭第2行第2列的窗户。"
    6 t% {0 {) O( I- O( o# V, h8 E* G) G
  85.   "  ①②③
    ( [1 ~/ Y! ?# }# L) U
  86. 1   
    9 _3 C$ Q9 \/ x, W0 i
  87. 2   
    4 w$ _  Z  c* d
  88. 3   
    " w7 E" y" ^/ I7 ^4 h* o
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。3 j' Y( f! d  }
  90. 请输入“11”,试着开启左上角的窗户。", U* w" @" H. W" c$ I6 m
  91.   "  ①②③- z4 n4 M( V# N- o1 w
  92. 1■■ ' f9 A% [! h) e
  93. 2■  
    ( G- f& y8 E3 ~* n
  94. 3   
    $ d4 s* S2 v$ o" Q; n  |
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。" i8 }' ?0 r* h9 h( m, ^& m: `) u% c) E
  96. 请输入“13”开启右上角的窗户。"
    1 X9 n# v  \* @9 u7 ^) p5 R4 }
  97.   "  ①②③( S3 L1 E" V  K1 c3 F& |, O0 r
  98. 1■ ■
    4 N9 R' }4 X6 M
  99. 2■ ■+ j, J5 H3 o& ~* @
  100. 3   / a$ q7 Z! V, x0 C: z  j' E3 S
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    % C" A# y2 O$ O3 W4 ~  q
  102. 请输入“31”开启左下角的窗户。"6 O; r- X  F$ L* |
  103.   "  ①②③5 X2 D9 [# O8 g$ |. m. X
  104. 1■ ■) ?$ q% C9 i* Q: |, o5 ~
  105. 2  ■
    - H, k, f; Z9 I+ s6 J$ U! `
  106. 3■■ 6 j: O( A. r& f3 X
  107. 此时,总共有5扇窗户被开启了。
    2 N5 f1 w  E' k: n" u
  108. 请输入“33”开启右下角的窗户。"5 `& L, a1 }( k! V' M) x2 E+ C0 u' o
  109.   "  ①②③
    & M9 W( W; `1 H6 d. O
  110. 1■ ■
    4 m( O. a# L0 v0 F$ ?
  111. 2   ' L+ H2 {' t. `! F6 ?
  112. 3■ ■
    # L+ J$ F  _+ K) b7 X7 i
  113. 现在,只有四个角落的窗户被打开。6 ^* a1 d2 u4 X  D' M( Q5 s: {$ I
  114. 请输入“22”完成最后一击!"+ g. p) v2 d3 u( t$ N
  115.   "  ①②③2 h  W* U2 ~- G, g+ g
  116. 1■■■9 ]0 C% {8 `6 ]( K
  117. 2■■■
    + o3 |; I5 {6 B9 F$ H
  118. 3■■■- ]7 P' _/ B! R& \3 M1 B
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")8 |# ?/ z( x7 y1 X1 w# v
  120. 1 I4 A  \3 \' u& e; G& a4 {) g1 ]
  121. ;;; 棋盘. q( `; J2 U8 k2 E/ [2 B
  122. (defconst *wechat-5x5-white-chess* 12288
    7 Y% y; @% B7 m1 S
  123.   " ")
    . i. Z6 b9 T' G! A
  124. : Y1 {. Q# D% v3 p* g) r* O# Y7 _
  125. (defconst *wechat-5x5-black-chess* 9632
    2 j, v6 i- Z  v& g# A
  126.   "■")# d) _8 S# y3 M# n8 R1 ^3 a
  127. . a% B. I% Y1 B3 \% l& k
  128. (defmacro with-board (&rest body)# J7 N; x  d# c& F
  129.   `(with-temp-buffer
      b' D6 p0 j3 f: x7 ~
  130.      (unwind-protect
    4 b0 `  Z6 X  d2 t, |: I% X
  131.          (progn
    5 J% A* L# p% f/ `, D% e4 P
  132.            (if (session "board")$ R5 y2 G& S- j5 {7 U
  133.                (insert (session "board")))
    $ _) ]* ^% `) z9 h
  134.            ,@body)$ {: u8 M& Z' _
  135.        (session "board" (buffer-string))))): ~% I- x$ j# _/ I9 ^

  136. 5 X# j! @) R8 C! `* N
  137. (defun board-init (size), L( Y. W" x9 h
  138.   (session "size" size)+ T7 @1 G$ X( z! K1 h7 I! Y
  139.   (session "step" 0)4 h, z0 t3 V5 G  D: _* x2 N
  140.   (erase-buffer)
    ; H4 `5 o  L1 J; B. V" h  a
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))7 U  z) U% g% ~) H! g
  142.   (dotimes (row size)7 Y* u: W/ h0 K; _* k$ a# z
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    7 ~# t1 Q! s' ]; K; h1 W- g

  144. 6 M5 i( I7 }" p( C$ ]
  145. (defun board-contains-p (y x)4 j: r5 [" N$ ]/ r1 ]; d0 R0 s
  146.   (let ((size (session "size")))
    ; p# L% n$ G+ I7 N9 N% w
  147.     (and (<= 1 y) (<= y size)
    ) ?* e; Z/ W( {' P4 k4 ~( ~
  148.          (<= 1 x) (<= x size))))
    ; h4 M: K2 Z5 P9 ]

  149. 5 @+ B  F+ i/ E# A3 Q
  150. (defun board-toggle (y x)
    7 x) r5 H/ C' z  s$ H
  151.   (when (board-contains-p y x)
    7 X7 K+ b# r* b* ~. ]8 w
  152.     (goto-line (1+ y))
    $ M4 g3 i$ x2 M! Q- I% {
  153.     (beginning-of-line)
      Q( V3 _( ^# p% Q) k; d8 S/ f
  154.     (forward-char x)
    , N; s  H: Y; ]
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    3 |6 w1 J( R7 M0 k7 Y
  156.                 *wechat-5x5-black-chess*
    ! N; N/ _4 c, K) M0 G- y6 g( i
  157.               *wechat-5x5-white-chess*))  _3 A! I  k1 Q/ e6 a
  158.     (delete-char 1)))7 N2 N" p. w  y# @' l) E, Y

  159. 7 Z$ _/ t6 Z6 t( [: ~
  160. (defun board-put (y x)
    4 O7 P" V$ _" |0 l8 h
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    6 v- w, v5 b2 M3 r
  162.     (board-toggle (+ y (first dir))
    2 P+ s# S- v$ S5 h3 u
  163.                   (+ x (second dir)))))
    & Z2 r1 i8 ~7 D$ l' K

  164. : A6 t+ B& g: \, |
  165. (defun game-over-p ()7 i3 E5 ^4 }: a7 c
  166.   (beginning-of-buffer)
    + ]1 k+ |* `/ C
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    " W. n0 k- v8 c  l8 M. |  a+ g

  168. , A5 Q6 p" O; u7 K
  169. (defun board-show ()) o- Y. [) C# ^0 Z
  170.   (with-board
    ' c) r: i" [2 P+ b0 }
  171.    (concat (buffer-string)9 {, g2 i4 X) j. Y& P, L
  172.            (if (game-over-p)
    4 F8 n, H- r& a" M
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))0 A8 W6 I7 g+ ^' h! D
  174.             (format "第%d步" (1+ (session "step")))))))  L& @  p$ y' w! m, L/ Z5 ?
  175. ! b2 i1 N- Q3 _& g' h/ _6 u7 w
  176. (defun board-position-parse (cmd)
    2 b3 n0 [) v, K8 {5 l
  177.   (if (= (length cmd) 2)
    4 x7 O; @2 I: ?) \2 ~% M- q: e( l& E
  178.       (list (string-to-int (substring cmd 0 1))+ V6 a4 H7 d2 j8 n
  179.             (string-to-int (substring cmd 1 2)))
    - P6 w9 J( y8 m1 _( p# ^
  180.     '(0 0)))" ?9 @1 u% \) I8 Y" m6 Q

  181. 0 h: C( V' Z* b: r! ~
  182. ;;; 游戏房间
    " ]& O+ c; |6 B8 q
  183. (defun game-room-init (cmd)3 d+ N9 n. d  J8 m! s
  184.   (let* ((middle (string-to-int cmd))
    . ?' B7 p$ b$ I* b8 r
  185.          (size (1- (* 2 middle))))
    ! r& `1 u' n4 `
  186.     (with-board" S6 t3 b- C( e# x, B
  187.      (board-init size)! v9 y& V% X# ~6 f9 [$ ], m
  188.      (board-put middle middle)))' ?) S4 U7 D4 f/ h$ S# c
  189.   'game-room)
    " F2 h# Q& _' [/ E; y2 H2 S

  190. 6 E, i3 \- E8 l2 j% t
  191. (def-room game-room( _! n& u) N" _
  192.   #'board-show
    % W" J# q' L* N9 g
  193.   (t (lambda (cmd)
    ) A- Q# O+ m, A9 l7 r( d
  194.          (with-board* Q6 i0 W3 N0 x0 H
  195.           (if (game-over-p)
    ) o; L4 b1 C% E
  196.               'living-room- F5 X( G6 g# d3 q5 `; f. K, r
  197.             (destructuring-bind (y x) (board-position-parse cmd)4 B4 T: a: _7 c% f% i
  198.               (when (board-contains-p y x)
    4 U/ ?1 p, w8 W8 k
  199.                 (board-toggle y x). k# P% v+ K6 s- P
  200.                 (session "step" (1+ (session "step"))))
    : ~1 S7 Z2 }# }; _
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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