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

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

  1. - X& b4 N' Z+ e- e' F
  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;">;; 定义新的游戏地图
    * m- U# V, e; p
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    ' J4 M" |7 m$ `; x1 b) @# A5 h" N# a
  4.   'tutorial-room-0)                     ; 默认的入口2 \% Z6 v8 T, O$ N
  5. % U8 J6 F; G  t
  6. ;;; 游戏大厅
    : f6 R) h/ T3 w, ?* C/ c4 h
  7. (def-room living-room
    - e2 H) W+ r8 j, P2 y) Z
  8.   ;; 进入该房间后的提示语
    % X. e$ n6 K# H
  9.   "1. 教程" P5 R  k( V2 n% r# r/ M
  10. 2. 入门(3x3)
    6 \% {6 S. W* n0 Y! V6 j3 a1 M
  11. 3. 初级(5x5)
    : f: I$ S% c5 u
  12. 4. 中级(7x7)
    ( S8 f& g9 y+ i+ I9 W
  13. 5. 高级(9x9)
    ! M0 t. y5 ~6 R. t
  14. 0. 关于作者
    & _8 f1 X/ Z4 t
  15. 请选择1-5开始新游戏:"
    : P/ l) R, U5 k/ Y, b! {
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    " C: k, \1 a  w8 [/ x9 N
  17.   ("1" tutorial-room-0)4 f) H( D- f: C
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    % @- t' \  b3 T* w. r
  19.                                         ; 相应的返回也可以为函数,动态返回房间名7 O7 X8 ~# i1 F/ m+ N9 c2 b
  20.   (t living-room))                      ; 如果条件为t,为永真
    ! E. y) T, S' M; i- A: t

  21. ) C) y) w7 F& g' N
  22. ;;; 作者信息
    1 [" e* E( p1 K$ Q9 {
  23. (def-room about-room- b! B3 A) y2 k9 `8 O( Q  j4 D+ l# z
  24.   "作者:redraiment* R1 h7 w+ ~3 E. ?' A6 ^( H
  25. 微博:http://weibo.com/redraiment4 M* c' s7 y; J* Y" [8 U6 K. i4 ?% W
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    . E+ t: @3 l* R
  27. 请输入任意数字返回游戏大厅。"+ v; G0 }& N" f7 s0 z  o! ~
  28.   (t living-room))
    / J6 C. Y) o8 V

  29. 2 B! l9 u0 z: b' t
  30. ;;; 教程' B- f( f3 M* C4 C0 s7 Q
  31. (defvar *wechat-5x5-tutorial-rooms* 06 u; j! m; {) \' x; P7 n( F" F
  32.   "The number of tutorial rooms")
    ! p' {3 ?, _, E0 r

  33. 4 [3 }3 ~4 e7 K; }
  34. ;;; 简化教程的定义
    ; j  O1 A9 x; Z
  35. (defun string-last-line (content)
    5 R! I+ P* i' j" [  A7 S" m# t
  36.   "多行内容组成的字符串中的最后一行"
    : @/ Q0 y) c+ A1 B6 U# ^
  37.   (with-temp-buffer
    , Y* N& a2 G/ w: c- r  e% V
  38.     (insert content)$ c0 H& Z! K4 l
  39.     (buffer-substring (line-beginning-position)
    " {' r( T/ s' l) g' A0 r- v$ v  E
  40.                       (point-max)))). M! [, b0 D/ y) K3 j
  41. : [) R$ x  H* P1 ~+ C5 w. X& A
  42. (defun def-tutorial-room (prompt)
    % ?6 z6 E9 {5 U5 a
  43.   "根据提示语自动生成教程房间。5 r0 d# V% t, s# f# o

  44. - ^- e$ X4 Z2 d; e4 W2 I# Q% F
  45. 1. 提取最后一行作为问题;& r7 D; N, _) O! {& H
  46. 2. 分析问题,获取期望用户输入的内容;# N5 d+ j: ~$ N; }9 h
  47. 3. 定义教程房间和重复提问房间。"
    ( o; [, j3 A" I
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    ( j- O' I8 F" Y% U; x; @
  49.          (repeat-room (concat room-name "-repeat"))
    - S( l9 a) b8 Q5 [* r
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))" b  o/ {8 ?6 A
  51.          (question (string-last-line prompt))
    2 j/ h, V) p( W
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    0 s  Y, b6 T$ ~& N5 v4 E( X
  53.                      (match-string 1 question)))
      l% s2 B. @8 T
  54.          (doors (if except
    : D& Y/ l4 D" R. |) m* x
  55.                     `((,except ,(intern next-room))
    $ Y0 o& |# P# C* J/ m' {" H
  56.                       ("q" living-room)
    0 R2 h2 u0 E7 ?9 P
  57.                       ("Q" living-room)3 w6 k7 ?" j! @& H% D6 C( l' V3 w
  58.                       (t ,(intern repeat-room)))
    # [2 [( o7 K; h  B' Y8 d, g" K& b
  59.                   '((t living-room)))))/ ~1 g$ u- i0 N" l; |' p- K4 O) |5 b
  60.     (def-room-raw (intern room-name) prompt doors)  R# B/ q6 q- }  p3 a( c
  61.     (def-room-raw (intern repeat-room) question doors)))
    % Z" [) V! E/ }! c, L, g! D) D/ @

  62. + S& s$ ^/ _& b4 ]7 @: Z
  63. (defun def-tutorial (&rest prompts)  f5 ^2 \3 h& e5 E. R! X
  64.   "批量生成教程房间。". C8 r5 l/ y+ C3 k
  65.   (dolist (prompt prompts)
    % S+ ]3 [5 A0 e* _
  66.     (def-tutorial-room prompt)))! L; h; ]6 m7 M8 Q
  67. , P& L0 }* c) r  g, Y6 H2 J
  68. (def-tutorial
    . A( s- X0 a" a- N' X1 s
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    5 E8 K. X" k" w, G8 a6 [
  70. 1. 教程3 {( @1 ?3 F* Q; s
  71. 2. 入门(3x3)
    ; e9 F# o! ?$ F( s- p5 L
  72. 3. 初级(5x5)2 \! [8 [# ?! z+ j; f* ?4 ~; W
  73. 4. 中级(7x7)
    1 e% d% ~: U) @9 y# u+ a
  74. 5. 高级(9x9)" V7 _9 h/ P$ ]
  75. 0. 关于作者( S8 l! B! z  D2 h
  76. 请选择1-5开始新游戏:
    8 s8 n! a/ c& [$ z
  77. 您现在正在游戏大厅里。
    / q8 J# u8 V8 O9 p& M1 s
  78. 请输入“2”进入入门级房间"6 R( a. w2 ?' A0 D9 f
  79.   "  ①②③
    3 ?, u6 P6 }% y8 d# X
  80. 1 ■ 3 P: F: _  S. A5 Y2 H
  81. 2■■■
    2 u5 Z2 }- A8 I9 @( f7 V0 S8 N
  82. 3 ■ 
    # W- r7 N5 b: O% U' s1 s
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    : @; s" N8 N# @; \- I4 a
  84. 请输入“22”来关闭第2行第2列的窗户。"
    " x! m/ B9 d; W2 L! j9 W
  85.   "  ①②③
    6 ~4 g1 O7 N  m
  86. 1   9 C8 B2 N# g" ]0 [1 P6 i
  87. 2   * o6 d! u5 B: C% e0 v  S6 l) R! P8 Q
  88. 3   
    1 a4 D2 V9 {6 y- i4 F1 v  ^: G
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。% |3 M3 |& s2 B8 F  W6 o
  90. 请输入“11”,试着开启左上角的窗户。"
    * [3 d! f: r' t$ f* e: B
  91.   "  ①②③7 v6 F  P8 R6 ?* a, q6 E
  92. 1■■ 1 M+ ]# s9 F' \( i6 z
  93. 2■  
    5 T; C7 [; w$ z0 C4 ]* A  n
  94. 3   
    & Z" ^3 U$ X( f" U4 O4 k5 Y# _
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    # x! H/ R+ a% f
  96. 请输入“13”开启右上角的窗户。"
    / ?& V6 a3 ]! o- ~
  97.   "  ①②③" s" z" E& K4 {/ l" J  s6 V
  98. 1■ ■
    : S5 b2 K" O1 y) @" u7 A! [
  99. 2■ ■
    4 t5 ?* a& w% {0 @8 j: J/ y0 Z
  100. 3   
    ! ^) u' d" ?4 Z2 Y, q0 u! K
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。( A* F( }3 S! \& N* z' S' u
  102. 请输入“31”开启左下角的窗户。"2 C( D7 P% ~1 s0 L) i% Z1 B
  103.   "  ①②③
    : [9 H; E, b* ~4 u9 d0 o. U' L
  104. 1■ ■
    1 }0 R% t/ z4 G( p- Q. z
  105. 2  ■6 ^% [9 G4 ~: x
  106. 3■■ 
    7 g% I* {) _" R9 E3 b* Z: ^
  107. 此时,总共有5扇窗户被开启了。8 |. M; @1 V/ R% B$ a+ u$ Y
  108. 请输入“33”开启右下角的窗户。"" w4 R  c" t! S# ?1 j
  109.   "  ①②③
    & s7 C6 B& K/ d
  110. 1■ ■
    + I  C% r" \) j. R
  111. 2   
    3 u. l' K9 U% m
  112. 3■ ■0 _1 ~0 s  y& N  m% P2 ^/ f1 F; L0 B
  113. 现在,只有四个角落的窗户被打开。* H0 U4 H2 ^1 I: c; }; I
  114. 请输入“22”完成最后一击!", Z) @- o4 k' x- Z
  115.   "  ①②③
    ' `1 s! E3 d; M! V, l: C" y2 J
  116. 1■■■
    ; e7 u7 q& y: z. {. G
  117. 2■■■
    5 m7 a0 A4 `$ K3 w& \. j
  118. 3■■■+ N* Z" P8 h$ H/ g- t0 P) y
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    5 t' E* f* U0 U/ G! n# ^+ \

  120. - p+ t: B& E& Q5 N- x. r, G- O
  121. ;;; 棋盘
    5 G7 ?5 e+ L, Z6 f
  122. (defconst *wechat-5x5-white-chess* 122882 a. ]( e% `; B4 ~1 I/ g
  123.   " ")$ c6 j8 _/ ^8 z  u7 b

  124. , E0 ?! _+ a8 U7 b8 B( p* F
  125. (defconst *wechat-5x5-black-chess* 9632
    ( t- o: R8 r* m; A% E
  126.   "■")
    5 ~  S$ Q4 Y  e. ?, F
  127. & Y; l, l/ e- S
  128. (defmacro with-board (&rest body)
    6 r6 u% N( h$ p9 V: y& J
  129.   `(with-temp-buffer5 g' u+ Z' l# R
  130.      (unwind-protect, F$ U! E8 i3 s5 u, K- b
  131.          (progn; K  O. {3 h' K2 S9 t8 f8 V
  132.            (if (session "board")
    1 r" S) \! g# Q* d3 F
  133.                (insert (session "board")))
    % Z9 z( {# l6 K6 o
  134.            ,@body)  m, p4 n" A; z% e- ?
  135.        (session "board" (buffer-string)))))3 h- c& ~( O* D0 ?
  136. , M" K) D: I- o
  137. (defun board-init (size)
    ( Y( r8 s' [) R) O3 J
  138.   (session "size" size)9 D, }. ]; [# C5 Y# T4 k
  139.   (session "step" 0)# H6 V3 S/ A+ q. i9 f3 _# \" K# A1 A
  140.   (erase-buffer)
    7 M& }. R; `( O1 [
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    7 \8 c# H% o( _1 H  \
  142.   (dotimes (row size)
    1 ~/ u6 K; X/ j- {# Y
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))! T( s' W; R5 k% X3 k

  144. 9 ]8 _1 |$ {) E  _2 b
  145. (defun board-contains-p (y x)0 ]/ v' k# W. ^% w% Q( C
  146.   (let ((size (session "size")))# m) c3 t7 M& L# ?' _
  147.     (and (<= 1 y) (<= y size)3 ?5 ?3 j, a* q
  148.          (<= 1 x) (<= x size))))
    / ~/ G- f/ W& M8 d

  149. ( Q5 q  S) _, v) _
  150. (defun board-toggle (y x)9 |; b" q! H# b+ c# |/ e
  151.   (when (board-contains-p y x)
    5 k. ^% z3 I% N/ U+ S
  152.     (goto-line (1+ y))
    2 O$ v' v+ i+ `( ~
  153.     (beginning-of-line)& Y) q' f+ y9 C2 C9 e+ ^
  154.     (forward-char x)  S( d0 v5 R/ K* U' a, o8 n
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))6 F( D& c3 H9 P. w) H
  156.                 *wechat-5x5-black-chess*
    " l' J8 B  {2 x1 f' J
  157.               *wechat-5x5-white-chess*))! B1 x* b0 f, q$ U$ ]6 s$ m
  158.     (delete-char 1)))
    ; I6 O6 A( i8 M
  159. 9 ^4 K( S( w0 l5 Y; W# |4 n
  160. (defun board-put (y x)
    8 Q* D0 v. m0 M4 I+ V
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))% {0 p6 m! [2 F
  162.     (board-toggle (+ y (first dir))) [& B$ V+ F2 U. j7 O0 ?0 X
  163.                   (+ x (second dir)))))
    ! O- {* S5 \8 l
  164. * s, }. {! O; z  b+ G/ H( \
  165. (defun game-over-p ()
    " H% @' k, f2 @, t1 w, e
  166.   (beginning-of-buffer)
    * Y" Y! J" l, L1 i
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    . O3 h% j  w3 ^
  168. ! s# O, f1 e: V* @% q
  169. (defun board-show ()5 S6 L% k- ^& h* D
  170.   (with-board' f( x: R3 I6 ]4 l9 H% B8 k3 D1 u# g
  171.    (concat (buffer-string): V+ P8 q* [8 ^% m0 Q
  172.            (if (game-over-p)
    ( B# c& i" B3 V' i3 G
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))  \- T/ c" ?/ }) m. X" |+ i
  174.             (format "第%d步" (1+ (session "step")))))))0 e+ q. R- @% q8 g+ j
  175. ; S8 g) C& M  J7 `/ U5 J' ?9 w
  176. (defun board-position-parse (cmd)
    . u5 V( ~: i  V$ L4 Z: @$ {
  177.   (if (= (length cmd) 2)
    : Z, u8 ?) l0 t0 ]+ y! L  m
  178.       (list (string-to-int (substring cmd 0 1))7 Q: K; x# c" b$ [8 o0 u9 q  ]
  179.             (string-to-int (substring cmd 1 2)))
    0 h. y$ Y7 J& E3 o
  180.     '(0 0)))
    - n8 J0 V+ \& B- |6 |
  181. 3 K, j) [& T; l/ F5 t0 B3 D$ H
  182. ;;; 游戏房间6 B. v' L5 Z0 Q0 ]  ~: a
  183. (defun game-room-init (cmd)
    # V; e+ m3 i' {  P/ m
  184.   (let* ((middle (string-to-int cmd))
    1 e+ q4 a. n; U# E* p; h
  185.          (size (1- (* 2 middle))))
    8 N: |; {6 p0 O8 x* @3 `' t4 k
  186.     (with-board! ~, O% L2 [9 z: O
  187.      (board-init size)( N3 C3 d1 G0 k8 X1 P9 e
  188.      (board-put middle middle)))
    ' a( K/ R5 I; ~7 C/ q9 s! K" ~8 M( t
  189.   'game-room)
      t! g) `; K/ L& t7 P4 X

  190. 4 O  l5 g, z2 w' I; K
  191. (def-room game-room# R( l% g, c; N& f2 D, h0 l
  192.   #'board-show( H  q9 D: a& S  e4 W! A4 X
  193.   (t (lambda (cmd)
    3 k- L$ [9 U% ]- ?3 a4 Y4 o
  194.          (with-board
    4 h/ ]  B- P8 C
  195.           (if (game-over-p); @6 ^3 X5 u2 J/ N
  196.               'living-room
    3 G+ e# o1 F+ j( {( l  |( T. i0 Y! U( _
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    ( J0 I( ^( {* F* t
  198.               (when (board-contains-p y x)
    . J/ s- Q: A. T% d8 F  M
  199.                 (board-toggle y x)3 d) Y0 t+ `# e1 w1 t1 h* G
  200.                 (session "step" (1+ (session "step"))))
    8 t' Z9 W: i, [! n
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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