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

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

  1. 1 I1 {( N$ s0 a( T
  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;">;; 定义新的游戏地图
    1 ^+ u7 y2 P6 I' n
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    . ?' x* L" f2 }: a7 y4 o" o2 a% {
  4.   'tutorial-room-0)                     ; 默认的入口
    & O9 K5 j6 A0 b% E+ _

  5. + O$ Q8 K& D2 |' _2 w& ?* x$ O1 ~, M
  6. ;;; 游戏大厅: O7 W" f) v6 q. o
  7. (def-room living-room
    3 m# N! C9 z( x3 \
  8.   ;; 进入该房间后的提示语
    8 }! v8 H( h. }' ]: G
  9.   "1. 教程
    9 H- f6 o- h0 t: o
  10. 2. 入门(3x3)) T8 Q0 V: T: Z! o
  11. 3. 初级(5x5)
    5 r. C( u  f1 }+ V
  12. 4. 中级(7x7)  P8 |( g. O0 m1 y/ [& |8 S$ G
  13. 5. 高级(9x9)4 j, }0 ^9 i* \) M& U# @
  14. 0. 关于作者
    6 W; H2 T5 y$ Y# J" S% G
  15. 请选择1-5开始新游戏:"# T8 k# c- E& a3 n
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    ! |& I2 {" j* Y& H/ r: v5 z
  17.   ("1" tutorial-room-0), E. K* X* o/ T5 ~) Z: _- G+ H( p0 Z
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配* E* ]& D. [# X/ I
  19.                                         ; 相应的返回也可以为函数,动态返回房间名# a( v  g; P4 s/ l6 x/ Q
  20.   (t living-room))                      ; 如果条件为t,为永真
    2 h4 n4 C1 |7 U. u  d2 D6 ]& \

  21. 5 \& f2 ~. x7 ~7 L8 ~
  22. ;;; 作者信息1 L& O- _: h0 {3 A$ h( }
  23. (def-room about-room  J4 C+ f  n6 k7 Y
  24.   "作者:redraiment
    6 b2 K0 Z8 h& d( f( l. S; ~
  25. 微博:http://weibo.com/redraiment6 V/ u! @' N0 Q/ [' x! H% F
  26. 有任何建议,欢迎在微博或微信上联系redraiment。! H: v& O" K. R
  27. 请输入任意数字返回游戏大厅。"
    3 }: h8 @- D- \- N9 t- Y
  28.   (t living-room))
    5 ^9 R* G& L- l6 g

  29. 8 e5 |# R& e( T: _
  30. ;;; 教程
    4 J- f' a  W+ P3 S8 z
  31. (defvar *wechat-5x5-tutorial-rooms* 0- t. |. O* `, X% C. W8 p- t4 M# C' O
  32.   "The number of tutorial rooms")# h. _1 Y; G+ y) |- q
  33. % L% X4 ~- a- c9 `4 e& Q
  34. ;;; 简化教程的定义& l* u( x/ x2 {; {3 y
  35. (defun string-last-line (content)4 m' k! c6 u% s4 n" R- n
  36.   "多行内容组成的字符串中的最后一行"% ~) a" l5 Y4 E0 x
  37.   (with-temp-buffer; e- H0 f% {/ [! C
  38.     (insert content)/ \: g' b' s4 F$ _
  39.     (buffer-substring (line-beginning-position)" t9 A2 K5 {. q
  40.                       (point-max))))
    & L$ O5 r# Q2 {0 l) I" h

  41. ; W0 V" e4 ]6 c
  42. (defun def-tutorial-room (prompt)
    : n" n4 |: d6 l
  43.   "根据提示语自动生成教程房间。  n" S- l% w7 T7 T# J

  44. * t# c& [- V' m# g  a! p7 [0 h- Q
  45. 1. 提取最后一行作为问题;# n) U4 A. \1 ]0 m; K
  46. 2. 分析问题,获取期望用户输入的内容;
      t/ k6 e0 U6 @$ w
  47. 3. 定义教程房间和重复提问房间。"
    9 H' |, f* r* y1 ^. ?
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    # r% B# Q$ T" W& p! [
  49.          (repeat-room (concat room-name "-repeat"))+ r5 j3 ]' V7 B- ~5 o+ \
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))  {! H8 k8 F, C& n. P5 R( P- p* I
  51.          (question (string-last-line prompt))
    . Y! D* `- ^/ f( P
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)0 J7 E* l. O. `" f% `, k) r+ P6 M
  53.                      (match-string 1 question)))% u  W; p$ l" k# W, `1 H* R
  54.          (doors (if except* G9 a: p. H- a/ i
  55.                     `((,except ,(intern next-room))
    ( s& i9 G/ J; u
  56.                       ("q" living-room)
    * a+ L6 M0 n4 |5 h+ D) F8 V' M  ?
  57.                       ("Q" living-room)
    ; `% _) R1 Q$ r2 G* w* f. t
  58.                       (t ,(intern repeat-room)))
    1 M( o; K: H2 D) Y' X7 ]
  59.                   '((t living-room))))), \: A9 o! ]  w+ p: |1 |
  60.     (def-room-raw (intern room-name) prompt doors)
    / I0 [6 r% R: k- c3 N
  61.     (def-room-raw (intern repeat-room) question doors)))
    2 A( v% d9 F3 S0 f
  62. * R0 B, i. z6 {: m5 R0 M* J- Q
  63. (defun def-tutorial (&rest prompts)5 Z2 |9 k9 h8 ?$ A" J* g, }; f
  64.   "批量生成教程房间。"
    / D) a9 P$ M0 u  w7 k5 }1 B
  65.   (dolist (prompt prompts)
      Z% {2 ?( u$ f  }' m
  66.     (def-tutorial-room prompt)))" ^9 N' W; }) {1 L3 a

  67. 5 H7 l; b! |/ x, o
  68. (def-tutorial) z$ Z  M8 p. r, @0 g
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。1 @4 [' W0 J* t. T. m: S
  70. 1. 教程
    , H% p% G5 m% u& y
  71. 2. 入门(3x3)6 T/ z) H% M9 M
  72. 3. 初级(5x5)6 k' F6 D5 i( V- ~5 |- }
  73. 4. 中级(7x7)7 J# D1 Y/ c; _  f3 R& g2 y
  74. 5. 高级(9x9)8 Q) g( r* ^1 v+ ?; F# O
  75. 0. 关于作者0 d" @0 Z/ s4 G; G# z# r4 X2 N2 \
  76. 请选择1-5开始新游戏:* A5 b+ O) ?- t+ ?2 A+ ?
  77. 您现在正在游戏大厅里。
    , N* j0 E6 R# N( C6 k
  78. 请输入“2”进入入门级房间"
      C; M" V7 ]3 P% f& t* l
  79.   "  ①②③
      ]- h& m( `: V# Q- x- \3 y
  80. 1 ■ + e( N0 y9 G2 C: e0 z
  81. 2■■■8 i9 u3 h+ B- U
  82. 3 ■ % l& D: Z# a. L* l! i9 H
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    + J" J1 ?# E& O  G! Q7 Y# K4 ~
  84. 请输入“22”来关闭第2行第2列的窗户。"- |9 I$ p+ v9 }! |) {2 E
  85.   "  ①②③
    - J+ Q/ u5 j; I7 b+ j' `
  86. 1   ! N9 Z/ w: b0 v/ y. |3 f) {' k1 l
  87. 2   
    7 Z- H' |: c1 ~9 W* E8 _6 V- j
  88. 3   
    % G! ]7 L: u" T5 [/ z
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    / E3 X  y0 g6 R. D
  90. 请输入“11”,试着开启左上角的窗户。"& J& F' ?# q/ v) `0 q) p5 x1 J1 A4 H$ j
  91.   "  ①②③
    / K2 [# ?) J9 ]
  92. 1■■ + `% ^* I7 y9 O. G2 k
  93. 2■  
    2 Q6 q( H- f( @* {1 H7 S: u% R" g/ n
  94. 3   6 E: |& g8 ^* O7 ~. n4 T" l7 S
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。) A/ ]- X- @3 c3 B8 |, C
  96. 请输入“13”开启右上角的窗户。"7 Q7 ?, ]2 l& v+ E
  97.   "  ①②③) u- {3 U+ k  X2 `* G2 l) N! }) f
  98. 1■ ■
    - x3 u. f- k) j1 Q- Z, s) l
  99. 2■ ■
    , X6 n4 y* z# q
  100. 3   & V% y: c: C: q( A
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。) j& ~& ~6 J0 N- _' D' |
  102. 请输入“31”开启左下角的窗户。"( \( t; d5 T* f8 `$ U' T
  103.   "  ①②③
    . O3 ]2 b3 f2 b& D
  104. 1■ ■" x5 r" F, N* X" u- Y
  105. 2  ■
    5 w- q/ a: C/ E+ ?+ H
  106. 3■■ ( G  O3 g6 x" t4 u4 s0 F/ r
  107. 此时,总共有5扇窗户被开启了。% q2 X/ n9 A! A3 l
  108. 请输入“33”开启右下角的窗户。"1 E2 q7 T" I5 |+ \! @5 y$ {4 u
  109.   "  ①②③/ b5 b% p* Q6 _# Q6 W
  110. 1■ ■
    2 [7 q" Q/ B! w2 @) P- f, |
  111. 2   
    0 W4 j2 w' n* r1 J/ N
  112. 3■ ■
    7 P) S' j; N& Z9 {& e# p
  113. 现在,只有四个角落的窗户被打开。
    2 n5 W1 g7 H5 \8 b
  114. 请输入“22”完成最后一击!"8 M8 ^5 K) ]! `% ^7 ]+ a0 |+ T: X
  115.   "  ①②③
    1 |+ [4 F2 B. J0 ~/ T' g
  116. 1■■■% @* Y: x: D" {) P
  117. 2■■■
    $ w, Q( D* ]: L: l1 B% a0 F7 _" a4 s
  118. 3■■■: ~, T# k! |; @) g4 V4 @
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")! r' K0 Y5 X- ]( H1 K3 @0 Y% \9 F3 H8 K  R

  120. 2 H# u- c) K0 b4 L$ C  q
  121. ;;; 棋盘
    : R* c! F3 ]/ [0 x0 @; i
  122. (defconst *wechat-5x5-white-chess* 12288
    * P! w# w) G* I% U0 J+ M  ]
  123.   " ")
    3 `- A' |! i; Z. J3 O

  124. 8 n8 p; @; O1 k
  125. (defconst *wechat-5x5-black-chess* 9632
    0 w; T% a1 P/ ~1 y
  126.   "■"); ^/ \5 P/ m% e

  127. + M% Q3 i# P, y$ Q; F2 C7 l
  128. (defmacro with-board (&rest body)+ {& p! b. E, z
  129.   `(with-temp-buffer6 Y9 w/ O0 ^( R7 W/ X0 o
  130.      (unwind-protect
    2 S; M5 p5 U$ J0 R
  131.          (progn
    5 `* I2 @2 q# m2 r: `3 Z9 |
  132.            (if (session "board")
    % e7 q1 b4 B# _- _: J' e
  133.                (insert (session "board")))
    7 |( R2 I) ~9 a- j, `* z8 a
  134.            ,@body)
    , J' n7 i9 w  I; C7 i4 V
  135.        (session "board" (buffer-string)))))
    * ^3 q) V/ ^5 Z

  136. " t0 _* G* Q3 U0 y$ b
  137. (defun board-init (size)9 X1 m. {3 q0 ?# Z8 Z
  138.   (session "size" size)
    . |, o5 A, W5 F6 K& o
  139.   (session "step" 0)' a, D; m- D* s* v4 V0 I
  140.   (erase-buffer)! }9 `" ?& g% N) Y! {  ~+ ?* L
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    9 g& |2 s& _- B# p# M
  142.   (dotimes (row size)- ]* W. t/ T- L/ m+ v1 {: H  \/ O
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))8 _& z9 W3 [' R

  144. " r; v$ s& n" ]1 K* W4 w
  145. (defun board-contains-p (y x)
    7 [0 h  D$ a5 f. y6 ]5 m
  146.   (let ((size (session "size")))
    3 s6 D1 A2 ]- f
  147.     (and (<= 1 y) (<= y size)
    ( F/ O! k, c# X3 a' j, k
  148.          (<= 1 x) (<= x size)))): c: R5 g6 P% Z# y

  149. + W2 k. f4 [( s- X. E  P; F
  150. (defun board-toggle (y x), _# M! a3 _. V/ Y6 T% x
  151.   (when (board-contains-p y x)
    - B3 }1 V* X$ T- G% r! p
  152.     (goto-line (1+ y))4 ^+ x. R; O/ |, d! Q: N
  153.     (beginning-of-line)2 [, N6 u& y. {# ^
  154.     (forward-char x)- L  y1 ]9 p: u2 h
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    , Y) L: A$ u0 H, t) ?
  156.                 *wechat-5x5-black-chess*4 O+ j- U) u: Y8 v/ W
  157.               *wechat-5x5-white-chess*))% u, L  W6 v) V) `2 D
  158.     (delete-char 1)))
    ) ^, X6 s( O3 g; N2 _* B
  159. " r; x& m1 I1 ?8 s9 n, f
  160. (defun board-put (y x)
    & A4 E! T9 l6 T3 I
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    * F9 \  m7 E" k0 F
  162.     (board-toggle (+ y (first dir))' z  f1 d$ K! U
  163.                   (+ x (second dir)))))
    7 G. r6 a1 C5 s2 E7 X- {) G8 L

  164. # J0 S+ a4 [0 A! X# F* q
  165. (defun game-over-p ()* W  e6 j- t' p; G( @7 n
  166.   (beginning-of-buffer)
      |, |1 l% n' _/ r
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    $ u* n7 N! C4 F

  168. . E) X& m' U  q$ r0 o$ R+ B
  169. (defun board-show ()
    8 ?! ?9 @; o% Z$ W. [4 B9 O+ o
  170.   (with-board
    3 ?0 R5 {* H) e" F  i
  171.    (concat (buffer-string)
    6 v/ x. W! G7 l: D, M. t3 A# D
  172.            (if (game-over-p)
    1 L% |1 l+ }: [
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))- U$ _, Y1 \- R+ R4 R9 }- {
  174.             (format "第%d步" (1+ (session "step")))))))
    6 S' ^6 j+ W( t

  175. : w7 B" b+ G$ M' A
  176. (defun board-position-parse (cmd)6 g+ {/ {7 d: I! D) v- k
  177.   (if (= (length cmd) 2)8 Q0 K( [$ R) G8 D8 q
  178.       (list (string-to-int (substring cmd 0 1))
    1 W7 d# U% G+ B2 j, @- h$ m* e0 x
  179.             (string-to-int (substring cmd 1 2)))
    ) S% h7 d8 ?9 [( @
  180.     '(0 0)))
    ) O/ W# X( H/ `* X1 \" T2 v

  181. * t4 t! J+ X" |( v' ?# @
  182. ;;; 游戏房间& U0 t( _; ?+ X/ w& J6 h
  183. (defun game-room-init (cmd)+ G* p4 n/ l7 N2 B. i: J7 B: Y1 f
  184.   (let* ((middle (string-to-int cmd))! ^8 p( O" u. F8 o
  185.          (size (1- (* 2 middle))))
    7 X: N; O% o% p0 f
  186.     (with-board) e  `  `6 r# U3 n8 g0 c9 z
  187.      (board-init size)6 X. Y: u6 j+ \  g- O6 R
  188.      (board-put middle middle))): M0 G0 W( o8 ]$ U  E! C6 E$ L0 l7 c
  189.   'game-room)# c' [" A" c6 M$ m6 n; {
  190. * C$ y, l6 {+ p7 E! q
  191. (def-room game-room- [- ^" p% T' w2 G$ E( F
  192.   #'board-show
    ! ?% u( ~$ k4 _& L! L0 N) U
  193.   (t (lambda (cmd)+ h1 b/ X' N5 F/ i8 {: n
  194.          (with-board
    & x4 Y- d3 l. U0 K+ }% Q' o
  195.           (if (game-over-p)
    % C( v+ ]& F" \2 t
  196.               'living-room
    7 D0 {/ S, @3 t" T" D
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    1 O$ a# l. s  D8 K. H6 u4 l
  198.               (when (board-contains-p y x); r. j9 ], u% V- F( w
  199.                 (board-toggle y x)5 e6 l. i2 `/ Q4 \/ ~7 O
  200.                 (session "step" (1+ (session "step"))))
    . o# w! b6 V! ^% Q% u0 P
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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