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

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

  1. ( V  _# ]6 Z- Z4 c3 O
  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;">;; 定义新的游戏地图+ Z6 `, h* b0 R( ]: ]2 X
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL" E2 B6 h8 V/ s8 p# U- \1 M
  4.   'tutorial-room-0)                     ; 默认的入口
    ' r! ~! l9 F* J3 M
  5. 2 f+ Y4 [1 \. w# U' ]- {
  6. ;;; 游戏大厅' R3 ]) X) e  \# p  |
  7. (def-room living-room
    * E! a9 R' T% `% B( j' r
  8.   ;; 进入该房间后的提示语3 z7 M5 O4 J: q+ o* w( ?' s7 T
  9.   "1. 教程5 B$ z5 n5 }0 |% N5 y1 U/ e
  10. 2. 入门(3x3)$ X7 `- x; A0 z9 `! E2 r) h/ D4 l
  11. 3. 初级(5x5)+ ~; Y' Z) f5 P+ a0 P+ o
  12. 4. 中级(7x7)7 i+ N1 w3 t* o& v
  13. 5. 高级(9x9)
    ' y" g# f$ r/ [- V" @2 a
  14. 0. 关于作者
    3 ]7 a) j8 d- W2 @7 R3 U
  15. 请选择1-5开始新游戏:"
    $ l3 v( Z% E' {5 d
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    ' C! \* x+ _/ P! K
  17.   ("1" tutorial-room-0)& C3 e7 T* h8 h% p2 g& _- [+ {
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    4 w2 W. Q( a! w
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    6 F7 V! A' g. Y2 W
  20.   (t living-room))                      ; 如果条件为t,为永真& b! Y$ E4 W9 c; N( K+ K
  21. 2 m( T: e( y1 b( D% k
  22. ;;; 作者信息
    ; l: {8 q6 s* y9 S; r5 S0 z
  23. (def-room about-room
    . _/ ?( g, m" ?2 W8 v9 K
  24.   "作者:redraiment( n1 N. D! ]9 B, M6 X$ k* f7 h
  25. 微博:http://weibo.com/redraiment/ ^# A6 S' \7 y$ ]& u. X4 I
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    . {. w" i( S! h+ i7 A) s& Q* _; e- @! \
  27. 请输入任意数字返回游戏大厅。"
    & i, j3 r2 E% N  x% H6 _4 m+ f- Q* b/ i
  28.   (t living-room))+ i/ A9 @1 \% a( T
  29. , g: L; u- V/ ?5 ~
  30. ;;; 教程' w* L% k% B; \. t+ _! j; l
  31. (defvar *wechat-5x5-tutorial-rooms* 0/ N# G5 g5 B1 E; P. Z$ @/ `5 Q- I* r* z
  32.   "The number of tutorial rooms")
    6 F" K! y3 z. y  H8 }# ]! z  I- U2 q
  33. - O& t. V' e8 v, j5 j
  34. ;;; 简化教程的定义
    ( y9 B% T. c2 H7 @5 O) P
  35. (defun string-last-line (content)
    ) Y# U; r1 n  @
  36.   "多行内容组成的字符串中的最后一行"
    + a1 y7 o, S% Y  Y
  37.   (with-temp-buffer; o8 E+ Y* k" b& _$ x& I. D
  38.     (insert content)
    9 l; F$ ?7 }) Y1 y* Q* q8 e
  39.     (buffer-substring (line-beginning-position)# i) t/ s2 u5 h
  40.                       (point-max))))
    $ W- Q2 F: P* t0 D

  41. # ~* b2 Z4 t( M6 F+ z. [% Y
  42. (defun def-tutorial-room (prompt)2 X& {; g+ h6 q% i" ~8 l; k
  43.   "根据提示语自动生成教程房间。8 J6 h) w- k: h7 _' o0 Q
  44. 4 B9 Q9 D- M2 _4 E/ m9 A& i+ z+ R
  45. 1. 提取最后一行作为问题;
    " b. T5 Y# x' ]+ R( ~* U
  46. 2. 分析问题,获取期望用户输入的内容;
    4 T& K. c: }3 e) Z6 g8 G2 y+ V
  47. 3. 定义教程房间和重复提问房间。") ?; ^! W4 K/ a+ e5 j) S
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    # o/ |' F; l1 N3 |! s1 \2 y4 p
  49.          (repeat-room (concat room-name "-repeat"))
    6 u8 l/ H, k" V0 W( N$ {1 q
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))4 \0 V8 s, w3 ]1 J
  51.          (question (string-last-line prompt))
    / u) _0 s6 }  @- Z! {5 \
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    1 m3 o" k+ W+ E0 p) q* h
  53.                      (match-string 1 question)))
    6 U5 P! l8 Z- r+ o# h# \
  54.          (doors (if except7 V' ~$ Q  o$ j( ?0 u( C
  55.                     `((,except ,(intern next-room))
    " _$ O! }4 l: R( {8 a& }4 S
  56.                       ("q" living-room)
    2 X  ~  {+ T2 X. g' T
  57.                       ("Q" living-room)
    ) b( h' X9 Y3 E5 C! b2 C
  58.                       (t ,(intern repeat-room)))
    : p6 a7 H, C3 \
  59.                   '((t living-room)))))
    ( Q! L2 o: B, Y  N1 a
  60.     (def-room-raw (intern room-name) prompt doors)
    " l& m9 Q: C& R( L5 J
  61.     (def-room-raw (intern repeat-room) question doors)))
    9 |' Z/ J1 I; E' c

  62. + }7 e/ W" F0 Q) d) o8 V* I3 N3 S
  63. (defun def-tutorial (&rest prompts)5 i' S$ L0 g7 ~( N
  64.   "批量生成教程房间。"
    9 P3 u0 m2 Q$ l% l6 s0 y2 P! v$ W
  65.   (dolist (prompt prompts)) t7 P9 H' y! Q4 ^1 o) U* z) ]
  66.     (def-tutorial-room prompt)))
    * U9 o! E( B1 T
  67. 9 ^' f" n1 m2 o8 r' x
  68. (def-tutorial( {, W6 S4 P; c0 ~8 I* h4 I" C9 |
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
      N) V0 D6 g+ ?  S
  70. 1. 教程
    ; }2 [% g  s( [2 H  U8 J
  71. 2. 入门(3x3)+ ?0 Q5 T* C' F9 h
  72. 3. 初级(5x5)- t# k$ a5 m  f" q" P) B
  73. 4. 中级(7x7)$ j1 ]/ C+ S. `7 |2 k7 i
  74. 5. 高级(9x9)) c: |8 u9 d, d0 g0 j
  75. 0. 关于作者; A3 t9 Q) y( P: `* n* ?2 m0 Y
  76. 请选择1-5开始新游戏:1 i/ n9 [7 I. ]. ?1 }0 N
  77. 您现在正在游戏大厅里。
    9 [! v, e# `& X- U
  78. 请输入“2”进入入门级房间"
    9 y+ v. a6 A1 I, ?
  79.   "  ①②③
    9 H1 w: [' i* V% x+ [% j" \* C' t2 |
  80. 1 ■ 
    1 i6 b" @$ I; e1 R! H8 W: W0 q
  81. 2■■■
    ! u, o- h  B$ C/ I( L
  82. 3 ■ * N# |& c& {& v5 Z" g  p/ {
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!3 ^3 g: ^. ~# K. o4 u  ]' D
  84. 请输入“22”来关闭第2行第2列的窗户。"
    5 I2 e2 V- b) S* g
  85.   "  ①②③
    . A  N; i7 s- ?
  86. 1   : g( y  w6 A: R
  87. 2   8 j& ]5 P) s  u3 g" j
  88. 3   ! @% m9 E1 {9 r/ H; ^+ T- x
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    $ W/ z) y! n6 B5 Q: T
  90. 请输入“11”,试着开启左上角的窗户。"
    7 J  ?/ G$ s4 T4 N  g" b- G
  91.   "  ①②③
    7 F! w5 `2 w' E: u4 P% T+ O
  92. 1■■ ! ?" t, N- q" a! S' y# W2 }
  93. 2■  9 }+ U% V0 \7 t' h  ]" B: E
  94. 3   
    & ~  p6 o; ^7 \' N
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    - Q; [8 j5 E! w) h# e
  96. 请输入“13”开启右上角的窗户。": J5 `/ a# a+ b- `, O- a) N+ U( b
  97.   "  ①②③0 J9 G5 b0 g! Q5 g
  98. 1■ ■
    + u8 _! x9 [& X& m* a
  99. 2■ ■
      Z8 c$ q3 O$ F8 R' v4 f
  100. 3   
    3 @  U$ G' @! k( s
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。4 c3 y2 ?! y0 l
  102. 请输入“31”开启左下角的窗户。"1 [: O! c. c+ d  @0 W& g+ b/ I/ ~
  103.   "  ①②③# X$ `8 C; w( T6 ]
  104. 1■ ■( [3 z3 O" H) \. g' o  L3 W
  105. 2  ■
    6 ?/ o' H+ P- h# J6 Z) i
  106. 3■■ ' _/ [; d8 \7 L& i% y! {
  107. 此时,总共有5扇窗户被开启了。5 O: z4 Q* I" u4 w( a
  108. 请输入“33”开启右下角的窗户。") t' s# G2 U0 \) g' Q# x+ |% W
  109.   "  ①②③
    ! M, H* z0 A6 r
  110. 1■ ■
    3 ~% w8 k: P6 ]
  111. 2   
    0 m& U6 o$ S. a4 U& W3 ?& h6 c
  112. 3■ ■
    2 B. _7 x: Q) f" Q" G8 V/ U4 R
  113. 现在,只有四个角落的窗户被打开。$ {9 l; T8 X& p
  114. 请输入“22”完成最后一击!"' r) U( W& R% W. z
  115.   "  ①②③
    3 Z! O5 V- m* M: _+ I1 [0 c
  116. 1■■■
    % z9 W% A1 Z# u; U
  117. 2■■■
    5 d3 N+ `# V$ R! E7 R! D( G
  118. 3■■■
    5 n% G6 f7 u; O
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    ' e2 f/ t& R* q( E
  120. 6 V# ^" U% x2 v2 g* w4 s4 r) L' X5 e
  121. ;;; 棋盘4 }  w' F8 P1 ^0 M% c
  122. (defconst *wechat-5x5-white-chess* 12288
    * e4 U: t2 t+ Z3 @/ {
  123.   " ")" U2 p( M0 U2 X6 g
  124. ! v7 {5 [0 g: `# ~9 ^
  125. (defconst *wechat-5x5-black-chess* 9632+ a; Q) g' ]# E6 A" n5 b, e% T
  126.   "■")  ]$ u& T. [7 T: g; s7 R
  127. ! W  K5 @5 F$ G, Q* h( E
  128. (defmacro with-board (&rest body); F& |, r; R0 h$ t% Y5 d( h8 U
  129.   `(with-temp-buffer# o% f5 g: w  \- A3 Z; C  W
  130.      (unwind-protect
    ! G+ W* h7 M" ^0 p; S$ l* T$ N
  131.          (progn5 G% e, }1 J9 m3 f
  132.            (if (session "board")! x: F: }" n- r" I
  133.                (insert (session "board")))2 B9 ?" V! p' W( z/ ]
  134.            ,@body)
    4 t& X' f2 H! E: Y  `
  135.        (session "board" (buffer-string)))))
    ) X: m( y- X+ n* Q, i

  136. 0 {4 K" n5 W7 l# |4 a
  137. (defun board-init (size)
    4 \- _; r2 D4 B, j2 d% g
  138.   (session "size" size)
    - H% u- K5 T+ h0 p$ V; k5 X/ L+ n' j
  139.   (session "step" 0)
    $ R, A" v9 u" ~' R
  140.   (erase-buffer)
    / |% W0 @( v5 o- ~* v
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    . P( v  `5 @1 K3 R- a/ p
  142.   (dotimes (row size)
    * Y2 I; }3 P) V3 z* H7 R' U
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))! R8 f3 o0 x7 e  n7 h3 F7 Y$ b3 Z

  144. ! C0 ^6 r+ l9 t) _& t) e
  145. (defun board-contains-p (y x)0 N5 `3 v$ Q" x. c, p
  146.   (let ((size (session "size")))& m- t  b# X8 I& s  C8 n
  147.     (and (<= 1 y) (<= y size)
    7 y) J0 E  q# I9 p7 e1 l4 K; N6 D
  148.          (<= 1 x) (<= x size))))' X2 R/ {& O2 h. I1 S3 u6 b
  149. + k. A6 ]+ I3 `% s8 y$ X% V
  150. (defun board-toggle (y x)
    ) \' [4 [# b  B1 }% A: U: B; Q1 o
  151.   (when (board-contains-p y x)
    ! D3 Z* ]) p0 o6 v9 q3 P
  152.     (goto-line (1+ y))0 y1 X* Q$ U& B. ?
  153.     (beginning-of-line)3 Y# M$ y4 \" v
  154.     (forward-char x)( \& R  ~# Z' ^4 Z7 ?+ Q/ K/ g# I; z
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char)): B! U2 G' b- q8 Q! B
  156.                 *wechat-5x5-black-chess*6 c+ }6 C8 C/ c  a/ M* m" `" Z
  157.               *wechat-5x5-white-chess*))- l8 e# Y2 g8 f, _+ ^3 e
  158.     (delete-char 1)))
    - V! }2 {* w, t1 h! H
  159. / |  J4 j/ ^- N& W* `
  160. (defun board-put (y x)
    , k5 q& W+ b2 j1 \& i& ?$ ?2 E$ |
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0))). t. z4 i2 E& R5 Z' V8 m
  162.     (board-toggle (+ y (first dir))% \. M3 c" b0 P; H
  163.                   (+ x (second dir)))))
    - M* H1 J2 m* f; m
  164. , S" y( O) C2 E
  165. (defun game-over-p ()# D" |4 g' r) K% `7 e( n
  166.   (beginning-of-buffer)  B3 g0 H+ I. _2 n
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t))): f% P/ F0 w" V) }! L5 L

  168. ! C' R$ F5 W! L
  169. (defun board-show ()
    1 u! J& F+ a9 I! U/ l
  170.   (with-board8 X4 e. Z  ?. ?# C3 [' j
  171.    (concat (buffer-string)5 O, |9 t$ r, T. h; }) m9 S# E
  172.            (if (game-over-p)% Y# ~1 ?6 X! O( k/ {- @
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    $ s/ }+ g# c! j2 ]3 H; B
  174.             (format "第%d步" (1+ (session "step")))))))& }. P% n: }/ ^7 s1 d% s
  175. & B! s* `3 b/ g
  176. (defun board-position-parse (cmd)) ]0 z0 l3 R/ o3 O- F0 q# R
  177.   (if (= (length cmd) 2)
    " {2 ]( P- p, c9 T' v- M; S$ z- s
  178.       (list (string-to-int (substring cmd 0 1))
    7 B% Q& M2 g) _2 _
  179.             (string-to-int (substring cmd 1 2)))
    $ c$ Z5 _3 m+ o8 U9 |) s% ^4 z
  180.     '(0 0)))
    8 T( ?! Z* O9 \) a

  181. ! N, }& W/ y/ A# j7 S5 V1 q
  182. ;;; 游戏房间" N5 q  u5 r0 W* F4 e5 t
  183. (defun game-room-init (cmd)
    8 Y& {% P- z9 W8 C% @2 o9 ]
  184.   (let* ((middle (string-to-int cmd))
    7 Z' A! D9 B- }4 }
  185.          (size (1- (* 2 middle))))2 ]0 A6 i+ ]# n( h: L) p  i; C
  186.     (with-board$ Y( w4 T) H* z0 j/ Y0 |9 k
  187.      (board-init size)$ V! j8 b- A/ K
  188.      (board-put middle middle)))1 B& s! C; F0 U2 K, s
  189.   'game-room)* k2 r5 O9 G6 V. T- h$ O, z9 \' Z! y

  190. , s2 x! Y1 `7 @/ j
  191. (def-room game-room* @$ D' p5 q/ a; ^6 N: h1 q; S
  192.   #'board-show
      ]% q$ X: i( e
  193.   (t (lambda (cmd)5 D2 ]$ V/ X1 J$ N! C' R
  194.          (with-board
    6 h% m6 U# I% C
  195.           (if (game-over-p)
    - f& }9 M8 \8 U( ?9 g
  196.               'living-room
    9 V& B+ U4 c3 ?1 I5 g: E" Z2 Q8 a
  197.             (destructuring-bind (y x) (board-position-parse cmd)3 V+ Y" q7 r# i4 W( h# Q# c
  198.               (when (board-contains-p y x)) }6 T( p1 V5 s* d- Y8 O% n
  199.                 (board-toggle y x)9 P0 a+ n/ w  v9 b
  200.                 (session "step" (1+ (session "step"))))9 [& s" k5 a8 B" G$ {
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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