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

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

  1. ; U* ^7 r8 t7 J+ P6 x0 }4 l
  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;">;; 定义新的游戏地图
    ( i4 e1 n+ n' M: s9 j- C7 K1 C
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL0 S$ [' K0 J3 p* B' r: u
  4.   'tutorial-room-0)                     ; 默认的入口' j" s* L' q$ f0 o5 B  G$ m9 S
  5. $ m# Q7 n( D, g8 m- `: ~9 A) V4 e2 u
  6. ;;; 游戏大厅4 O! p1 {% D4 f( y: o
  7. (def-room living-room( m% [. V7 U3 \& C
  8.   ;; 进入该房间后的提示语! q8 j# b' `* S  o
  9.   "1. 教程- B$ w: C! q6 {% f# [7 c
  10. 2. 入门(3x3)
    1 u1 x' U0 F) h( M  x! a9 r$ e
  11. 3. 初级(5x5)- C9 U( p" N, D8 V
  12. 4. 中级(7x7)
    - W' p* S9 Q1 V) n' v2 N& }
  13. 5. 高级(9x9)
    7 j, F2 e0 T& ~
  14. 0. 关于作者! O  K3 w3 B$ B- K8 t+ Q  E1 j' |0 [
  15. 请选择1-5开始新游戏:"9 o5 F+ h  r* I& R' N
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名+ A/ ~$ Q, \$ W/ S
  17.   ("1" tutorial-room-0)7 A: _0 l: t& w- N; A/ _- ]9 y* R( b
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配. M- Q0 w/ f) \1 P$ R7 W( A
  19.                                         ; 相应的返回也可以为函数,动态返回房间名1 W1 u! j+ K2 |5 i) X7 s
  20.   (t living-room))                      ; 如果条件为t,为永真
    ; k# i) p, R5 w" x
  21. , s# b! u- S$ s0 T4 q
  22. ;;; 作者信息2 ]% [- q1 D: C% s. p4 [. o
  23. (def-room about-room  x3 l9 k) p+ @/ C, e- y/ z$ Y
  24.   "作者:redraiment
    0 n0 ?6 @4 @6 q. [' P
  25. 微博:http://weibo.com/redraiment
    . R7 I0 h6 @& V
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    # ~* A: r& `) r+ v1 j- w
  27. 请输入任意数字返回游戏大厅。"
    0 E- z8 w  D0 Z/ K( w8 I
  28.   (t living-room))8 R3 N, @# r! ]+ r' d+ u
  29.   `  }6 P  `0 z9 C
  30. ;;; 教程
    9 l6 S4 \; b. B6 T' D
  31. (defvar *wechat-5x5-tutorial-rooms* 0& t4 Z- J. r) t+ a2 {) K
  32.   "The number of tutorial rooms")
    ( p- P1 T5 F, j0 k$ r
  33. 3 d# A0 L/ k4 O! c1 {; A8 b
  34. ;;; 简化教程的定义) z5 q* o  }( Z' ]3 Y# m6 g+ ?7 D
  35. (defun string-last-line (content)
    ; N6 X# ^3 c6 c# H' a8 _/ V
  36.   "多行内容组成的字符串中的最后一行"
    # k9 N6 M+ Y% O/ I! n6 a, N
  37.   (with-temp-buffer
    % p4 r, u0 {4 S, L
  38.     (insert content)9 v( d6 k- [, J3 s! O
  39.     (buffer-substring (line-beginning-position)7 R6 G6 ~; v7 G5 e' C5 F# s! x
  40.                       (point-max))))
    2 Y0 z2 q: Y$ J/ h+ m: A

  41. 9 l4 b+ j/ t' {/ e/ d
  42. (defun def-tutorial-room (prompt)
    * ~" s, z9 H( S- n
  43.   "根据提示语自动生成教程房间。8 G* Y2 G$ X; \/ L- ]8 f
  44. 5 I" d9 O+ h1 d' [6 K# _5 F2 _- z
  45. 1. 提取最后一行作为问题;
    + b" T( x& Y  G: H& t
  46. 2. 分析问题,获取期望用户输入的内容;" {: c5 S9 y% I2 J( I1 m- A
  47. 3. 定义教程房间和重复提问房间。"* x; [# B, y6 o( V, q0 L  Q
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))9 Z; Q/ O  x5 C
  49.          (repeat-room (concat room-name "-repeat"))
    : O2 A) _2 D0 g
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))" v4 p$ j+ M: ?# x  R) J( z# z
  51.          (question (string-last-line prompt))) i( z7 C% d" D
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question); ]$ h5 p" o! e/ s/ y
  53.                      (match-string 1 question)))8 @) R$ R' m+ t
  54.          (doors (if except
    " N9 A; B" D$ j3 I0 N
  55.                     `((,except ,(intern next-room))
    0 P( _" X$ ]8 a5 o+ o
  56.                       ("q" living-room)+ n- h4 f2 a/ d/ {+ b) h
  57.                       ("Q" living-room)
    9 R5 x, `9 Q  Z  d! \6 R
  58.                       (t ,(intern repeat-room)))
    4 m5 C$ }# `( X( h1 W3 H
  59.                   '((t living-room)))))
    3 U( y8 Q) v+ q) D9 g
  60.     (def-room-raw (intern room-name) prompt doors)
    9 X1 L4 L, U) b6 Z
  61.     (def-room-raw (intern repeat-room) question doors)))
    5 z2 |1 C5 A  v# L: H, G# D  a

  62. & }1 p3 W2 `' E# U* t
  63. (defun def-tutorial (&rest prompts)
    , m9 u  y4 l& |/ y' p4 V
  64.   "批量生成教程房间。"
    2 E/ ]# n$ |3 H1 i
  65.   (dolist (prompt prompts). H5 }" L2 F. M" C' h
  66.     (def-tutorial-room prompt)))( b2 D0 C2 r- b9 P
  67. + W0 P- z5 M% F7 `% a8 }
  68. (def-tutorial. l) C. R$ ^. G! m/ G
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。" w5 E$ I9 H3 C+ @
  70. 1. 教程
    3 V* T; `5 q3 X5 t" N: u
  71. 2. 入门(3x3)2 p- ~/ t, j4 G4 N
  72. 3. 初级(5x5)
    4 F1 z/ t0 A6 I- X
  73. 4. 中级(7x7)( @/ p; n' T2 o" }
  74. 5. 高级(9x9)
    3 R( L6 O3 H) L" v( s) {0 g
  75. 0. 关于作者
    ) S- i2 y/ a8 q! r5 M# J
  76. 请选择1-5开始新游戏:7 E) J# @# l+ Q/ o  `9 H$ \
  77. 您现在正在游戏大厅里。
    ; f9 g* H5 i2 N5 y/ }$ W+ g
  78. 请输入“2”进入入门级房间"
    4 G2 Z& z) {. ?
  79.   "  ①②③
    , g0 `# v2 ^* ^) ]( P
  80. 1 ■ 
    8 D- W; X. k1 p9 m' E+ n' n2 z8 Q
  81. 2■■■
    9 X8 l7 P; @/ o+ N3 E. w8 V
  82. 3 ■ * ~6 j" C7 v3 v2 h( D( q, V6 u
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    ( H) ^! P" E  W1 l% s3 I
  84. 请输入“22”来关闭第2行第2列的窗户。"
    % l3 L5 D; x# J
  85.   "  ①②③
    * Q. G2 N& E  g
  86. 1   
    ' h6 y- K4 M) v( J
  87. 2   5 j, o0 m# I+ ~2 k2 Q: T! C. R
  88. 3   
    6 Q( l5 z/ q- m2 `( t  S
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    " d8 n. ^" Y* i! K7 K& f4 R
  90. 请输入“11”,试着开启左上角的窗户。"
    ' q  t& p6 G/ b, k
  91.   "  ①②③
    * N" H" {3 u  A7 h4 n
  92. 1■■ 
    7 l  d  C4 E' {; {7 w2 {
  93. 2■  
    7 [5 H7 E" S' S9 x+ l
  94. 3   / w+ L2 n. u+ _$ s2 d
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。* N: T7 Z, T: U, v3 `
  96. 请输入“13”开启右上角的窗户。"/ X- R; S- E9 P5 g
  97.   "  ①②③: U7 H" i& p+ i7 g- y* Y
  98. 1■ ■4 z/ K2 C2 L8 I* Z
  99. 2■ ■9 `. v: S8 B! U' y' n! R
  100. 3   
    & A& r% T0 N6 i8 I1 x  T$ ]& j7 n  r
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。& K' e9 ]* V5 e( O
  102. 请输入“31”开启左下角的窗户。"
    & E" u* a7 M+ }9 y2 C  G0 z' n
  103.   "  ①②③+ F% r0 E; u6 U: G! \
  104. 1■ ■( D3 v" z% s! u  J
  105. 2  ■( U& O4 D8 Y4 l( {8 C- {$ P
  106. 3■■ 
    3 _( A! v7 }1 U
  107. 此时,总共有5扇窗户被开启了。! I3 h3 ~' ~1 I/ ]0 H9 q
  108. 请输入“33”开启右下角的窗户。"% q4 U$ D0 n' X: j" y  v, Y7 ]
  109.   "  ①②③; F* D, o1 D7 ~3 @
  110. 1■ ■. ?/ Z( c9 \& s" i8 u
  111. 2   
    $ z! Q$ l& g/ p- D% k" C, r5 A3 P
  112. 3■ ■# Q6 z4 }  |; M* G9 G& F' ]) u
  113. 现在,只有四个角落的窗户被打开。
    + t0 S: x3 Z4 @( y
  114. 请输入“22”完成最后一击!"' N4 {* u, @) x! f: f# H) _& o5 l0 Q
  115.   "  ①②③5 N6 y2 Q( Q) K% k
  116. 1■■■  V! d8 D$ k! C# B  f' F" u4 z/ K
  117. 2■■■
    " E) P& g2 N$ \
  118. 3■■■
    ( W" G2 q. ], S# c/ l6 U- M
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")% d( `1 V- x2 i8 W
  120. 2 p4 x# _7 S" h2 G. O5 w! S% A
  121. ;;; 棋盘
      ]" W( \3 q" t0 ~5 ~
  122. (defconst *wechat-5x5-white-chess* 12288% o1 K2 \& `$ P7 T! m
  123.   " ")& y7 ~: Z' b( k- s+ y. d8 i* S/ |

  124. 1 i' ]7 u4 O6 m% _
  125. (defconst *wechat-5x5-black-chess* 96320 B( ^' B, a" L) P6 t
  126.   "■")
    ! A- g" n& x3 M3 Y1 m1 B

  127. , F# f9 h! M; Q
  128. (defmacro with-board (&rest body)1 f9 r6 {+ E! {8 ]# i
  129.   `(with-temp-buffer
    - X% a5 }' [/ p9 `# L8 f, A
  130.      (unwind-protect. h0 T1 }; Z9 J2 u9 a
  131.          (progn: O4 a) z! V# {# p5 w, r3 _
  132.            (if (session "board")5 U% N# ]9 D4 w4 r+ b* w
  133.                (insert (session "board")))
    % L$ ]5 E4 X! I$ }/ e
  134.            ,@body)4 @/ `, v0 D7 h" ~- D
  135.        (session "board" (buffer-string)))))
    9 V' y0 a7 w6 Q8 y/ y1 i& ^
  136. ) i: F9 N; f- |: p: ]
  137. (defun board-init (size)' e' j, i) M2 A
  138.   (session "size" size)0 D* J" y2 C- v5 t
  139.   (session "step" 0)
    4 }' `; w5 R* M# K
  140.   (erase-buffer)
    ( A  E( p0 B: @; J' J9 D6 H
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨")); J. o; F+ }7 \% q& a/ `# c
  142.   (dotimes (row size)
    / D8 Z8 J* x' [2 V
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*))))): k7 }/ }9 ?: R: C, o9 @- e
  144. * T+ R) W# G% Y5 n9 E9 c  ^$ r
  145. (defun board-contains-p (y x)) _! N; d; f  U0 `6 V
  146.   (let ((size (session "size")))7 e6 c# x5 R9 H2 Q
  147.     (and (<= 1 y) (<= y size)& G6 t/ @" C' Q; f2 N3 D: B. l
  148.          (<= 1 x) (<= x size))))
    7 J( L& m/ b5 ?6 C) _, c

  149. 8 L5 u$ @% x! d& @& U3 L8 Z
  150. (defun board-toggle (y x)+ F( a8 u: A- U8 H) b( r9 ?
  151.   (when (board-contains-p y x)
    2 |; P, f9 Z  b2 h, {' {3 }) f
  152.     (goto-line (1+ y))" B+ @3 s" Q' v% `; P0 p
  153.     (beginning-of-line)
    & ?( K# W( T' _; C3 y
  154.     (forward-char x); t- W9 x* ?) z* M3 X4 o7 G) f
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    ' |  I- t( @& O( J
  156.                 *wechat-5x5-black-chess*$ T0 L$ @) |+ z
  157.               *wechat-5x5-white-chess*))6 H( Y1 \+ o* Q2 f( s
  158.     (delete-char 1)))
    4 {$ o  S3 m4 j& q2 j+ w+ [
  159. ' {% I" w9 |! V9 e' C
  160. (defun board-put (y x)! G$ Z/ g. l1 @5 Z9 ^' q1 y. _3 ~
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    $ `$ C$ O  q! @: i: @' I
  162.     (board-toggle (+ y (first dir))
    & N* W* g' Z/ l! W: T- `  I
  163.                   (+ x (second dir)))))
    + p+ |0 a8 d8 ~. Q# R
  164. $ Q; q: t3 v, X6 J) O) j# q6 {
  165. (defun game-over-p ()) d6 F. Z( g4 L1 K" a0 u6 b
  166.   (beginning-of-buffer)
    . R1 Q) j: A& t' g
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))& c0 t# \; }% p. _2 ~+ ], I; N

  168. 2 Q/ l$ e( E- y4 Z/ F
  169. (defun board-show ()
    - P  a) o9 E6 O/ o8 f
  170.   (with-board
    ; F% R$ ?1 X: O7 T3 @: h. e
  171.    (concat (buffer-string)
    # h9 o! M4 D3 w) m8 h9 E
  172.            (if (game-over-p)
    ; m1 @; J$ c7 ]+ i/ b& b* \6 L
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    / ^0 `& C9 |- m( h$ k
  174.             (format "第%d步" (1+ (session "step")))))))6 U. Q+ C( q' e) P1 h! P
  175. " v5 X# ^( o( H  ]/ B! G
  176. (defun board-position-parse (cmd)
    , B8 q6 z) `$ `- N# U: j$ b
  177.   (if (= (length cmd) 2)
    ; b  i1 {. M! w/ C" h
  178.       (list (string-to-int (substring cmd 0 1))
    , S3 U1 g# S: B4 ?: }- @
  179.             (string-to-int (substring cmd 1 2)))$ v" Q' M% J8 c2 p
  180.     '(0 0)))+ m. K- e- c' k- `9 K+ R

  181. % ?; {% q) f1 R; W. f
  182. ;;; 游戏房间( V% ?( }$ C- i, {& o8 [) E! ^
  183. (defun game-room-init (cmd)
    9 M+ C3 P  I3 z$ {+ ?& l) ~0 V
  184.   (let* ((middle (string-to-int cmd))
    # C2 N1 Z+ Z5 v& D0 Z# h
  185.          (size (1- (* 2 middle))))) B5 ~) d. c8 y+ M$ m# `
  186.     (with-board
    4 `5 z& x4 H& f5 T! h/ M
  187.      (board-init size)1 R# s1 w9 L8 K# ]" \3 v0 l6 Q
  188.      (board-put middle middle)))
    # Z& W* h  ~/ n7 W0 l+ ?: f' |
  189.   'game-room)
    ; |& i/ [+ X9 i$ F
  190. $ M( m8 s" b$ ^8 i1 P4 |; i% S
  191. (def-room game-room
    $ O8 X6 T% Y& D* W7 v7 q6 [; h
  192.   #'board-show
    3 {( P+ c+ J) j6 u  W
  193.   (t (lambda (cmd)& _7 `/ u# O# u7 W
  194.          (with-board0 |6 {' ?. ?; T$ E- E8 G8 t! p6 b. B0 ?4 y
  195.           (if (game-over-p)
    - R+ d7 Z' x, t- s) L) x
  196.               'living-room6 Q4 a+ J8 Y5 r/ t3 i9 O" Q
  197.             (destructuring-bind (y x) (board-position-parse cmd)# t7 _: `: Z; _" b
  198.               (when (board-contains-p y x)0 W- R6 Z% F" D1 i  Q- D" Q
  199.                 (board-toggle y x)  b) v! q! X2 W* j1 c: `1 h& w
  200.                 (session "step" (1+ (session "step")))); b) b! K6 c$ h
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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