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

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

  1. # L. }7 U0 r  f, V- ?" Q$ q6 M
  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;">;; 定义新的游戏地图7 c* _( i% p# \
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    7 n' q! C% }3 D6 N, a9 A
  4.   'tutorial-room-0)                     ; 默认的入口# t/ y( {) o, a& h( j. I. G

  5. 4 D! O, E0 `8 a$ q; y
  6. ;;; 游戏大厅
    3 S: Q, Z! c- ^' U9 g5 Z( m! \
  7. (def-room living-room
    3 z; l* M+ `, d: w* O( L7 H& A- [
  8.   ;; 进入该房间后的提示语: l6 v3 L% I( Q$ W$ }
  9.   "1. 教程+ O5 v: e1 m) J
  10. 2. 入门(3x3)
    1 b  a0 W* o6 F% q" U/ p/ t) Y
  11. 3. 初级(5x5)
    7 L' A/ b) e7 {6 h  k" d2 I
  12. 4. 中级(7x7)
    7 {7 U9 D& m- x- }" M
  13. 5. 高级(9x9)
    4 _0 o. Y% S: ^  Y
  14. 0. 关于作者  I. l! x" Z6 N/ b" R4 G. W
  15. 请选择1-5开始新游戏:"  t" ?5 P! w# w
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名9 Q- k# k. _! Q8 M4 ?
  17.   ("1" tutorial-room-0)
      U. V: F; q0 \5 z* J
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配2 L9 X% @2 }0 U$ U! E
  19.                                         ; 相应的返回也可以为函数,动态返回房间名4 X0 y7 @7 q( n7 r, v
  20.   (t living-room))                      ; 如果条件为t,为永真. w# x) U' m; f: n

  21. # ^5 ~" O9 M. |
  22. ;;; 作者信息7 m, m. ?# N$ Z
  23. (def-room about-room
    / \% `5 k6 H9 \3 H- s9 I7 ]
  24.   "作者:redraiment
    / G+ r' I5 i& ]  B) g* p' h
  25. 微博:http://weibo.com/redraiment
    5 [" k7 L; J6 Y& L9 d+ u% h
  26. 有任何建议,欢迎在微博或微信上联系redraiment。3 J* \# q% j4 {) n4 u. E
  27. 请输入任意数字返回游戏大厅。"4 Q4 E; I' `$ K$ ~/ ~
  28.   (t living-room))
    / h/ A4 g/ T- I% U( V/ }: {

  29. 2 x7 ?5 B  w% o, k  `( v" p
  30. ;;; 教程" s+ I/ v/ x, F
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    ( {0 k/ O; v4 A
  32.   "The number of tutorial rooms")# _- J6 @0 z) y1 l
  33. $ ]7 ^" ~/ a& O! c4 |* H/ I2 |
  34. ;;; 简化教程的定义9 f2 Z' C7 i* e- C7 C  H
  35. (defun string-last-line (content)
    ' G0 J1 q  `) v7 R. Y) r9 W+ w& f
  36.   "多行内容组成的字符串中的最后一行"8 N" K+ p% ?$ Y7 J) x. e
  37.   (with-temp-buffer
    4 F7 ~) G# n7 j2 M/ c
  38.     (insert content)
      D! F0 T8 X, B/ o
  39.     (buffer-substring (line-beginning-position)" T/ a* y# ^! ]5 {: Q& ^
  40.                       (point-max))))
    # r5 r, C# G& u( q; E
  41. ' r5 J; `  B& N' j7 [6 [
  42. (defun def-tutorial-room (prompt)
    " S+ Q( a0 j* D+ x0 v
  43.   "根据提示语自动生成教程房间。
    3 `" r! G3 c" o- D7 l2 O- [7 t& R7 N' t

  44. , k0 [; H3 w% h- N
  45. 1. 提取最后一行作为问题;1 {# T6 ~4 z, S! A# L2 T; m8 s
  46. 2. 分析问题,获取期望用户输入的内容;  a+ I8 A+ @( y- H2 z7 F
  47. 3. 定义教程房间和重复提问房间。"; W0 y5 o( x2 M1 N5 C
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    / ?- p/ @) o6 d5 n6 Z8 p3 t% _4 N
  49.          (repeat-room (concat room-name "-repeat")); e4 m8 W, p- U  [1 ]# `0 P' O4 M7 W
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))" x& R2 }% Z3 j2 Z; O
  51.          (question (string-last-line prompt)). C. o  G! a' a
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)" [1 s5 u. `* K; f* G
  53.                      (match-string 1 question))). Y) V. M4 K+ r6 |
  54.          (doors (if except
    # H1 H6 \: F  B; a& O$ m
  55.                     `((,except ,(intern next-room)). r+ j! t5 W6 m0 g- L) B9 Q- Z
  56.                       ("q" living-room)" B$ L. o( g0 M- L+ s, e0 N" i* D) U& D
  57.                       ("Q" living-room)
    4 i7 m5 D, w5 E  x! Z5 S3 y
  58.                       (t ,(intern repeat-room))); D; {1 ^/ q6 j/ B: D" C
  59.                   '((t living-room)))))6 a$ x. J' |; x: x
  60.     (def-room-raw (intern room-name) prompt doors)
    ) m: H8 S% A7 t+ I0 i
  61.     (def-room-raw (intern repeat-room) question doors)))
    . ~4 X% r( ^4 K; m. E3 q" }3 D8 ?+ |

  62. * _1 h( r+ r% P
  63. (defun def-tutorial (&rest prompts)# |, f4 A2 x1 h* t5 \% K
  64.   "批量生成教程房间。"
    % G7 l: K/ Y5 o: P. A
  65.   (dolist (prompt prompts)) R! n2 _2 Q: a7 j) F7 Q
  66.     (def-tutorial-room prompt)))- i* \5 q  l* A# Q5 J% p
  67. ( `. N% ?* |/ }6 \( U6 x) M
  68. (def-tutorial" n; g; E$ D6 K/ {' o5 F, {
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。% L' R( @3 z3 S
  70. 1. 教程
    ) h8 Q) l( d; ~2 a- }
  71. 2. 入门(3x3)  k+ n% j! J/ J( @4 e& `
  72. 3. 初级(5x5)
    + O. ?) h; Y5 p2 ^, {; K- Q6 |6 S! B
  73. 4. 中级(7x7)
    ( k9 Q) {1 I0 W) l
  74. 5. 高级(9x9)+ a% e: x  H$ \! j$ p1 o( i2 V
  75. 0. 关于作者+ q4 U6 Y1 D+ B* j0 S& l) Z! g( a
  76. 请选择1-5开始新游戏:
    - G6 V: S! Y- F6 t
  77. 您现在正在游戏大厅里。
    ; L& w1 ~1 S8 g, f) i, n* M
  78. 请输入“2”进入入门级房间"
    ) `' ]% R  r) G5 _5 L1 X
  79.   "  ①②③% J" C  U9 D6 Q) v
  80. 1 ■ / h3 w4 c! E, b$ {
  81. 2■■■
    ) x. ^# Z/ ]- x; B0 p" q
  82. 3 ■ 5 H7 W3 j5 F6 B, [
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    7 ~! z6 N4 F+ }& ~2 {
  84. 请输入“22”来关闭第2行第2列的窗户。"
    ' o8 Y, v, [* \& y- r7 ^4 i5 {" c
  85.   "  ①②③( V" Z9 y- T2 {( z' @# Q; v
  86. 1   
    , i1 m# y# z* n3 c1 L" @: r
  87. 2   0 I8 M% Y; ^1 b
  88. 3   " K, d' ~) z0 C. t* E
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。& H* l7 k+ d! O7 V* ^, K
  90. 请输入“11”,试着开启左上角的窗户。"
    ! j, O6 P+ S2 j) r1 P- A
  91.   "  ①②③4 _3 k4 x( _0 V1 e% B0 ^0 L
  92. 1■■ , [$ e8 C& f; U) N# D
  93. 2■  
    , h" N  X7 x1 s
  94. 3   - n. S5 a; T% v' I. _) N% K
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    # m+ {8 X+ G# j7 t
  96. 请输入“13”开启右上角的窗户。"
    4 x" u( ~* i8 `) k" E, \
  97.   "  ①②③0 p& U6 N* b1 _2 F; ]. y9 T
  98. 1■ ■/ P* S1 a. d' n5 f2 R  J. a$ |% F
  99. 2■ ■
    7 M2 o, {. h: z$ S: S* ?$ s: C
  100. 3   6 A% v8 @" G+ g
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    1 Q: U9 m  J: R) i
  102. 请输入“31”开启左下角的窗户。"
    ) S+ G; b/ {* e
  103.   "  ①②③
    1 J% ^% E5 I4 O  J1 D  X4 i
  104. 1■ ■
    , ?) I- a) H6 X* Z# Q8 D
  105. 2  ■1 i+ S6 U! R3 {9 U
  106. 3■■ 
    4 k4 x/ I, R) b7 u2 ?1 H; Y
  107. 此时,总共有5扇窗户被开启了。
    * c( f. Y" `7 B7 W5 v* |
  108. 请输入“33”开启右下角的窗户。"
    8 L! y; ?( S0 Q: z1 K
  109.   "  ①②③0 E2 p5 J3 @' k: O- |9 ^/ c
  110. 1■ ■8 T7 r( y/ F' U
  111. 2   
    " |7 [7 z9 R3 t, a0 N4 a
  112. 3■ ■6 y' }; ?1 D0 b. S- ]
  113. 现在,只有四个角落的窗户被打开。
    0 K- m. a3 h! v
  114. 请输入“22”完成最后一击!"# `8 h7 }5 h4 M2 R
  115.   "  ①②③
    ! b& F+ v1 h( r% f; ]
  116. 1■■■, q# h! B  |; \* z% e/ |8 @6 @3 c
  117. 2■■■
    : e+ o1 H- p" V' [3 I
  118. 3■■■
    - m7 P+ j: J: e  o" S4 F
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    & B0 ?( Z( \% o; k5 ]6 S
  120. / i( A% `$ m5 s$ p2 e: m, v
  121. ;;; 棋盘4 y5 `8 {) N6 g
  122. (defconst *wechat-5x5-white-chess* 12288
    $ `# U7 W$ x8 J1 {& G
  123.   " ")3 a1 s8 K3 ]4 D4 I
  124. " }. ]/ e" u" C0 G: L# Z, \
  125. (defconst *wechat-5x5-black-chess* 9632
    ! J9 ?5 z! L* S2 m, Q5 `4 H
  126.   "■")
    . X- o  }+ N+ U: U
  127. " N/ e; ?' Y) r8 I, p  @* I! Q/ l
  128. (defmacro with-board (&rest body)! F; K5 m9 u5 s6 r# U% i" [
  129.   `(with-temp-buffer
      H0 P; e7 V# W/ S
  130.      (unwind-protect( z$ m' m6 n$ Y! o: Z
  131.          (progn
    . V  T* T# D0 k; d1 {2 Q5 e
  132.            (if (session "board")
    4 b- ~. p, `( w, O
  133.                (insert (session "board")))
    7 t% z! [# u% T( E+ Q' d  M2 W, f
  134.            ,@body)
    : B  `3 m1 u9 D! e8 @9 Q- v
  135.        (session "board" (buffer-string)))))
    ( i3 C  G- l3 U2 s7 \; U
  136. 9 \3 D* m, c& Z" j, `
  137. (defun board-init (size). b! O0 s& E* v) Z" M$ P
  138.   (session "size" size)+ ]  L; E* S; i1 Q
  139.   (session "step" 0)
    - S! q2 ?. Z: R$ d! {
  140.   (erase-buffer)  s+ _- \8 u; I& Y# y
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))% ~1 M4 t& @* J; |  {/ n4 \
  142.   (dotimes (row size)
    3 k) J/ C9 e9 F" X5 n' D
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))5 ]5 v+ U6 z( p; v* W( r$ H

  144. 7 r, `, o6 b/ R# a' r1 W
  145. (defun board-contains-p (y x)
    0 j  Z7 F! b$ [' I4 Z; d7 J4 U5 C! D
  146.   (let ((size (session "size"))); B7 {# p0 W# h1 {4 i5 t
  147.     (and (<= 1 y) (<= y size)
    9 k1 k( L& p/ J6 {8 G6 |
  148.          (<= 1 x) (<= x size))))# E# z! M7 |1 g& N9 N6 [

  149. 1 d* P. W0 H. Z' V
  150. (defun board-toggle (y x)
    6 d+ o6 F7 [$ o+ A/ F, w% x: c6 e
  151.   (when (board-contains-p y x)
    : r* k% P% F- a* ?* s4 w9 X7 f; @  ~
  152.     (goto-line (1+ y))3 ~# e# K/ b0 U9 J5 U  N
  153.     (beginning-of-line)0 F& l* N4 x2 u3 Y7 Q( {5 x% r" ^+ E
  154.     (forward-char x); s/ z: N( K/ {- `, `
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))8 P; h* z8 s, u! c4 M
  156.                 *wechat-5x5-black-chess*4 H" t+ R2 o3 t3 C! N
  157.               *wechat-5x5-white-chess*))
    3 S: }" p& a0 K1 C
  158.     (delete-char 1))): a3 G" o9 n( i8 B9 V
  159. 0 }# D8 p/ G/ Z8 l" b$ Y" L0 J
  160. (defun board-put (y x)# v' O8 }# w+ t  K
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))! s) ~9 d4 u1 T# I
  162.     (board-toggle (+ y (first dir)), }. H5 A+ |$ Y- @$ q
  163.                   (+ x (second dir)))))
    . t  t9 T" _. y& I# d

  164. # Q9 e" X# }! }% [# T* T8 w5 M: j! n: @
  165. (defun game-over-p ()
    9 S3 Q) u$ f- h  f# K( P
  166.   (beginning-of-buffer)! n6 C7 T: `' z3 @
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    ; M8 Z1 w- E; I" i" V

  168. 1 b5 N5 N3 A$ J) J4 k
  169. (defun board-show ()% N& [8 h0 ]! ~- Q/ ]
  170.   (with-board4 F* d' x7 p* s9 C+ K+ z
  171.    (concat (buffer-string)
    . O  e4 x5 n" X0 E
  172.            (if (game-over-p)
    $ m2 l9 b' |0 i
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))! b9 K" Q% _9 z' ]+ m' A. W/ ~
  174.             (format "第%d步" (1+ (session "step")))))))
    1 o1 g/ A2 h7 d; ~2 r, w- z; K

  175. $ d0 D8 P4 o% z
  176. (defun board-position-parse (cmd)" O/ k0 f4 r8 [, Y
  177.   (if (= (length cmd) 2)
    ! q. [6 s- ~+ ~) @1 M
  178.       (list (string-to-int (substring cmd 0 1))6 h! }( J6 }, q$ K0 C
  179.             (string-to-int (substring cmd 1 2)))  y- j" I) R3 a! V
  180.     '(0 0)))# Y' Z: S. F# I: b6 L: h# [

  181. 3 v; Z" L* \. t8 V
  182. ;;; 游戏房间
    0 o9 m! f' B2 M" e
  183. (defun game-room-init (cmd)1 f% V: q5 s2 J4 P/ T* n' O# A# E
  184.   (let* ((middle (string-to-int cmd))
    * H: B( I4 g5 A, A5 A: T( I
  185.          (size (1- (* 2 middle))))
    / M: b! I# |4 t  N3 `. H
  186.     (with-board
    7 l4 w7 A4 i/ w4 A0 W6 D( x9 M4 \
  187.      (board-init size)2 u  E4 q+ P* D- f, [- P6 z
  188.      (board-put middle middle)))
      [+ V  e; j& |3 v
  189.   'game-room)6 y1 N, p" o# `6 b: o
  190. * c9 G6 j1 S; o. V5 U$ z9 d
  191. (def-room game-room
    3 J8 _; g. i% [+ ^0 C
  192.   #'board-show
    , K0 e: `  B' n' l* J
  193.   (t (lambda (cmd)
    0 X$ e3 F6 e& T# V9 r# W! L+ }
  194.          (with-board
    ' k) t- M4 {  O8 N' y
  195.           (if (game-over-p)
    / \% @9 s' r$ {5 ~" [
  196.               'living-room
    % f8 c8 a) P" F5 G% r( I
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    $ R$ Q6 @  [; T+ w$ M2 s
  198.               (when (board-contains-p y x)
    7 {  z  c9 p4 {" I+ P# U" S! q8 s
  199.                 (board-toggle y x)
    ' n# y$ G) ]% Q5 C+ g! |
  200.                 (session "step" (1+ (session "step"))))
    % t: O6 w  W5 d! [5 {- b
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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