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

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

  1. ; N- ~4 ~" p8 r  h5 X3 U
  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;">;; 定义新的游戏地图
    ; l+ x1 ]3 m; e4 Q! {* O
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    , |- H+ t2 p0 _/ G/ r/ T
  4.   'tutorial-room-0)                     ; 默认的入口9 u: {1 h: q) O6 N+ B9 i, h) I
  5. ) r; G$ ~+ Z; q+ B$ D. w! ]
  6. ;;; 游戏大厅
    / {' P, T% A6 a* {' C' u
  7. (def-room living-room
    7 N7 z( b& }% q1 o) U
  8.   ;; 进入该房间后的提示语; e8 s0 M: [8 O
  9.   "1. 教程
    1 \4 b3 H3 s  F- z. S0 R
  10. 2. 入门(3x3)- X( E" H* d* ]' |( T# B. p
  11. 3. 初级(5x5)4 `9 V% u" b' b/ d0 c
  12. 4. 中级(7x7), q- T" U1 `$ ^8 Q: o0 ]: u! Y  R7 k' E
  13. 5. 高级(9x9)5 E2 J; J- i- ~1 v
  14. 0. 关于作者
    8 l  @& [; x; d) z
  15. 请选择1-5开始新游戏:"
    $ O) ]- |  B2 t: X. t( w' y
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名' s& j$ w$ J- U2 l) B
  17.   ("1" tutorial-room-0)/ u3 }3 q2 y" ~
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    1 L$ m. G8 S/ B: g
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    5 z) P$ [' R% @2 K( V
  20.   (t living-room))                      ; 如果条件为t,为永真
    + p& c9 `+ R0 n4 O. q$ u& A8 h5 {

  21. ) p1 Q! _- ]$ v
  22. ;;; 作者信息
    9 E: K) k' D: Y% p
  23. (def-room about-room
    " X9 b* b* \1 I$ l$ ^
  24.   "作者:redraiment/ n* J* _: d- n0 V: ^; a8 K7 W$ i- V
  25. 微博:http://weibo.com/redraiment
    9 J3 b/ d3 I# L3 `
  26. 有任何建议,欢迎在微博或微信上联系redraiment。
    , e" I  W6 o, c: I- l) J
  27. 请输入任意数字返回游戏大厅。"
    0 C, D; [! {' I, C
  28.   (t living-room))
    $ q* o" [) l) h
  29. ' M0 Q! D8 n$ |$ e- B+ {
  30. ;;; 教程( i; R  z! Y' r) f
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    ' s" M2 e: h4 H7 s) f3 }
  32.   "The number of tutorial rooms")9 d7 f( N+ [% v7 l3 q. Y, ^2 j3 w  p

  33. 7 Y  q& U' y5 ~# l2 t; m
  34. ;;; 简化教程的定义
    + u0 b( z* c; k& Q5 i
  35. (defun string-last-line (content)
    : }. s2 U" z2 Q9 t0 |4 t: N
  36.   "多行内容组成的字符串中的最后一行"2 j$ Z. f$ m: _) r) W) ~
  37.   (with-temp-buffer3 }1 @0 a4 R) Q1 q; F
  38.     (insert content)/ O- O  t3 q! _4 X# j
  39.     (buffer-substring (line-beginning-position)! S$ E) ]8 J% m2 \; j; e
  40.                       (point-max))))  b/ {* f4 y$ O: m6 X4 \# z! M9 \/ }( E

  41. 5 P( R) m2 o  X" H( R+ p' F/ V5 N7 f8 @5 u
  42. (defun def-tutorial-room (prompt), s9 v+ C4 H" v
  43.   "根据提示语自动生成教程房间。/ K5 Y( j$ F5 z1 F, L7 h+ o
  44. 4 Z+ Q$ [3 g) S3 Q
  45. 1. 提取最后一行作为问题;+ q0 c: g7 \& ]' M
  46. 2. 分析问题,获取期望用户输入的内容;$ s. C- L: I& [; }
  47. 3. 定义教程房间和重复提问房间。"
    3 F; P: Y- S, O: |7 t$ g% p
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))( Q- N0 i5 Z. l& O
  49.          (repeat-room (concat room-name "-repeat"))
    " R# n4 z4 a0 ~' W
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*)))); Y0 E+ G8 q; ~  ~+ _" m% ^
  51.          (question (string-last-line prompt))
    & S) k  e/ ?- O# e+ J0 P
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)& `+ b2 L  d4 Q4 ~- C& [
  53.                      (match-string 1 question)))7 J* y3 P, i6 H
  54.          (doors (if except4 o# i1 ^! ~* n
  55.                     `((,except ,(intern next-room))
      C3 J+ G' c! B: c7 \
  56.                       ("q" living-room)
    $ J% s' _2 l! h3 z7 B% H+ t% v
  57.                       ("Q" living-room)/ @; g* U% p4 L; \$ [- T
  58.                       (t ,(intern repeat-room)))
    + I  c5 H8 s$ L
  59.                   '((t living-room)))))4 |8 x; {& [8 c- n
  60.     (def-room-raw (intern room-name) prompt doors)0 d* r) @$ l4 R
  61.     (def-room-raw (intern repeat-room) question doors)))
    % J& e6 K' i2 u( B+ q
  62. 8 S; |3 t$ [, Q4 ~9 O2 P
  63. (defun def-tutorial (&rest prompts)5 ~+ w  b# _5 J, q  C9 G) \$ T
  64.   "批量生成教程房间。"
    1 m; |6 Z2 G* S+ q% B8 I/ x( V2 {
  65.   (dolist (prompt prompts)  M3 J5 J. R( I& q, K- w
  66.     (def-tutorial-room prompt)))7 V& b9 ^9 W  f0 c0 o
  67. % l& q$ L$ Z% Y( r8 g/ _/ O
  68. (def-tutorial" ~4 z7 a& p% D3 L4 Q7 k% U
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。& @, D4 w% K  B& j
  70. 1. 教程0 U% [  \7 M. P+ R6 \/ b
  71. 2. 入门(3x3): M& Y' G' Y' i/ F
  72. 3. 初级(5x5)
    - x% ?) z' E% Y. E: c
  73. 4. 中级(7x7)
    $ X2 O" o) g; A
  74. 5. 高级(9x9)
    7 H$ v, [- ~; v4 C8 e4 V" X* n2 n
  75. 0. 关于作者
    6 `9 C- |: n& a" p7 i6 C& r% d
  76. 请选择1-5开始新游戏:
    7 v2 O6 R$ H" q& ?  S2 s
  77. 您现在正在游戏大厅里。' D: h$ y$ _" h) B* Y
  78. 请输入“2”进入入门级房间"  K$ X7 D/ N' e* i
  79.   "  ①②③
    5 U6 q& q: y- x- J7 \& x
  80. 1 ■ 
    4 ^. T1 f7 I$ H& I
  81. 2■■■7 T9 \$ M+ V! X0 o  O4 P
  82. 3 ■ # [5 I! X7 O7 x0 I
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!- z" H4 M4 u4 O" M0 l
  84. 请输入“22”来关闭第2行第2列的窗户。"! z; }6 L* R6 }" @0 v: Z) `$ J% h
  85.   "  ①②③
    5 ?- D3 N+ R. }; u  Z+ g
  86. 1   4 c, W7 Y2 E/ s5 m8 v
  87. 2   
    , }( d7 |  w# p  V* ^" O1 ]
  88. 3   ' G( n  _) A2 b6 x
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    ' a: |' L1 h' y4 D
  90. 请输入“11”,试着开启左上角的窗户。"2 K6 i" Y! j7 ?. b
  91.   "  ①②③$ Q! R* {( l# k1 n5 P- ^
  92. 1■■ 
    7 T# L9 E/ q5 z6 {. ]* P
  93. 2■  2 L: E: m6 t: Q% I/ L$ g$ \
  94. 3   
    / K- C+ g; N8 ?7 u$ M% `5 F
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    ( D1 H6 k3 s9 q% j  i1 \
  96. 请输入“13”开启右上角的窗户。"
    ; [4 v0 V! [5 U2 {5 ?1 S
  97.   "  ①②③4 E* ]2 I$ G$ a$ u
  98. 1■ ■
    % `5 D6 I" \+ F. Z2 U+ }
  99. 2■ ■2 [4 \4 M* z0 P" B2 |0 R; e
  100. 3   
    ' n9 M1 p8 e* F3 d% N) T
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    + J4 v6 F& J9 o
  102. 请输入“31”开启左下角的窗户。"
    3 V* L) f% f  A9 q* ?
  103.   "  ①②③
    2 o9 s; d, _" W8 E1 [0 U
  104. 1■ ■
    " H% C4 R3 w6 Y9 W* H7 G
  105. 2  ■. {, T+ @' U- Q0 V) |7 a# K+ W# W
  106. 3■■ + o4 c4 o8 V9 m% {0 V' E- H
  107. 此时,总共有5扇窗户被开启了。
    # L. n1 C2 D, P& M) ]- C
  108. 请输入“33”开启右下角的窗户。": p5 M0 V% r7 }
  109.   "  ①②③
    7 h2 m! e# T  `: n$ f
  110. 1■ ■7 P/ T: c7 e5 s: l. Y3 n
  111. 2   - L, B: _) c! z
  112. 3■ ■
      m, {( P4 q& J" G. T0 U4 [
  113. 现在,只有四个角落的窗户被打开。
    $ f  f! G2 @% z
  114. 请输入“22”完成最后一击!", E$ g" t& g2 t" G4 P9 ?: l
  115.   "  ①②③
    - {+ v- d; W" O2 o# t3 A& H9 J0 _
  116. 1■■■
    # O6 O+ J5 S4 u; m
  117. 2■■■
    % s: Y+ K# g' S5 ^$ T
  118. 3■■■6 ?  j1 Z. l% o' u
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    , G; W, Y, ~, y8 f$ H* j' h
  120. & `3 L2 X( J! H& n
  121. ;;; 棋盘
      ?8 L6 C$ S2 C2 i1 M/ P
  122. (defconst *wechat-5x5-white-chess* 12288; D- ~5 b& J2 N/ J( |! f  o% E
  123.   " ")
    ) y9 |# l8 E2 {. M$ R; B& s, N

  124. 3 m. |) l1 Y3 A  y, K  b# A4 ]% A
  125. (defconst *wechat-5x5-black-chess* 9632/ m$ X$ g2 X& C% @6 ?
  126.   "■")
    & u$ A, T) d4 P( `

  127. & c6 ~1 A& m, ]6 B8 _
  128. (defmacro with-board (&rest body)
    / M6 `$ U! ]* s* [: o9 z* O6 t
  129.   `(with-temp-buffer
    8 T* x/ [+ }  Q& t$ {
  130.      (unwind-protect
    % ?; m% q5 P8 J, b3 \+ o9 Q; s
  131.          (progn
    - L  y: f7 U" i. z
  132.            (if (session "board")
    / b- i. M  V+ C* y4 p! Q
  133.                (insert (session "board")))1 u1 M1 q8 k& _) k8 k0 Q
  134.            ,@body)
    6 J' V9 |  v/ e8 W- j' b
  135.        (session "board" (buffer-string)))))1 R2 z  M* ^" `, c$ K) t
  136. % \% ]9 ]. S9 g
  137. (defun board-init (size)8 S5 s3 K/ R) I0 O6 P  {
  138.   (session "size" size)
    - _4 D3 S6 [, p7 |4 l$ [3 e
  139.   (session "step" 0). n8 P1 d- z; a! C+ h) P( S" _' f- q( X
  140.   (erase-buffer)
    # d1 J% D; m1 i0 B4 J6 s
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))( `2 d6 f5 I+ ~3 ~# M7 R7 P6 E' }
  142.   (dotimes (row size)/ N- d4 t8 L, S9 G
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    6 A6 r' c( s0 p+ R: u% Z& X

  144. $ `! P, f' d# z5 b2 k6 y5 ^
  145. (defun board-contains-p (y x)
    2 h$ W9 {* y# R, M( M: {/ U- a
  146.   (let ((size (session "size")))" Z" D: {+ c+ \
  147.     (and (<= 1 y) (<= y size)  c8 g0 i; t- G3 b
  148.          (<= 1 x) (<= x size))))4 {/ D, d+ Q* G2 U
  149. $ W& g: v, }# z- U- x' K) K( k3 U
  150. (defun board-toggle (y x)0 L+ P' R/ ?' ]$ o3 A4 n
  151.   (when (board-contains-p y x)
    4 Q# H/ g, ~& d+ o: p
  152.     (goto-line (1+ y))+ w& p7 w6 x8 Q1 ?& \* `  \
  153.     (beginning-of-line)
    1 L/ l2 g0 E* w) q
  154.     (forward-char x); N8 P8 y2 _5 j9 }- n3 @
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    . `/ K! u) V- v) Z1 \& Q$ |) ]" ~
  156.                 *wechat-5x5-black-chess*
    # k5 ?3 v1 V" |% D* f& f) V
  157.               *wechat-5x5-white-chess*))
    9 \( _3 N) a9 C2 E
  158.     (delete-char 1)))) E8 E0 g! c/ K3 R9 m
  159. 0 i4 B# m! t( ~% E- J" Q
  160. (defun board-put (y x), X5 A- }% ?; S9 W3 R) t
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    ' t8 |" b% S& N/ T
  162.     (board-toggle (+ y (first dir))2 b3 z/ j: [% p: c, Y6 G7 N% ]4 Y
  163.                   (+ x (second dir)))))& q: F4 @/ U" k2 y7 i% j- t: _8 k

  164. / E, h( G$ t% l) q' y
  165. (defun game-over-p ()
    " l7 F9 {5 l  Q
  166.   (beginning-of-buffer)
    ; ^$ ?  H; Z; j
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))8 K) [- y2 ?8 R4 b  C1 n7 A  Q

  168. 2 n; D3 q, Y: E  e) ~8 X3 j
  169. (defun board-show ()
    * T% N+ T6 K: ~" q8 L; ^$ b4 l
  170.   (with-board
    9 r% a3 K4 ]% H
  171.    (concat (buffer-string)
    9 w; Q. P. z& i
  172.            (if (game-over-p)
    + j" {, h; r2 E/ @. k7 E
  173.               (format "共%d步,输入任意内容返回大厅" (session "step")): b/ |) I  w- h* j# h
  174.             (format "第%d步" (1+ (session "step")))))))  @" z0 c3 g' Y& W7 i( `

  175. 4 _* t% K1 n7 W
  176. (defun board-position-parse (cmd)
    " G% j' \1 R, P' z! s& ]
  177.   (if (= (length cmd) 2)
    8 n0 \# H. E. H" ^% I
  178.       (list (string-to-int (substring cmd 0 1))5 K; }" i7 E5 b0 ?
  179.             (string-to-int (substring cmd 1 2)))
    ' q8 M8 u% e% ~/ [
  180.     '(0 0)))" k! b/ t* B& h2 Z
  181. 2 ?$ T1 d9 x/ Z$ _7 _( o6 B( |
  182. ;;; 游戏房间7 e& t( ?1 h0 H. ^  m8 i
  183. (defun game-room-init (cmd). x) y; T. r9 p: P" ?7 t$ D
  184.   (let* ((middle (string-to-int cmd))* ?5 E2 e0 I0 L1 n5 \
  185.          (size (1- (* 2 middle))))
    - v2 E! H( F5 Z$ U0 `5 Z" r
  186.     (with-board3 X/ h5 ]3 M1 i$ c; ^" G- {
  187.      (board-init size)& [, z$ G& M2 t, I) b* b
  188.      (board-put middle middle)))" w  ]; B% ^4 L- Z2 e5 ]8 D
  189.   'game-room)
    * N. t( ]8 ^0 r6 q7 }9 \
  190. 0 @6 {2 k, F9 ]# T7 i% N9 A+ D
  191. (def-room game-room
    4 {8 H# o! o( t. {# o, v
  192.   #'board-show
    : F# g+ ^- `/ ~3 Q) l# F  p6 v" C& P
  193.   (t (lambda (cmd)5 s2 F9 c! R% ~% @$ F1 H
  194.          (with-board, ?8 c& O/ C1 ^  t- n  e
  195.           (if (game-over-p)
    . n* V+ Z0 ~: ]- t0 n$ T
  196.               'living-room+ h; g* m* Z$ }* ~' {- B
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    0 ~0 ]$ E9 C! l$ U  j
  198.               (when (board-contains-p y x)
    % j' p5 n! P3 A/ P
  199.                 (board-toggle y x)
    ( Q% Q- W! E" t! G3 j( r) g/ |; f
  200.                 (session "step" (1+ (session "step"))))3 ?  z7 p. N6 O: }% P
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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