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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
' ^& _# T+ h' o" X' A% d8 y7 z% Y借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. 6 T5 _2 }; u7 x9 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;">;; 定义新的游戏地图: O( ]! m$ V/ z& P- k
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL8 @8 u6 {2 r$ k
  4.   'tutorial-room-0)                     ; 默认的入口
    & ]9 i- k2 X* q' D. n3 j7 N$ f

  5.   b: `8 D$ c: H
  6. ;;; 游戏大厅
    & t( g0 i7 _/ i% N: Q" \
  7. (def-room living-room; z# M7 S. b& n: x$ g# A
  8.   ;; 进入该房间后的提示语
    ' ]" Z2 Y6 x! Y4 s, k. J. K7 r
  9.   "1. 教程
    ) G# E7 E/ R+ H  m* H
  10. 2. 入门(3x3)8 S( w/ q# c% i4 n5 F0 {
  11. 3. 初级(5x5)
    ; z$ [6 j, C+ o6 y
  12. 4. 中级(7x7)
    . t; x/ b8 ?& I, a; o
  13. 5. 高级(9x9)' V0 I: L6 ~$ g/ J, {8 E& l
  14. 0. 关于作者
    : @( F1 ?) z' h( }/ R4 B
  15. 请选择1-5开始新游戏:"
    , @: f1 K9 ]# M8 i6 G
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    + o, v3 r8 e8 g8 t, o  F$ \# F
  17.   ("1" tutorial-room-0)) G  H* z) N1 I8 g6 g5 r, m. {' B
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配; X* |4 M8 G+ c) I
  19.                                         ; 相应的返回也可以为函数,动态返回房间名$ l0 q- [. |  g2 x( B
  20.   (t living-room))                      ; 如果条件为t,为永真5 J0 d' \- S( ^" P+ i

  21. & W0 s8 o! A: [$ Q! i
  22. ;;; 作者信息% {$ Z' f/ X4 K" s6 Q' K
  23. (def-room about-room
    % U5 v, _5 a9 K5 v8 J% j9 o% L) M
  24.   "作者:redraiment
    & T5 a6 k& i( L6 B- E8 S5 [
  25. 微博:http://weibo.com/redraiment
    1 W8 l. \7 j" {* Q( A- ]: P0 }
  26. 有任何建议,欢迎在微博或微信上联系redraiment。) I! V$ x& H; J. {) c
  27. 请输入任意数字返回游戏大厅。"& ?# O( L, B. j
  28.   (t living-room))
    ' ^4 x. o0 v) }7 o7 a

  29. : c, P6 ~( B) v" @7 b; S/ V/ I; \
  30. ;;; 教程
    : R. J- A# e6 F6 Z; Z
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    8 ?% H/ j( |, M, v8 k3 f# W0 d
  32.   "The number of tutorial rooms")* y3 q+ l' H* {# i8 q6 ]

  33. , g) L5 }4 D. r' L! _2 A
  34. ;;; 简化教程的定义, Q( D% @2 K& j+ B
  35. (defun string-last-line (content)
    / r2 B' N. \  S7 d. b- G9 N. j( t
  36.   "多行内容组成的字符串中的最后一行"# l* G( o9 t) F9 }8 p
  37.   (with-temp-buffer% h: X) E; G- N
  38.     (insert content)
    . m, N4 G8 v( Z0 N3 }# \
  39.     (buffer-substring (line-beginning-position)6 ]$ e) q4 x8 I$ K1 {% \- H% u' {
  40.                       (point-max))))& M5 q& r! u# h5 y
  41. , P8 K+ H2 Z8 v+ n+ d
  42. (defun def-tutorial-room (prompt)
    2 Z9 L0 k* ?* d& l5 P+ h# l1 P2 q# D' Y
  43.   "根据提示语自动生成教程房间。
    + G" H8 H0 C: |  z/ t! a( ?
  44. ' S7 x! E+ u" }8 W! D: `! V
  45. 1. 提取最后一行作为问题;
      ], M, `" A/ t% k- h8 G
  46. 2. 分析问题,获取期望用户输入的内容;
    / |0 ]" x5 W6 E5 ?% m0 T; B1 T
  47. 3. 定义教程房间和重复提问房间。"
    - `0 f2 ?; L( v9 R- E# H
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))7 @# p8 M' i$ m, X6 s$ Z* Y/ h
  49.          (repeat-room (concat room-name "-repeat"))
    9 `6 }$ u/ p1 m1 c! r# [
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))+ }: s4 q, J8 t  C( u9 ]9 l
  51.          (question (string-last-line prompt))
    2 l6 h9 T1 o; Z
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    # s% ?1 H! p  V2 s# h5 Q
  53.                      (match-string 1 question)))
    ) C: {6 Z6 n$ x6 {
  54.          (doors (if except, U2 s, d. Y" x  T
  55.                     `((,except ,(intern next-room))
    6 u- c2 A* \9 J) ^5 P
  56.                       ("q" living-room)
    + `  R6 ~  b* [! e9 m! ~& a
  57.                       ("Q" living-room)
    , |7 H1 n) p6 S' f% \
  58.                       (t ,(intern repeat-room)))6 |4 T/ I' B  L4 c
  59.                   '((t living-room)))))# U6 K  d+ p' s/ c, ?0 p
  60.     (def-room-raw (intern room-name) prompt doors)1 T1 I7 N# K" s5 e. u) P
  61.     (def-room-raw (intern repeat-room) question doors)))6 K! t- E& S& `) E

  62. ( v# `7 _, ~8 v
  63. (defun def-tutorial (&rest prompts)+ A6 W4 q. C" i, U" f( Q. k3 `" P+ M
  64.   "批量生成教程房间。"
    + |- [. [4 G8 m# _, e' d
  65.   (dolist (prompt prompts)1 a- J) {: M; e2 Q4 y
  66.     (def-tutorial-room prompt)))
    # I. D$ i8 M# h  W- O: u
  67. # E" ^% T: K0 j
  68. (def-tutorial5 B; @. t9 T! _  C0 G# o# O4 Z
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。" I2 y  n( [: t( P
  70. 1. 教程
    6 {. ?9 k1 u' N
  71. 2. 入门(3x3)/ r. K3 u) H/ d- e* \2 U0 F0 Y
  72. 3. 初级(5x5)9 q( h$ |. ]& w; ]; m5 t% C% X
  73. 4. 中级(7x7)
    9 E6 X) k, Z2 l% L. e
  74. 5. 高级(9x9)( b6 e3 }' L8 x
  75. 0. 关于作者
    + H# F2 }" q$ X3 W; `, a3 J
  76. 请选择1-5开始新游戏:
    # y! L, `5 p9 e& ]5 }$ u
  77. 您现在正在游戏大厅里。  H0 b$ @- K; h& r- B5 m
  78. 请输入“2”进入入门级房间"
    0 |2 E) H6 X  K6 i
  79.   "  ①②③
    8 L, h# x$ M- u7 c$ W) B
  80. 1 ■ 
    5 ]1 h* R  r2 A5 L; ^; w, L6 ]  [6 s
  81. 2■■■: j; D+ y$ Y" Y+ K! @
  82. 3 ■ 
    ) Y5 D- h& ]7 D" e
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!+ M4 H* k% `, W9 R
  84. 请输入“22”来关闭第2行第2列的窗户。"
    $ L) f5 N, S6 m% D; ?$ P
  85.   "  ①②③3 {5 U2 r, t4 n; G
  86. 1   2 Q/ U: X* {4 }( p3 N
  87. 2   
    # W4 l# l% s; a/ f  X
  88. 3   ) V( U8 Y% l! I; \
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    + y4 z9 k9 [6 l5 ]9 P  c% t
  90. 请输入“11”,试着开启左上角的窗户。"
    8 N/ w( v* H0 |2 z. B/ R7 W
  91.   "  ①②③
    3 K/ J  r  a( E7 O$ `6 E
  92. 1■■ 
    & X% l. W, a! K" {+ Q8 N
  93. 2■  : g2 I4 M( _6 Y6 ^0 L7 d# q1 S8 V
  94. 3   
    7 E9 @$ \. B' q9 M; T7 l
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    ) ?& O+ T7 J3 d. L! a3 x8 o
  96. 请输入“13”开启右上角的窗户。"
    3 e" B- V9 H/ n" i
  97.   "  ①②③
    & M. ^" W$ l2 \( E
  98. 1■ ■
    7 `6 |, O( C! g' Y9 X
  99. 2■ ■+ x1 `1 k5 B, K2 G$ I
  100. 3   
    ) C# g# V, O; b6 ?# F
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    & ~$ U( c' p# U/ N1 I+ Y- a4 o
  102. 请输入“31”开启左下角的窗户。"
    ; x8 l$ A2 z  Z( V
  103.   "  ①②③
    ; ?: v; [" A3 T! j. m: j* q7 x+ r
  104. 1■ ■
    ; G! r$ k  U- S0 q! w6 B
  105. 2  ■
    0 i9 n8 T* d" G) f
  106. 3■■ / o: _2 G4 v7 J* {# X# g
  107. 此时,总共有5扇窗户被开启了。7 ~7 `9 e7 n$ g# I$ U
  108. 请输入“33”开启右下角的窗户。"
    & U2 [# P, Q2 F% m- l0 ]
  109.   "  ①②③
    ; E7 ]$ I+ ]3 B' l& A
  110. 1■ ■
    ! O( P- F! g# Q1 z
  111. 2   
    9 G8 W. e+ t+ c! G* B$ m) c- |* N
  112. 3■ ■/ C  N4 r; t, ]1 q
  113. 现在,只有四个角落的窗户被打开。7 M; ]. V8 Y4 H  D0 a  j0 j
  114. 请输入“22”完成最后一击!"
    : L# l( _/ d; r1 B/ L
  115.   "  ①②③. J2 t0 k6 I/ z# c3 E, C, A& |& X
  116. 1■■■
    9 g% W! `. x& u0 h
  117. 2■■■4 I" k/ m5 I# P- l
  118. 3■■■! v- g) N+ [0 `! U1 m
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    - _8 B5 O5 Z, ?4 N9 }+ w
  120. " o) n( n, H5 z  G' ]
  121. ;;; 棋盘* @) [2 p- a7 h8 z; ^  i
  122. (defconst *wechat-5x5-white-chess* 12288! X4 C2 O1 o4 H+ k
  123.   " ")3 q* @9 k+ `/ b. R  L1 |% B2 J
  124. 6 n: b  d+ y$ h
  125. (defconst *wechat-5x5-black-chess* 9632
    , P+ j+ H9 }' Y0 b9 d/ |
  126.   "■"); G, E: a. Q, |) C& O& B- V
  127. $ R; B' J0 {" ^' ~; X
  128. (defmacro with-board (&rest body)' e: Z) v% R& ^- g9 n: o5 ^
  129.   `(with-temp-buffer2 ~5 m5 w$ D, R) k
  130.      (unwind-protect
    . @8 Z0 W5 N8 _! c" y$ V
  131.          (progn2 [6 S$ i$ ]* H; l
  132.            (if (session "board")& c9 V( ?" F1 G  F" |7 f! N  q
  133.                (insert (session "board")))
    2 X( U8 [/ }3 L& m% e$ D
  134.            ,@body)5 a/ e# ^7 H. D" F0 \7 {
  135.        (session "board" (buffer-string)))))
    ( \" s, f' L, W  Z

  136. - X1 I9 c5 s3 Y- A9 h4 r
  137. (defun board-init (size)
    & O- A& h0 R& e; j
  138.   (session "size" size)
    # K# @5 }' }$ r/ z( w2 N
  139.   (session "step" 0). j' W2 E, j6 z! N+ w
  140.   (erase-buffer)/ P1 l: y+ H" L3 I( X8 a
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    # c) [/ V/ G3 W. N' Y
  142.   (dotimes (row size)5 T2 w4 ~8 n" p0 `( v
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    : N$ p2 _1 X6 ~
  144. 8 p: D) n# X- }! B$ t
  145. (defun board-contains-p (y x)1 z) k- l2 i+ m$ @, E1 D' R. \% ?9 I
  146.   (let ((size (session "size")))
    ) ^# ?) r2 ?- w3 V- l
  147.     (and (<= 1 y) (<= y size)9 `& p2 }, ^+ n" J
  148.          (<= 1 x) (<= x size))))
    ' e4 r9 p2 v' T  i" a5 B. {' J
  149.   S9 ~. q: j1 m; x1 B
  150. (defun board-toggle (y x)! M6 y' ?6 `/ c4 {
  151.   (when (board-contains-p y x)
    # [! x9 i' k* ~# q
  152.     (goto-line (1+ y))8 l! |9 n- B% L4 @! \8 ?1 ]
  153.     (beginning-of-line)' P$ A8 R5 x; m- x! R, t
  154.     (forward-char x)
    $ r9 N/ H7 E/ }+ F: t
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    0 Z+ b0 ?5 I* ~
  156.                 *wechat-5x5-black-chess*' A& R- X4 @$ i1 s$ I
  157.               *wechat-5x5-white-chess*))0 E5 O$ }6 j" ], ?& P1 }
  158.     (delete-char 1)))/ a- J3 K4 J- [$ {- N7 G+ y) `# {
  159. 5 e# U" y- x$ s- P
  160. (defun board-put (y x)  n& y2 A: f  T
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    ; M) ~( Z6 [9 M2 m, K4 e0 U
  162.     (board-toggle (+ y (first dir))8 f1 q) l  k. X: Q8 A! Z$ u
  163.                   (+ x (second dir)))))
    : P9 ]% |6 W* K; ~# R% _

  164. 1 B2 z6 _5 N$ ~- D. I5 B
  165. (defun game-over-p (), k; `4 `; \! `( f% W' Q1 ?, k4 S
  166.   (beginning-of-buffer)
    & K# ]9 x4 V) P  M
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t))); e5 N( Z6 }* `% f$ q

  168. , }) G7 d5 h/ u! G% W  ]  n3 e* H
  169. (defun board-show ()
    / L" C; w8 \' y8 k1 l; X6 l5 X; s, l
  170.   (with-board6 @* u( l( P- K9 D5 }1 o
  171.    (concat (buffer-string)+ q, q) N* k4 p$ ?4 X
  172.            (if (game-over-p)+ u- K4 U6 F* ^/ [" N; f) A: K
  173.               (format "共%d步,输入任意内容返回大厅" (session "step")): m. c3 ~1 M$ C; ^
  174.             (format "第%d步" (1+ (session "step")))))))  X$ j# V# L" s- t. S; X

  175. ! k# \" i1 P( `
  176. (defun board-position-parse (cmd)& v* d$ }- D- |( U
  177.   (if (= (length cmd) 2)
    3 z3 ]# q, l3 K0 }5 Q' T, p$ C" u- G$ F: J
  178.       (list (string-to-int (substring cmd 0 1)). o0 o+ L  Q8 ^
  179.             (string-to-int (substring cmd 1 2)))
    0 O9 \5 `- u1 W0 l' z+ r0 p
  180.     '(0 0)))
    , U& Z: z: {) Y4 k. ~% j

  181. 0 D& c! n6 H! q0 P- n
  182. ;;; 游戏房间4 r9 v6 Y) X# ~6 A  H
  183. (defun game-room-init (cmd)8 W, y, L- O' C% D+ N
  184.   (let* ((middle (string-to-int cmd))+ J  r' b0 `: X$ A  x
  185.          (size (1- (* 2 middle))))
    ; @6 e8 ]5 R4 \: V+ I' |3 J* S  i$ N$ H
  186.     (with-board
    + O9 x- w5 }1 K( B1 b+ T" z2 v' Y
  187.      (board-init size)6 \9 ~. t: Y9 t  i
  188.      (board-put middle middle)))
    8 w4 v, J1 {# K/ d$ S
  189.   'game-room). d6 R: e# H2 h' I

  190. ( D7 s+ H8 ]6 Y( c: m
  191. (def-room game-room
    0 ]3 u' A) k/ ^' V6 F' G# s- u, f( n
  192.   #'board-show) h9 A% w# C/ a4 X' `2 H6 T
  193.   (t (lambda (cmd)
    " _+ k* M0 q; a- z. {. e
  194.          (with-board
    * d) B1 \$ ^7 c0 Z' k& j  M" H  d
  195.           (if (game-over-p)
    2 [" E5 E4 G2 u$ E! a8 c* n1 c
  196.               'living-room
    : C. d" P( }% Y2 q
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    - e7 s" k: Y3 k. b- Z
  198.               (when (board-contains-p y x)) W; _) L5 ?7 e
  199.                 (board-toggle y x)) Q3 V; B3 R$ F) W# g
  200.                 (session "step" (1+ (session "step"))))& f5 r( _) Y# l0 K
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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