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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。
- }( e: B- ]7 ~, ]- ~) v0 N借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1. / J" w$ p; t6 i# F3 R, c
  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;">;; 定义新的游戏地图9 b8 h( `6 R, T: g) m1 V) C+ B
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL/ i) p+ l( q( D: D. F
  4.   'tutorial-room-0)                     ; 默认的入口
    & }8 ~% \, x$ {% a8 O* L
  5. 5 D; ^: x% `3 B7 ]8 H) o3 k; A3 K
  6. ;;; 游戏大厅$ |$ w2 c* K+ L  ]! u( Q0 S; o+ f
  7. (def-room living-room9 @; Q6 Z6 U' L
  8.   ;; 进入该房间后的提示语  c! b$ f1 n1 ?: S; ]& M2 Y  z
  9.   "1. 教程
    2 b/ r; [* p! @& p7 \, u
  10. 2. 入门(3x3)) ^/ F3 U* k5 n* W- }
  11. 3. 初级(5x5)
    ( L' v0 Z9 K% F2 R; K/ m  k
  12. 4. 中级(7x7)9 m7 t7 X3 y9 n. q7 b: t
  13. 5. 高级(9x9)+ x* [$ M% S0 W# g+ z
  14. 0. 关于作者
    0 d/ Q# \' A; ]! |9 f& T
  15. 请选择1-5开始新游戏:"  F% _9 E- N7 Q* S
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名
    ' b! ?" q4 H2 ~0 a+ y. Y( r* e- f
  17.   ("1" tutorial-room-0)7 N0 t: ]: Y% J# H
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配, V+ X! j" j2 K8 s* w' Y
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    + V! \3 d) ^) G8 M$ f+ M! H$ [4 I5 W
  20.   (t living-room))                      ; 如果条件为t,为永真4 y) K8 A! O2 k2 r, ^
  21. / M( g6 s9 K5 b& ^* Z
  22. ;;; 作者信息
    " [2 u1 p8 Z) q* {1 F
  23. (def-room about-room, U$ t" h. Q) y) W# h! A
  24.   "作者:redraiment
    4 {6 V2 g* ?6 j. u5 ~! J. b3 @
  25. 微博:http://weibo.com/redraiment" ~, ]. b; c+ O/ T: N, z; J
  26. 有任何建议,欢迎在微博或微信上联系redraiment。6 ?+ J& M8 n! v/ H, ?" c
  27. 请输入任意数字返回游戏大厅。"
    , b% ?: c4 |3 `, ]) w+ C; e, B5 W
  28.   (t living-room))6 A! v" g* X8 ^) v  S# r

  29. * X4 O: ~7 K5 p  a8 U( D9 A: t! Y
  30. ;;; 教程. {! ~. O1 V; }; j
  31. (defvar *wechat-5x5-tutorial-rooms* 0
    ) }' Z! {5 T- F1 H1 d$ y$ h
  32.   "The number of tutorial rooms")2 M+ ^; e" E4 w: K/ r

  33. 7 S) x1 g8 l( F4 b
  34. ;;; 简化教程的定义; p0 I) H, V) l$ ^
  35. (defun string-last-line (content)
    ) q2 L$ {* s5 q; O% j2 @- b, [
  36.   "多行内容组成的字符串中的最后一行"0 }9 z. E. ]% H; W8 d. A6 ^6 u2 r
  37.   (with-temp-buffer" G- `1 s# P. h' l# x8 T% B( x, a
  38.     (insert content)+ \1 T( N" f! \* F" L" B
  39.     (buffer-substring (line-beginning-position)
    2 q0 e3 \; h: }* H% V0 x
  40.                       (point-max))))
    1 P2 w( c0 z4 ~  K

  41. % ?- Z0 a3 _, a
  42. (defun def-tutorial-room (prompt)
    * E+ M$ Y: T( Y6 y) a
  43.   "根据提示语自动生成教程房间。
    0 G& D7 g  F& A- o; k4 L0 M  N

  44. " _8 u- E6 ~4 S: b$ i2 C- ~
  45. 1. 提取最后一行作为问题;1 J. [, v" ]  J  j
  46. 2. 分析问题,获取期望用户输入的内容;# p6 E/ F/ |1 T6 \' S8 |2 S+ [
  47. 3. 定义教程房间和重复提问房间。"
    5 e3 Z, Y( X4 K8 A$ ^3 l( I
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    ; q+ ]2 f& x4 v9 k$ q
  49.          (repeat-room (concat room-name "-repeat"))5 {( U" [4 k$ c) Q; h7 n
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    3 X* R; P# c6 t( z; ~$ L4 }- [1 _
  51.          (question (string-last-line prompt))
    4 D& c- b6 R# ^" I
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    ( F" [- u, S/ f5 V* @8 O
  53.                      (match-string 1 question)))! C# o5 G% Y6 D2 n
  54.          (doors (if except3 P+ `+ ]( G0 l: }( Q
  55.                     `((,except ,(intern next-room))& E3 e( T3 k% \6 v* A; m, q4 T
  56.                       ("q" living-room)
    ; H5 g9 S& X$ T2 u& W; c
  57.                       ("Q" living-room); f6 u9 W4 L2 }
  58.                       (t ,(intern repeat-room)))8 @- G1 d! z. o. L
  59.                   '((t living-room)))))
    ( {+ f' `4 ]! A6 a! h% w. l* W
  60.     (def-room-raw (intern room-name) prompt doors)! Q. ?3 e6 }- T7 q
  61.     (def-room-raw (intern repeat-room) question doors)))
    ; i1 c) `2 d1 _  u7 f8 m" }" _; }
  62. 1 _- p6 l3 b$ e
  63. (defun def-tutorial (&rest prompts)
    7 N4 C, _" d/ s- B+ i- k/ I
  64.   "批量生成教程房间。"# T- n- {! j' y2 R! v
  65.   (dolist (prompt prompts)& z/ }4 Z- ~( p; a2 M7 D
  66.     (def-tutorial-room prompt))): z8 X% F  V# F; c! N9 T

  67. 9 V2 Y# z# |' |4 H! r7 l6 c1 a
  68. (def-tutorial
    * _# b4 D" ^& [! P
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。
    5 @$ Y- W' f% M( @6 W
  70. 1. 教程. [$ v% [/ o! }" S* F: ?
  71. 2. 入门(3x3)
    ) u' r6 {  K$ A% N
  72. 3. 初级(5x5)
    ' m) e) U; i. s6 x
  73. 4. 中级(7x7)
    7 g8 H0 Q% {, ^) F' N4 [3 C
  74. 5. 高级(9x9)
    8 F9 b* |+ t% Q8 J% O3 y5 R
  75. 0. 关于作者3 U% \2 A. z$ s/ J& h
  76. 请选择1-5开始新游戏:+ Y7 o, R5 }- K' X6 U: m3 R7 q5 }
  77. 您现在正在游戏大厅里。
    , E  \- ~) F1 H' S; A
  78. 请输入“2”进入入门级房间"
    & q$ Q0 [" k  {( z; Q8 O
  79.   "  ①②③' ^% m, p. K3 ]
  80. 1 ■ 
    2 Z0 L6 H7 j6 E7 X4 B5 |
  81. 2■■■
    " y4 l& `* G; }. V$ g
  82. 3 ■ ) g' s0 D7 ?) z$ _1 z
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!
    ; G' Z, l6 q1 y) k  X
  84. 请输入“22”来关闭第2行第2列的窗户。"  t, c9 m& i* r( x& c* w0 K
  85.   "  ①②③
    % H! O( Z+ m$ r; R2 D1 p
  86. 1   
    % @6 T/ z5 }8 Z1 n: s. K
  87. 2   2 m2 D9 B' u) Y) w( y! E( c
  88. 3   
    . d0 K8 p8 {6 `& z5 O% S2 q4 u' S
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。2 _3 U( l, @* p% }& @+ e" s  H( g
  90. 请输入“11”,试着开启左上角的窗户。"
    ( d3 g$ {1 o" U& |* _
  91.   "  ①②③  f: a* E3 v" m: Z
  92. 1■■ 3 Q3 m5 J2 [1 ?
  93. 2■  
    , D8 D) I0 L! g+ i5 U  e
  94. 3   
    1 V  h7 n' O7 Q8 P; W# Y" [
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    1 ?' s( i8 q0 y9 h6 b
  96. 请输入“13”开启右上角的窗户。"
    / W% I" M- L! i1 W) B" Z6 ^
  97.   "  ①②③1 z+ ~$ T6 d2 ?5 c; O1 C/ W& b
  98. 1■ ■+ ^0 V2 z% i" s
  99. 2■ ■
    ! j; _5 }. w! z3 _: X* h0 x
  100. 3   % f/ `8 h  ], b3 I
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。( U$ B. I. q- D
  102. 请输入“31”开启左下角的窗户。"
    9 E0 h3 D1 @: a9 H
  103.   "  ①②③
    1 B( c6 S# a& j2 }: g
  104. 1■ ■
    0 U& S; ]0 P2 [* M. u
  105. 2  ■
    7 [( c9 z& O+ j6 u; X' ~
  106. 3■■ " Z) h; n1 A% k
  107. 此时,总共有5扇窗户被开启了。( g1 W& f( O3 {0 s
  108. 请输入“33”开启右下角的窗户。"
    + J, |+ R& E# k/ z( c2 q
  109.   "  ①②③
    . [4 U" S1 K" h
  110. 1■ ■7 k8 M. D9 ~' d4 G
  111. 2   1 o* ?! L2 ^  _+ r
  112. 3■ ■1 r! {$ V0 {# f' o& o: q
  113. 现在,只有四个角落的窗户被打开。! \4 ?! w+ S( \. ]4 u$ h! ]/ S
  114. 请输入“22”完成最后一击!"  @7 C4 O% c, I+ v/ S
  115.   "  ①②③
    ( \0 o$ D1 @4 R5 ]% Y
  116. 1■■■  t' v% _' k! y$ E8 e  Z6 ?
  117. 2■■■' k) ~9 ~0 @5 A/ l
  118. 3■■■
    # Z! N  h+ s0 S0 I# ]5 _
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    / i; F/ K2 A; W& S# }

  120. 4 C2 X  `  Q( Q3 X% a9 ~- o- \
  121. ;;; 棋盘) i8 r/ x5 ~6 S' j0 @$ M
  122. (defconst *wechat-5x5-white-chess* 12288
    ( b4 G7 @. F, |/ `5 a2 t
  123.   " ")
    6 [; ^- R: o7 f. O
  124. ) w6 Y9 `: {8 B4 y2 i7 A0 Q) P
  125. (defconst *wechat-5x5-black-chess* 9632. _1 ~2 c9 ]5 Y$ v0 D
  126.   "■")
    & I; T( ^) w% Y. f7 L+ P% Q: @
  127. % Y6 u% ~' S/ \2 X- T( W/ N
  128. (defmacro with-board (&rest body)* |, k7 r  S9 C" z% L  S. Z
  129.   `(with-temp-buffer9 m: {. J4 f8 {$ d
  130.      (unwind-protect
      L* z3 e' Z$ u7 s3 k! e" C0 t, _
  131.          (progn
    ) A+ P7 n2 {) s( j. s/ ~2 p
  132.            (if (session "board")* g- h: c4 {4 J( U$ K) j
  133.                (insert (session "board")))
    ( Y3 o& m" j+ K, D
  134.            ,@body)1 U3 D% y; p: a7 X3 s! L4 |
  135.        (session "board" (buffer-string)))))  g' u! B" b  L" X0 C

  136. ! m* _& |# E3 v0 O
  137. (defun board-init (size)
    7 J$ J. S/ |  V2 L* S4 F6 i
  138.   (session "size" size)& w; ^2 N0 s* ]# b
  139.   (session "step" 0)/ s1 k" s3 K& ~. C0 F
  140.   (erase-buffer)
    ( P: z# G0 {: G- E, [6 z2 q! W
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))5 M! Q/ C$ e$ [" B9 z; E$ v8 ?+ ?
  142.   (dotimes (row size). M8 ^/ y. [2 m& w# Q  F8 M
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    + `- ^' I* r, j2 V
  144. 1 U5 m: d6 c9 H
  145. (defun board-contains-p (y x)1 J, [" L$ n( z/ p
  146.   (let ((size (session "size")))
    ' d; T8 f  A1 O, C
  147.     (and (<= 1 y) (<= y size)8 n' L: y* _8 C. s3 a
  148.          (<= 1 x) (<= x size)))). z0 F8 a6 I3 i( C* y

  149. 9 \- q% ~% }" S
  150. (defun board-toggle (y x)
    , e- S2 b+ {# M
  151.   (when (board-contains-p y x)8 |  i( k$ h5 e4 M# r3 V, X2 F
  152.     (goto-line (1+ y)), q5 N9 U- v; P7 A# \8 ?
  153.     (beginning-of-line)# d* ^# }, a% u3 _/ Z) }7 o, Y( h
  154.     (forward-char x)3 I4 z' {  X: |/ g
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))2 _% B' S& i0 R1 x8 [. ?) K
  156.                 *wechat-5x5-black-chess*
      V. K4 m' v0 J2 f
  157.               *wechat-5x5-white-chess*))
    - N. }9 _% ^: J$ e& z. m, G2 J
  158.     (delete-char 1)))
    2 Z/ g$ u1 f# l; v- l

  159. : c9 l. y. p( ^' I  e
  160. (defun board-put (y x)/ |7 M8 y& A$ |3 w/ @% Y% m' R/ r
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0)))$ ?: q2 e# P# U2 K+ c
  162.     (board-toggle (+ y (first dir))! M% M8 y7 E& T+ U* w' f! `) p
  163.                   (+ x (second dir)))))
    : l/ _7 g* Z! m% b4 X5 b
  164. . U1 ]8 w8 f, I' b3 \
  165. (defun game-over-p ()5 M5 R% @$ N" v
  166.   (beginning-of-buffer)7 }  c( J* J5 I. P) {
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t)))
    3 y: l" y7 g) H  _# ^
  168. % ^$ p' k6 Q! A$ b9 T' W! {3 D
  169. (defun board-show ()/ h0 K+ l+ u9 N* l9 T
  170.   (with-board9 H! I( |. E) ?3 ?
  171.    (concat (buffer-string)
    1 f9 D$ h0 o4 d& h0 w9 F  u
  172.            (if (game-over-p)
    1 K; z$ U5 v; ^, b
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))
    2 l, y  q: w$ U
  174.             (format "第%d步" (1+ (session "step")))))))9 r2 h* G; Z8 n+ L( b
  175. * |, P3 Z6 G( `& `( g
  176. (defun board-position-parse (cmd)
    5 }! H* Q% K' ^$ J' N
  177.   (if (= (length cmd) 2)
    ( w' u2 ?7 D& M
  178.       (list (string-to-int (substring cmd 0 1))* r8 o# M. b0 j9 j& q" g1 N6 x' G
  179.             (string-to-int (substring cmd 1 2))). u7 \0 T9 y' i' F; L$ \
  180.     '(0 0)))
    8 i7 f$ h5 z: x; b2 T& N
  181. ! I1 W9 |& T3 x2 U# b7 ?6 Q" x, _/ y
  182. ;;; 游戏房间
    " v2 g: ^& V5 a( ?1 T7 N
  183. (defun game-room-init (cmd)
    # j# k0 D: j& w4 k  l& r
  184.   (let* ((middle (string-to-int cmd))' ]6 E9 e( Y% c/ D* ^- t7 |
  185.          (size (1- (* 2 middle))))
    2 X$ f  B0 a5 s! X. N; }# |2 E" S
  186.     (with-board8 `/ e7 j, ~) z9 O( q- \$ f- B
  187.      (board-init size)  B; ~4 }: j, T; s
  188.      (board-put middle middle)))( X# {! k. K" [
  189.   'game-room); v8 x: L5 M1 V& J+ @
  190. % U- C" [6 D" b# g' Q, V
  191. (def-room game-room. R+ d" j# e& g$ r9 \. G
  192.   #'board-show
    0 y$ H. f4 @$ p  e3 }9 v4 L. D
  193.   (t (lambda (cmd)
    5 P# w/ S& j$ D. @" K# x0 ~
  194.          (with-board: H8 ~9 S  [1 j6 K; B
  195.           (if (game-over-p)* s( x: G4 [9 X, s5 W% O& j
  196.               'living-room5 Y7 t7 V7 |$ p* M5 }. V1 Q
  197.             (destructuring-bind (y x) (board-position-parse cmd). C- i3 Y: r. x! _4 C5 P9 D3 h
  198.               (when (board-contains-p y x)3 p' ~% H& t. q  P) ^7 L2 @% k
  199.                 (board-toggle y x)
    4 o: h9 z8 R! X# M/ K
  200.                 (session "step" (1+ (session "step"))))6 {, E+ K0 n* Q8 q7 E7 R/ u
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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