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

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

  1. + F9 H4 v2 o/ H- Z2 }
  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;">;; 定义新的游戏地图
    1 Q% `, Q+ B  G  p5 n
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL
    8 v! ]' P" C( p1 `& m( s7 c
  4.   'tutorial-room-0)                     ; 默认的入口
    % L: d; i1 P2 g0 S

  5. ! Q6 J) Y- K" |( i) l
  6. ;;; 游戏大厅
    ( X, e( o% z: F/ Z" Y# p
  7. (def-room living-room1 I' B. O; u$ S. E* V+ l' a! e
  8.   ;; 进入该房间后的提示语. H7 y4 y( N6 B& m
  9.   "1. 教程+ J- I* D" J) P7 y# u1 \
  10. 2. 入门(3x3)' g' H- j1 n5 L, z6 l
  11. 3. 初级(5x5), Z2 s; N7 n- |% d& {, J# u, `2 W
  12. 4. 中级(7x7)5 I9 J6 k; z5 m9 ^& R* _
  13. 5. 高级(9x9)5 t: H9 O3 Y/ |
  14. 0. 关于作者0 o3 \7 [( t; o  r9 b) z" {& s
  15. 请选择1-5开始新游戏:": s2 X9 W! {, I0 c
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名, A) q1 n+ z( H% X1 v
  17.   ("1" tutorial-room-0)0 j! A8 U/ O6 D# Q' i+ h( U  h
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配/ ~9 H' U2 {5 Q
  19.                                         ; 相应的返回也可以为函数,动态返回房间名7 |: w, A( D9 n5 b  D# e
  20.   (t living-room))                      ; 如果条件为t,为永真
    6 g% G; V* p' i4 k4 [

  21. & L9 M. J$ f# k1 v# t7 a
  22. ;;; 作者信息$ a' Q) C; K' c2 H# ?- t6 T' V
  23. (def-room about-room* Q* |9 v: ~5 P1 G* k1 l% [+ C' K
  24.   "作者:redraiment
    7 o9 y1 b- J3 I: ?& m  u' x
  25. 微博:http://weibo.com/redraiment
    : U, ]# ?+ j1 F* H+ l3 C
  26. 有任何建议,欢迎在微博或微信上联系redraiment。2 d5 ?7 ]7 T* Z9 [
  27. 请输入任意数字返回游戏大厅。"
    % c1 H" B8 [; {: s$ b6 ]- i
  28.   (t living-room)): _; N; ]* [+ Y! u

  29. ) Y6 P! v" H& C% l* r; |
  30. ;;; 教程
    / i% C+ q2 N1 p) Z$ B0 a2 r6 c
  31. (defvar *wechat-5x5-tutorial-rooms* 01 ~8 K/ L# \, G/ h* |& i1 @+ a
  32.   "The number of tutorial rooms")  ^* M; \# W1 n' C& z
  33. 7 t8 F! g# T) w( t! B) D
  34. ;;; 简化教程的定义
    ; ?% J8 A) }4 I
  35. (defun string-last-line (content)9 S3 J  U' G3 D# a* _1 Y
  36.   "多行内容组成的字符串中的最后一行"9 q  [( [* f% g
  37.   (with-temp-buffer
    9 z1 o" T7 L9 o" [1 \$ U
  38.     (insert content)
    ' y2 `5 \' X; B+ ^% m9 U; R" T
  39.     (buffer-substring (line-beginning-position)
    / i* W9 R- L6 @# }) P- e1 z8 t
  40.                       (point-max)))), d* `: f, T( W$ P* L9 |- w

  41. % c( l7 K8 Y# m1 ]! y1 g5 \# m& A
  42. (defun def-tutorial-room (prompt)8 j: Q: H4 o1 w) o1 b
  43.   "根据提示语自动生成教程房间。
    6 j1 E1 K1 F! N3 O* A
  44. 1 o: s! O+ s3 B% ~1 q0 _2 p
  45. 1. 提取最后一行作为问题;
    , c  l! g3 `" v7 D
  46. 2. 分析问题,获取期望用户输入的内容;4 m7 ^0 I* H% I3 D$ }) z
  47. 3. 定义教程房间和重复提问房间。"
    ' @3 d* r" o3 t) L! h' y. b
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))1 I  ?8 {& u. C2 g. S
  49.          (repeat-room (concat room-name "-repeat"))+ O, i4 u" {  N% l  j' U
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))& [0 \5 e: {, M* r4 g
  51.          (question (string-last-line prompt))
    " l% j' U) a6 L; d9 K
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)  y8 D1 p" m% f* Q1 g6 M; V
  53.                      (match-string 1 question)))' d: Y0 S6 w# N
  54.          (doors (if except  x9 Z8 T+ r; s+ d
  55.                     `((,except ,(intern next-room))6 y$ @( v1 p* d5 \; x6 \0 ?. Y
  56.                       ("q" living-room)# ~/ {4 a5 j  G, R+ a
  57.                       ("Q" living-room)
    $ B; f, J" v* {. F: K3 @& ^
  58.                       (t ,(intern repeat-room)))
    : w! A9 e0 n# G1 S% ?' l' k
  59.                   '((t living-room)))))( H$ ^6 O+ \$ v# o! f. }: ~9 }
  60.     (def-room-raw (intern room-name) prompt doors)
    ) R( }  D, y9 C
  61.     (def-room-raw (intern repeat-room) question doors)))
    & _3 P4 V' I3 O/ t3 `7 B

  62. % D! W) i6 C, {2 m0 t) R, D8 @
  63. (defun def-tutorial (&rest prompts)" v* a/ }; T2 N- c* U
  64.   "批量生成教程房间。"
    6 y% e+ c' n2 A/ K
  65.   (dolist (prompt prompts)* m. T3 G6 N; E7 \) J& X* ?! J
  66.     (def-tutorial-room prompt)))1 ]3 ^; W& l0 a  k  p& F3 u1 {( x

  67. 7 U# z- o9 ?$ @! y1 E7 ]  O
  68. (def-tutorial
    & M  k/ p. }+ c/ V
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。& a& [4 i9 \  n4 ~( f9 @
  70. 1. 教程
    + b% ~& f  V6 t+ W" e& `
  71. 2. 入门(3x3); s* o# N+ g; F9 V. d2 C$ {; ?1 I2 X
  72. 3. 初级(5x5)
    2 F9 O" E( A/ h! I
  73. 4. 中级(7x7)
    . ]1 o7 \2 q8 ], b+ x+ Z, t" Q
  74. 5. 高级(9x9)) w5 Z' u' U& y+ V; }* Z
  75. 0. 关于作者! A' [& ~% J" d
  76. 请选择1-5开始新游戏:) B8 H0 I, M1 |/ u" \. h  b
  77. 您现在正在游戏大厅里。' V, W, h8 J( u5 O4 c% t
  78. 请输入“2”进入入门级房间"* i2 {' b5 m- _/ w0 _: ?
  79.   "  ①②③
    + [1 l1 I0 D/ i3 D
  80. 1 ■ % }" f0 X& A7 p: p5 f1 m) D
  81. 2■■■$ x+ J* g& J8 R
  82. 3 ■ + m8 b. Z9 ~/ T
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    " \2 m. K! k' y! E
  84. 请输入“22”来关闭第2行第2列的窗户。"
    ( s% d: D# v; I( c' q! v. [
  85.   "  ①②③( ]0 \6 l" r: |3 L, F  N7 d
  86. 1   & c' g( R5 ]* d
  87. 2   ) E, Q3 S% y( z, v
  88. 3   
    8 E2 _/ ^; f" m; g4 ?! X0 i
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。
    & x0 \! I5 G& O+ p
  90. 请输入“11”,试着开启左上角的窗户。"; k* V$ }+ S& d  {4 T
  91.   "  ①②③( F& ~. d1 `$ R& P7 u  r
  92. 1■■ / t% r# t% @- F% u6 ~! t* R/ O) b
  93. 2■  : ^5 V5 ^, X# Q' }
  94. 3   
    9 a$ g( t. t( n! x* a5 z& y
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。  w: {. T/ q; ^
  96. 请输入“13”开启右上角的窗户。"
    6 b0 d9 Q$ v, f' N- y
  97.   "  ①②③
    1 `! U' u5 w( n! _( d$ f* U" d
  98. 1■ ■" Y9 y6 k# D8 f1 C5 U
  99. 2■ ■
    2 e% j7 y5 y# a( B
  100. 3   5 |. X0 k9 q. N
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。
    . z1 D! E# F" H5 D* X$ a; m' l
  102. 请输入“31”开启左下角的窗户。"
    ( J2 K' L1 [$ v1 A9 }
  103.   "  ①②③. j& J0 W/ s( S1 n* ^" c7 r5 H
  104. 1■ ■' \0 G- j. N  [* k1 t* N
  105. 2  ■4 b! M) R2 t& O  {" I) Z
  106. 3■■ 
    4 f% h" q9 E* T% h# g4 T
  107. 此时,总共有5扇窗户被开启了。- x6 \; P' y4 U: d' b" i
  108. 请输入“33”开启右下角的窗户。"
    , x2 [. s, O5 F: l) ?- e
  109.   "  ①②③8 W7 \. E. d6 j4 w
  110. 1■ ■2 K' M% s& L" k* c3 ~2 W8 F" U
  111. 2   
    0 f8 P; s0 x1 [. A6 b
  112. 3■ ■
    / E: p* |3 ?8 q0 O; F
  113. 现在,只有四个角落的窗户被打开。" Q& D+ R& ~# n
  114. 请输入“22”完成最后一击!"
    0 t5 [! @9 A" d; e
  115.   "  ①②③
    8 h9 w  ?# B. E- \! z+ K! Q# A
  116. 1■■■
    - r$ Z5 T! \' v' x1 C" t* m
  117. 2■■■
    - v1 C" ^' Y; R3 B/ \
  118. 3■■■+ a/ e5 h4 P/ q3 J- x
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")9 H* {- S3 b4 _2 f
  120. ) c- o. g7 }% w) c! t- x1 P
  121. ;;; 棋盘' J+ _8 R% X. f) r8 w) I+ J4 r% u
  122. (defconst *wechat-5x5-white-chess* 12288
    $ i# o& ^/ O) |: a
  123.   " ")9 q: n) M6 s+ {5 u: C2 C4 \" q9 V
  124. 9 [: b- D. W6 v" Z- s; ~+ [6 r
  125. (defconst *wechat-5x5-black-chess* 96320 X+ R. H3 W0 m
  126.   "■")
    1 Q2 A; k) |0 s' @+ t

  127. 2 s: t: g/ P2 S  w8 {
  128. (defmacro with-board (&rest body)
    . t& Q0 p) w0 F1 P9 P% C( H
  129.   `(with-temp-buffer
    4 v* Q' G1 a* }: s* F2 Z6 m2 O
  130.      (unwind-protect
    7 K+ h$ @  N' {
  131.          (progn
    3 h; @. U+ Q" F- M: M4 P0 Z4 S
  132.            (if (session "board")
    7 G+ _! A" i* D
  133.                (insert (session "board")))
    % ~! G) O! Y6 b7 P& C. e
  134.            ,@body)
    0 w& s% V9 t2 f3 R( Q7 h9 [
  135.        (session "board" (buffer-string)))))
    2 K) i  S: e* p( ?" F4 C# i) B

  136. + ^! N2 Q5 l  Y% j
  137. (defun board-init (size)+ w  }1 d2 r7 \& h
  138.   (session "size" size)
    # D( O7 R; `5 m2 ~; H
  139.   (session "step" 0)
    2 y" y7 W* s) C, g. Z7 Q; H' A
  140.   (erase-buffer)
    1 b3 h0 w# \& F# i9 }
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    / X  Y) m( v+ P9 N
  142.   (dotimes (row size)
    , M2 E9 s! \6 f: X8 e
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))( d4 Y# x6 X# C- I" i& f

  144. 3 u1 I# W- F6 ^- E- v# i
  145. (defun board-contains-p (y x)
    ) M6 S9 T0 Y( Y4 U( g$ F% W5 i% d6 d/ r
  146.   (let ((size (session "size")))
    ( m3 y  x% E- ?4 M( y
  147.     (and (<= 1 y) (<= y size)% g2 r8 u0 Y1 }' }! ~4 F6 ~
  148.          (<= 1 x) (<= x size)))). {, N" J; G. y, A" D% D
  149. - i4 N1 W" g# U# t5 P
  150. (defun board-toggle (y x)% h% _# H# I2 ?( D
  151.   (when (board-contains-p y x)% t# d; G' C: _* o; d
  152.     (goto-line (1+ y))1 Z( ^- w0 X, X; @$ ^. W
  153.     (beginning-of-line)
    5 y" p# I, y) T! ~
  154.     (forward-char x)
    ) {4 ]0 Q) W' Z5 [8 y
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))' F5 t; t5 O8 p4 x$ q3 x8 Y
  156.                 *wechat-5x5-black-chess*! |3 ~3 ^+ x* j, n
  157.               *wechat-5x5-white-chess*))
    6 M% j' x0 m* i9 f+ a/ J
  158.     (delete-char 1)))
    " o5 m( \% V7 P8 V

  159. ; t( b- s8 p) }5 z9 q
  160. (defun board-put (y x)
    - k' v' C$ y5 h7 z
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))
    $ I3 i: M  C' Y! A% S; Q4 D( v, [% x9 s
  162.     (board-toggle (+ y (first dir))1 U: S+ A. o+ o0 C0 S
  163.                   (+ x (second dir)))))( A! w4 h: G/ b1 J6 Y( i

  164. 6 |3 g$ {  M& J3 \3 V
  165. (defun game-over-p ()
    : N; a) }6 U! s& m
  166.   (beginning-of-buffer)
      S  D; R( ?6 N5 x6 Q
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    9 K, @, Q# f) }' ]6 r

  168. & V+ l6 ]8 x* C; E: H3 E! }: k6 G7 ~
  169. (defun board-show ()
    2 K6 B) ^' W! A. t; V6 s: U/ {
  170.   (with-board
    ' E: _9 T$ E0 |
  171.    (concat (buffer-string)
    5 }1 Q& S+ H! O, l; j# T% A
  172.            (if (game-over-p)
    0 l9 q5 T6 X& W
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))! ^+ c, s4 p" D2 `- r+ p
  174.             (format "第%d步" (1+ (session "step"))))))): D- z" V8 b! N' V
  175. 3 M7 ^+ d% q1 `8 H7 y) T- g
  176. (defun board-position-parse (cmd)' P- w# U/ g$ E5 _
  177.   (if (= (length cmd) 2)
    % |8 v+ Y* `+ ~# o. X2 B- V
  178.       (list (string-to-int (substring cmd 0 1))
    1 T0 `4 ~9 S4 P, j
  179.             (string-to-int (substring cmd 1 2)))/ k' C+ ]7 Q8 p1 }+ P9 x+ [
  180.     '(0 0)))7 Z# x# D+ {5 m5 u, R, B
  181. 7 f5 y! A+ N; |+ c" |
  182. ;;; 游戏房间
    4 t; X3 A0 q3 ]9 G2 G
  183. (defun game-room-init (cmd)2 r( A  I0 {- L/ p
  184.   (let* ((middle (string-to-int cmd))1 l8 Q( |7 ^9 g: {  e& J+ O
  185.          (size (1- (* 2 middle))))1 ~5 k  Y0 ]" }. [% c+ T5 i
  186.     (with-board0 O1 a& S; r! V/ T8 M3 g$ B9 y2 t
  187.      (board-init size)
    - n& N7 i1 V# N4 o. z# k  V
  188.      (board-put middle middle)))# h* H' {! m# |0 X
  189.   'game-room), b; O8 H/ a! P& W( N# p/ d* k

  190. ; X/ R  I; t. L: S
  191. (def-room game-room
    * u& L) e# b1 g! D
  192.   #'board-show3 R1 i4 S* ?* S% K. C, D8 {
  193.   (t (lambda (cmd)
    + A- d  @* N" T, q% v; A
  194.          (with-board
    9 E; f9 J* A/ ~* ]  N. e
  195.           (if (game-over-p)  A' _! l& G$ E1 J
  196.               'living-room  B" n. `9 G" C# L9 p
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    % ^; A! _7 c: o9 [. d1 C) o( }$ O
  198.               (when (board-contains-p y x)" ]0 p+ o1 {! G3 t
  199.                 (board-toggle y x)
    , p8 O" D' F9 Q$ Y$ z' T. Q
  200.                 (session "step" (1+ (session "step"))))
    " i0 j  L9 u3 p9 y: o* _
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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