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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
# C3 U# m* y" v1 A$ g0 y借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1.   X* B0 }/ g+ h7 z
  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;">;; 定义新的游戏地图
    " F: M- o. ]* U0 r, M1 ^9 t
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL. a# X9 y+ ]) p0 f6 x( n5 [
  4.   'tutorial-room-0)                     ; 默认的入口1 D- ?6 p) K/ u- G0 D% ~  e/ e
  5. , f- u5 m# b. `. d3 k; V
  6. ;;; 游戏大厅
      g* o, q( ]0 a4 z% ?# A
  7. (def-room living-room
    ) c) K! Q7 O1 y% Z
  8.   ;; 进入该房间后的提示语1 X; w7 n. y6 E% M9 e5 f
  9.   "1. 教程) q, b# q3 q6 M' T4 F
  10. 2. 入门(3x3)
    4 w4 H& D: r& d
  11. 3. 初级(5x5)0 O' u( h  B2 N4 Y1 s
  12. 4. 中级(7x7)5 q% r& E! G6 t# i7 H: i
  13. 5. 高级(9x9)
    ( H, x4 t. @. _' o( u& y. g& \
  14. 0. 关于作者6 n& c  c6 ?" `, `" o
  15. 请选择1-5开始新游戏:"9 k  a$ W/ M; o3 a0 L2 `
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
      X5 \! v3 A: }2 W
  17.   ("1" tutorial-room-0)
    ) F6 e, ^$ f3 F' `6 l7 e; _5 o
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    2 V) e0 l$ F2 I2 T9 {
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    4 K( |. ^% N; v- b
  20.   (t living-room))                      ; 如果条件为t,为永真7 v  r' i8 x- ^' S3 u# K

  21. - X$ [% \- p5 `' W4 s
  22. ;;; 作者信息
    " Z! u$ ?3 P4 P1 @
  23. (def-room about-room
    ( m- E) F) j! T5 n
  24.   "作者:redraiment
    2 H) d7 W, H" x1 X4 _  ^9 }9 L
  25. 微博:http://weibo.com/redraiment  h1 }8 Y/ M8 C6 {# a- c
  26. 有任何建议,欢迎在微博或微信上联系redraiment。1 u/ ]% x: Q4 h
  27. 请输入任意数字返回游戏大厅。"% m2 o7 h* a( W/ s( v
  28.   (t living-room))! B1 N6 f) z0 N, z4 V2 J9 v" x1 ^

  29. & C* p+ N* ?8 M6 O) c& D; X3 t. a
  30. ;;; 教程# W4 Y) `8 Q; O2 e4 p! H) N
  31. (defvar *wechat-5x5-tutorial-rooms* 01 a6 G* y$ {3 d$ }- s  A
  32.   "The number of tutorial rooms")) S! w) k6 W+ |+ j
  33. 6 s9 A) R- ^+ y. [5 s
  34. ;;; 简化教程的定义3 [$ D- d' h5 v. c  p
  35. (defun string-last-line (content)) g0 n7 s6 _- J1 l; |
  36.   "多行内容组成的字符串中的最后一行"% n! o5 L  b1 Z# c2 g& c# j) k
  37.   (with-temp-buffer
    - J! _* u- z4 K. D9 h% }) X
  38.     (insert content). D' v6 j5 ]5 T5 D
  39.     (buffer-substring (line-beginning-position)
    7 u: x+ K. ?5 j# D
  40.                       (point-max))))" e; z3 Y9 \; s0 V8 I
  41. ! |1 _; ~5 @* D( D6 T
  42. (defun def-tutorial-room (prompt); m6 P! M! z4 I+ j4 z+ b" [
  43.   "根据提示语自动生成教程房间。
    ( M# {% r8 A5 c% Z$ m9 m  C
  44. ' G7 e: ?" X8 ^: G' l6 T4 ?
  45. 1. 提取最后一行作为问题;
    . Z1 E$ w, H5 C2 u
  46. 2. 分析问题,获取期望用户输入的内容;$ L& n1 S: s& n7 U- z
  47. 3. 定义教程房间和重复提问房间。"
    . A0 c# S' K1 v, u2 b/ Y
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    ) X8 J0 c2 x+ ?
  49.          (repeat-room (concat room-name "-repeat"))
    $ Y1 B8 j4 x5 v; m' `" B# B
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*)))); |# A2 c' c9 n
  51.          (question (string-last-line prompt))
    5 n: D" J5 q; V/ m7 N: T% @! X
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    # M8 o; X& U* o7 |
  53.                      (match-string 1 question)))& ^% o" e" T7 t7 S' N7 O1 ]
  54.          (doors (if except
    : j  E+ ^3 i/ h1 [" T3 }: j5 r% M
  55.                     `((,except ,(intern next-room))
    ; V! M: b/ r8 c  e& W
  56.                       ("q" living-room)
    " I- b5 ~5 H8 n; L+ m2 z, X8 N
  57.                       ("Q" living-room)
    : z; |5 F% \) k: t9 Q1 n
  58.                       (t ,(intern repeat-room))); t% j+ S- Z# {. t8 t1 r& k3 `$ f& x
  59.                   '((t living-room)))))
    $ p( f1 A) l0 u
  60.     (def-room-raw (intern room-name) prompt doors)( V# u% v; L* |! e
  61.     (def-room-raw (intern repeat-room) question doors)))
    , f% X+ v: C- Z7 C; b
  62. $ L  E+ h, b4 b, g1 G, _. b
  63. (defun def-tutorial (&rest prompts)) x  b1 e* `* l4 c$ H
  64.   "批量生成教程房间。"; V& t) P. D" S/ }. E- E) ~
  65.   (dolist (prompt prompts)
    6 I' A% c9 g9 r- O
  66.     (def-tutorial-room prompt)))
    ! J& i+ a# F; B2 L+ @2 y

  67. , i$ I' r% P) ~! |0 @0 H1 K
  68. (def-tutorial+ G5 T& z% _  g0 P" Y& H
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。) q/ _& g2 w  q
  70. 1. 教程% R: K- L8 S2 ^5 W; z9 B
  71. 2. 入门(3x3)2 C' s: \( G" G: A, k4 C) O7 F
  72. 3. 初级(5x5)
    ; k- g/ r* X) h5 q% T5 P6 f5 W
  73. 4. 中级(7x7)
    , f- T& S! J% W6 V/ ]
  74. 5. 高级(9x9)1 P" k" J( {+ D( `9 G0 ?
  75. 0. 关于作者
    ( L+ \# k3 d4 t
  76. 请选择1-5开始新游戏:
    - @. h3 j/ ]; M  r
  77. 您现在正在游戏大厅里。, o% w! Q% j5 _5 G* `9 Q$ h
  78. 请输入“2”进入入门级房间"
    7 Z4 f/ t2 l# v9 T- ?
  79.   "  ①②③
    " C" A% P4 P8 U  V) j+ A9 V
  80. 1 ■ 
    5 w' |  M. T# R# R& k, \9 H! C9 d+ U/ c
  81. 2■■■
    & l$ W; A! X% ~! K% N2 {
  82. 3 ■ 
    + Y! S7 x8 _7 W; C$ q7 j
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!" Q) o& [" o- N. {
  84. 请输入“22”来关闭第2行第2列的窗户。"9 `( M+ q2 ?8 S  s
  85.   "  ①②③, A. c. |/ I$ t% Z
  86. 1   
    & ^! j# Q# o3 I2 t# l+ p
  87. 2   
      g7 V) Z# y+ E! _
  88. 3   % v6 i& O  N7 R8 Y
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。+ A( q& m; Y) L% e( J* j
  90. 请输入“11”,试着开启左上角的窗户。". K9 t7 q8 o% N& d7 B
  91.   "  ①②③
    ( L% _. @0 V  w( P
  92. 1■■ ' N7 e+ R9 o2 y) _
  93. 2■  5 O% n, y5 x8 s1 l% L7 ?1 y; o
  94. 3   & Z3 u- C8 v% E5 |& V; i
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。: f! A3 t5 \' Z( r, f! A) s
  96. 请输入“13”开启右上角的窗户。"$ o5 j3 }& K. {9 ^9 G
  97.   "  ①②③
    # V3 _9 T' Z* f1 O
  98. 1■ ■
    . g' ]; o% B) @
  99. 2■ ■) @$ J" d. D" B; v
  100. 3   
    7 g* L) p0 }' g- m
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    ; n1 [- c: n2 I* i0 M, ]  X! `# o
  102. 请输入“31”开启左下角的窗户。"
    7 V% c+ P! w0 d0 G8 r* _6 @
  103.   "  ①②③9 H# N4 J) C+ a
  104. 1■ ■2 Z+ K% f& D! U* T7 C
  105. 2  ■4 B$ b' Y/ L. _$ P- n) L: e3 ?- M. Y
  106. 3■■ 4 p. O# @$ q/ y7 D  @6 o
  107. 此时,总共有5扇窗户被开启了。
    4 f& U9 }( H. V6 ]' r+ `
  108. 请输入“33”开启右下角的窗户。"; [6 b/ Z8 y/ M% e4 a
  109.   "  ①②③! a4 @/ N* e% U! h% X+ V
  110. 1■ ■* B$ p5 K  K0 S8 R1 H6 p' t3 V
  111. 2   
    9 `- m; l1 `; u( E5 {) q# ^4 A
  112. 3■ ■4 H  ~7 J6 ?8 p( {6 L- T
  113. 现在,只有四个角落的窗户被打开。* ?  W) i8 x: H1 B# n: G' S" A) r* Z3 Q
  114. 请输入“22”完成最后一击!"4 R9 n' f7 ^/ O4 [' Y: b! O* b) j
  115.   "  ①②③" }- N# y% B2 P$ J6 I. L
  116. 1■■■
    9 b6 ~* H' \  I$ p- M
  117. 2■■■5 M% S, ^' h' C( l& B; i$ s
  118. 3■■■
    ; K$ B# t+ J- I) M6 l  U4 I3 U
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")$ n) N9 O, a/ C% y4 [9 z
  120. 4 {# B5 k2 j% `1 O
  121. ;;; 棋盘- M$ g8 p/ t! u  p* h& M: t
  122. (defconst *wechat-5x5-white-chess* 12288+ [) |7 ?  G: m/ k% h6 ~; r. c
  123.   " ")
    / l3 g6 G) y  q0 q! L5 r

  124. ) [$ b" K( I1 [9 k5 t9 H
  125. (defconst *wechat-5x5-black-chess* 9632
    3 F, q% Q8 q5 h1 q! J% C
  126.   "■")
      z' w, W( o- u3 f  s. @

  127. # h, Q2 t9 f- ?
  128. (defmacro with-board (&rest body)6 t7 G4 V3 e+ Q( |; e
  129.   `(with-temp-buffer
    % e5 s4 D# z1 Z1 ]9 e1 s: {
  130.      (unwind-protect. I( ]% H: D" l6 u% |+ N  H
  131.          (progn
    3 H  O, [0 J, E. g( t  C
  132.            (if (session "board"). |7 S# I' e+ a( ~
  133.                (insert (session "board"))); k% W9 Q$ F3 G* n: E6 Q
  134.            ,@body)
    6 A  S0 ]: @: E, L( A# c5 [" [
  135.        (session "board" (buffer-string)))))
    ' s' M# n4 C! U. J$ ]
  136. + V2 l  p0 y% H' P3 _6 L
  137. (defun board-init (size)
    $ f% r. r; Q: L1 O: W$ k4 W/ }
  138.   (session "size" size): u( a7 q, \* [  U9 D
  139.   (session "step" 0)
    ! u: e% D+ `7 j+ K+ P# I' V
  140.   (erase-buffer)
    : S" e, q+ N0 W  V' ~
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))" O$ X8 M# }- P6 c/ R( L6 M5 [
  142.   (dotimes (row size)
    2 y8 M6 A4 T. t2 X4 h' e' s
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))% t: ^/ x/ _* y  G) ]4 @

  144. 2 d$ s- H* ?3 \, A
  145. (defun board-contains-p (y x)
    0 C& [2 Q3 W8 U% g
  146.   (let ((size (session "size")))" r- _; J( x! U
  147.     (and (<= 1 y) (<= y size)0 b& O5 @' |& q$ g9 U1 ?$ \6 ~+ f% e
  148.          (<= 1 x) (<= x size))))
    ; N1 i7 s0 R$ Z0 d1 O  f6 l* m
  149. 5 m3 i5 D9 a- ^( c
  150. (defun board-toggle (y x)
      v" f1 a" c, o, z! S
  151.   (when (board-contains-p y x)
    0 I/ \$ _) f, {& u9 M
  152.     (goto-line (1+ y)): k9 t6 ~- ]+ x6 V
  153.     (beginning-of-line); q# {* Z0 m9 x" ?7 e, h7 d
  154.     (forward-char x)+ H! G  o( ]6 C2 z. r# l
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char)). {# S+ s# z# x# B- b- v9 |
  156.                 *wechat-5x5-black-chess*
    0 f. m' M1 j; s
  157.               *wechat-5x5-white-chess*))( `& I2 ]" l( A
  158.     (delete-char 1)))! Y. P$ u; k: d6 [  ~

  159. : v' `* Z: a* t; ]( D
  160. (defun board-put (y x)
    + c( Q$ m1 h; m
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))6 ^4 e$ \$ t' }  d
  162.     (board-toggle (+ y (first dir))
    ! Y' C! i: L  t; @
  163.                   (+ x (second dir))))): T& i3 v7 u7 f/ n7 G

  164. 0 [; O% s& M* A- ^  V7 x" Z
  165. (defun game-over-p ()
    4 Z! s& h) a& B' x! z
  166.   (beginning-of-buffer): ?- r5 q* O9 |$ F* |# t$ k
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    ! b' U. _( b: m* H2 V, m

  168. 1 J$ E* j3 `+ E9 R3 u' X
  169. (defun board-show ()
    7 [* ]  I/ {) ]. }, E
  170.   (with-board
    / e1 ]) c8 x9 w. {$ V
  171.    (concat (buffer-string)
    ( U$ Y& I! }/ ~. u( @7 U; ^
  172.            (if (game-over-p)
    4 q  @6 \; |' c- P; P
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))8 i7 U& N$ i6 L$ z6 L. v. D, r
  174.             (format "第%d步" (1+ (session "step")))))))
    ' b1 u* k( M& Z# J$ F
  175. % g# ^, T" P+ o6 r6 N. G
  176. (defun board-position-parse (cmd)( c! z, H+ w) {5 \' r
  177.   (if (= (length cmd) 2)8 k1 D) B4 c: M0 f/ G
  178.       (list (string-to-int (substring cmd 0 1))7 M$ F" A) W+ x9 K, v
  179.             (string-to-int (substring cmd 1 2)))
    0 E% l; d" x3 k( I1 t: N6 h; Z8 o
  180.     '(0 0)))
    6 Q* ]# g+ R( `& p1 X
  181. 2 e7 E3 y& ?5 `: M' m/ N
  182. ;;; 游戏房间
    . W2 p2 X1 k9 B' H
  183. (defun game-room-init (cmd)- ^, a/ Z( e3 `+ r8 q, U: n
  184.   (let* ((middle (string-to-int cmd))+ X0 V8 `' a( x4 V' e
  185.          (size (1- (* 2 middle))))* [% o; }8 G( X5 S
  186.     (with-board
    5 q# a2 V$ N( k1 \$ R: R* v* e1 Z9 e
  187.      (board-init size)) i& `1 j5 q; ]7 j5 O9 [
  188.      (board-put middle middle)))& a) i. [6 H' P2 k9 r
  189.   'game-room)
    / R3 b6 Y, ?" |/ ?8 z

  190. 4 c6 M& h5 y& b4 F5 J1 l/ {
  191. (def-room game-room
    ; d( O6 `* `  B
  192.   #'board-show
    7 i2 _& Z6 g( X! J
  193.   (t (lambda (cmd)+ A! F1 T1 D; {# X% U' G
  194.          (with-board' ~. F' o+ L" j5 B
  195.           (if (game-over-p)
    5 ^& p2 F" ?( P0 ^
  196.               'living-room: A, f6 m/ y0 l' r5 X
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    ; r; M! W2 d% [6 s6 {3 _1 }4 r
  198.               (when (board-contains-p y x)! F# X5 ~! z( q. \
  199.                 (board-toggle y x)
    1 P' \* w' N  b- j
  200.                 (session "step" (1+ (session "step"))))
    3 _0 P4 F5 E/ u8 O. i6 D8 f
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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