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

[复制链接]
发表于 2014-2-3 20:48:26 | 显示全部楼层 |阅读模式
wechat.el是用Emacs Lisp打造的微信公众平台开发框架,基于elnode。源码已经托管在Github上:https://github.com/redraiment/wechat.el,现已收录在oschina。# h/ [: u4 u& W* I* ^$ C. N1 i
借助Lisp语言强大的可定制性,使得开发一个公众平台的应用犹如编写一段剧本一样简单!例如本例——开窗游戏,展示了如何给予wechat.el实现自己的应用。
  1.   Y; _- _; o+ i1 K
  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;">;; 定义新的游戏地图$ W# i/ Z7 Y# H2 I+ C
  3. (def-map "/game/5x5.el"                 ; 对外开放的URL" k4 {7 W/ k8 T% h/ x
  4.   'tutorial-room-0)                     ; 默认的入口
    $ x" }# h% [2 j$ X, Q  {9 U
  5. ( a' l1 c+ G( |
  6. ;;; 游戏大厅- z& J$ x0 t. r! m; J, k
  7. (def-room living-room- ^% A  H; K! ^0 Z1 R
  8.   ;; 进入该房间后的提示语" A" a9 C, s2 O5 V
  9.   "1. 教程
    $ h6 Q6 v1 p1 |
  10. 2. 入门(3x3)6 f. K* l& C: e3 I& i; @  C5 g
  11. 3. 初级(5x5)
    ! `3 v: F7 w  K- P) ?
  12. 4. 中级(7x7)
    / B  A# W2 }* H+ F) o6 o8 C; A
  13. 5. 高级(9x9)
    " C3 M$ r" {6 N" g! a
  14. 0. 关于作者$ y$ C5 g% O1 }1 s4 G
  15. 请选择1-5开始新游戏:"' ?5 |3 ~% c$ r4 n9 k& b0 _
  16.   ("0" about-room)                      ; 期望用户输入的信息以及相应的后续房间名! D! @5 [, ]& z" _- H
  17.   ("1" tutorial-room-0)4 V% m0 \+ x0 i
  18.   ("[2-5]" game-room-init)              ; 用户输入信息可用正则表达式匹配
    0 V0 _2 Z- i8 H7 h3 r+ H
  19.                                         ; 相应的返回也可以为函数,动态返回房间名
    3 W& y8 }1 M6 {, E
  20.   (t living-room))                      ; 如果条件为t,为永真
    7 Y  n  q/ i9 x% C  A6 a

  21. 2 D- a4 f% |: r, O
  22. ;;; 作者信息
    " t3 H% M8 x. N4 X# Y
  23. (def-room about-room
    0 g- n; T3 E; o  O. j: z4 a
  24.   "作者:redraiment5 [# v3 f4 H/ i! ^5 b7 |
  25. 微博:http://weibo.com/redraiment
    6 R3 [% w0 V& O! x
  26. 有任何建议,欢迎在微博或微信上联系redraiment。; t' T" C$ A! {. D) y$ m9 |0 z
  27. 请输入任意数字返回游戏大厅。", D( f" P6 g' f/ K
  28.   (t living-room))
    ' q/ ^' C4 O+ B& _; b
  29. 5 R" ?; S# J# @6 o: T' ^+ U
  30. ;;; 教程
    , C# I0 l: ~' W1 d7 m
  31. (defvar *wechat-5x5-tutorial-rooms* 02 X8 _! v  t8 P; a, t  I
  32.   "The number of tutorial rooms")5 M/ n# [! f; ^$ c9 c
  33. / [2 t+ h. }5 G5 |  M5 W/ B) q
  34. ;;; 简化教程的定义
    . g0 i4 n# j% U) {3 B6 N2 |
  35. (defun string-last-line (content)( ?& F) r5 ]& w2 ]  z2 W6 a
  36.   "多行内容组成的字符串中的最后一行"
    , Z6 \5 \/ V5 b, ]* j- Y% l1 E! f
  37.   (with-temp-buffer
    3 R+ t5 v4 Y$ T* |! r
  38.     (insert content), F( D  m* }3 J9 y
  39.     (buffer-substring (line-beginning-position)% ]' _2 G: ^$ L9 C! t
  40.                       (point-max))))
    8 o5 d) b- G& W( m0 ?" l
  41. ; f2 x( ]/ t' N  f
  42. (defun def-tutorial-room (prompt)& V. ~" \7 \3 z, C
  43.   "根据提示语自动生成教程房间。6 V! P3 T# f/ B# [2 r& K4 b. m* o
  44.   F* B2 L" A$ g3 C: i
  45. 1. 提取最后一行作为问题;
    6 U: M7 M# T" I  v
  46. 2. 分析问题,获取期望用户输入的内容;: l3 d3 U) l+ g) P$ ?1 ^& {  |
  47. 3. 定义教程房间和重复提问房间。"4 V3 Z! a4 K' y  w, P. V
  48.   (let* ((room-name (concat "tutorial-room-" (number-to-string *wechat-5x5-tutorial-rooms*)))
    1 C1 e1 l- w1 V/ t
  49.          (repeat-room (concat room-name "-repeat"))% R8 x: {5 O7 g" `( v* @7 d
  50.          (next-room (concat "tutorial-room-" (number-to-string (incf *wechat-5x5-tutorial-rooms*))))
    " [; w3 H- L8 y: s
  51.          (question (string-last-line prompt)): i+ b% u, B9 S2 i* X/ R; u9 {
  52.          (except (if (string-match "请输入“\\([0-9a-zA-Z]+\\)”" question)
    3 y6 c1 `3 ^9 J$ [2 G1 z
  53.                      (match-string 1 question)))2 X+ l/ f' U, o* o5 N/ U% D
  54.          (doors (if except
    0 s$ k+ p' x  k2 A" ~# S$ ]
  55.                     `((,except ,(intern next-room))  f3 R8 n) n; b% k) _- R
  56.                       ("q" living-room)
    3 G2 o: p4 [2 m9 M) ?' Q6 L4 O5 R/ h2 i
  57.                       ("Q" living-room)
    , \7 I- N" k; s; c' y' g
  58.                       (t ,(intern repeat-room)))
    ' A5 Q# \. [4 I; A3 s+ N
  59.                   '((t living-room)))))
    / t* x9 Z6 ]/ ~3 u2 e( C
  60.     (def-room-raw (intern room-name) prompt doors)
    / {, C: i( t+ l9 f& G
  61.     (def-room-raw (intern repeat-room) question doors)))
    + Q+ m8 i/ n% K. j- g

  62. 7 \# i" T  E$ f2 c' G
  63. (defun def-tutorial (&rest prompts): A* E9 ^  K) l' v- R
  64.   "批量生成教程房间。"' o3 p  q8 d. Z) P4 M
  65.   (dolist (prompt prompts)6 ~0 S: q! Q- n3 f9 V' n; V
  66.     (def-tutorial-room prompt)))# l8 S4 c; i  r" V8 j+ C7 \
  67. 9 ?! n, j! q, Z1 J8 E4 O0 M
  68. (def-tutorial3 \; m+ ~+ z2 y) c+ }
  69.   "欢迎体验微信版的开窗游戏!为了让您尽快熟悉游戏的规则,请根据教程提示一步一步完成要求的操作。注:任何时刻,您都能输入“q”来退出本教程。1 E  T& q  t7 e; [5 l
  70. 1. 教程+ Z) t0 B3 Y6 ~( f/ g
  71. 2. 入门(3x3). E1 ?: T5 [7 \
  72. 3. 初级(5x5)
    * t# ?1 |$ G( |5 A- r( U- P
  73. 4. 中级(7x7)
    ' V% U5 a; r, D! l* d5 B
  74. 5. 高级(9x9)5 n1 [* o% ]7 ?; C2 U9 ~8 J
  75. 0. 关于作者( A) i6 X9 J& O: g2 s, p
  76. 请选择1-5开始新游戏:
    $ Y0 T4 `( ?$ ]$ b
  77. 您现在正在游戏大厅里。) k7 o; e  B; s8 E3 G
  78. 请输入“2”进入入门级房间"
    / d8 x6 `# d8 k8 B. D; I, l
  79.   "  ①②③9 T8 t) K/ N: b* L& B, Y
  80. 1 ■ 
    3 O1 L, R$ |* y8 S, ~1 Q
  81. 2■■■; T9 e7 w7 Y, g8 X3 P) U8 ~
  82. 3 ■ & \* K( |- y$ W( G/ H( @8 R
  83. 进入房间后,您就会看到一个如上图所示的3x3棋盘。其中黑子代表打开的窗户,白子代表关闭的窗户。您可以输入任意一个棋盘上的坐标,来开启或关闭某扇窗户。游戏的目标是开启所有窗户!) x4 p+ H) D7 K/ `9 O
  84. 请输入“22”来关闭第2行第2列的窗户。"5 Q' @; z: n/ P0 I- j, l. l
  85.   "  ①②③
    . }2 t8 E5 D# H( m% Y2 v
  86. 1   $ J$ m3 S/ D+ s2 L) o/ V9 Q
  87. 2   ; t% D' Z! A, f! C! c# v
  88. 3   
    # y. n0 f5 u- [& e2 q. O% ^
  89. 你可能被吓了一跳,怎么所有的窗户都被关闭了?我忘了告诉您:这些格子是被按了机关的,只要一个窗户被开启或关闭,它的上下左右四扇窗户也会连锁反应,被开启或关闭。- `: c/ l; F# V( f
  90. 请输入“11”,试着开启左上角的窗户。"
    ! o+ z. ?8 N' v+ {' s& T% \
  91.   "  ①②③2 P9 R) B2 b0 O/ e" d1 \, p
  92. 1■■ 0 m  w& @% {0 Z. ?7 A( [) T+ g
  93. 2■  
    ! F, A" O. t/ n% I% ^- |; z4 u
  94. 3   / s6 j4 j1 C7 n# r
  95. 因为连锁反应,11右边和下面的窗户也被开启了。但因为11在左上角,没有上面和左边,因此这回总共只开启了三扇窗户。
    ( e& Y% l) i( a( |, V' H
  96. 请输入“13”开启右上角的窗户。"
      n! w% |7 x9 _* Q
  97.   "  ①②③
    ( w. S6 m2 \1 h
  98. 1■ ■
    ' q/ C; X) R2 [8 z* }5 n  P
  99. 2■ ■
    4 V) m# ~' m2 k0 j6 F
  100. 3   
    . [6 ~. a2 X+ w, ~
  101. 第1行第2列上的窗户因为本来就是处于开启状态的,这回因为13的连锁反应,又重新被关闭了。* s' ~9 ]1 e/ S- x5 e
  102. 请输入“31”开启左下角的窗户。"( y4 U( J- R! T/ t. I
  103.   "  ①②③
    ' L& h- o8 I7 V
  104. 1■ ■) ~( D; c4 V! G9 e
  105. 2  ■
    - c; v( T* q# c) y9 o5 R
  106. 3■■ 
    8 U8 Y" }  |* J6 J& H; W+ Y' E
  107. 此时,总共有5扇窗户被开启了。
    7 V( S- Z/ f" ~5 x
  108. 请输入“33”开启右下角的窗户。") v0 c9 `$ }0 |1 q0 j- q- C6 ~
  109.   "  ①②③
    6 ^% b5 i* B% ^& J
  110. 1■ ■
    / q  n+ k& D# }! @- ?6 P
  111. 2   
    . g: K0 K  s: j6 K
  112. 3■ ■
    8 x$ Y5 k1 P" B/ R& K5 r
  113. 现在,只有四个角落的窗户被打开。
    ( d8 A0 M6 |& G6 c# Y7 q/ D0 r
  114. 请输入“22”完成最后一击!"
    1 S5 X3 v3 `9 I
  115.   "  ①②③
    . G2 H5 [* Z/ @3 [
  116. 1■■■
    0 q* d% c! N! V0 }8 a7 x2 B3 N
  117. 2■■■% D  `) v2 S+ d7 I* H
  118. 3■■■& n# g6 o* B/ {: I+ |, J1 R
  119. 您已完成教程的所有内容,输入任意内容返回大厅,开始您的开窗之旅!")
    . H1 ]- m/ |# D9 ^
  120. 4 N* B; k' B& {% a# ^
  121. ;;; 棋盘3 }- I8 K+ q) |  M2 x
  122. (defconst *wechat-5x5-white-chess* 12288
    4 s6 b$ k( V  ~  i3 d8 B
  123.   " ")
    ( K5 `0 x' k. e( r8 R

  124. ' I1 ]  ]1 x- A. W
  125. (defconst *wechat-5x5-black-chess* 9632
    3 n7 [0 `# x6 h( \7 j
  126.   "■")' ?/ O- D2 S: @

  127. 4 F+ `0 Z" G0 y' i% `) X
  128. (defmacro with-board (&rest body)
    ! I& ?( A8 s8 F9 ~6 y& B, [' w
  129.   `(with-temp-buffer" L! m4 {8 w4 ~9 r
  130.      (unwind-protect
    * A; {( v& w9 p  Q8 d5 \2 |
  131.          (progn
    0 I6 X3 y3 u; W! c- W
  132.            (if (session "board")6 i4 C- e) o9 U' k7 Y
  133.                (insert (session "board")))  |7 E' z9 p* |+ ]8 t' q
  134.            ,@body)
    2 h( D4 ]- i% Q. H( k- X2 M
  135.        (session "board" (buffer-string)))))
    9 Z) o# a" m7 z
  136. ! r3 K# A  u& J# ?* _- @: L9 l
  137. (defun board-init (size)
    # _! d" [, G  v/ h6 |
  138.   (session "size" size)
    $ T+ v+ ]. [( X; \# E% x
  139.   (session "step" 0)
    % R  ~  O( j* b2 Q  R8 z+ Z
  140.   (erase-buffer)
      C) W& Y8 H+ Y1 b4 c9 T9 w' h
  141.   (insert (format (format "  %%.%ds\n" size) "①②③④⑤⑥⑦⑧⑨"))
    - R+ z  |- Z# H* T
  142.   (dotimes (row size)4 C8 ~- z+ m* v$ |" D  V
  143.     (insert (format "%d%s\n" (1+ row) (make-string size *wechat-5x5-white-chess*)))))
    6 x% x* _0 X" W( n8 R3 k) b& y8 i

  144. 2 _  M* C  _) L% w* [+ Z
  145. (defun board-contains-p (y x)) G) e5 a5 z5 o6 n
  146.   (let ((size (session "size")))1 O% H1 S$ _$ b5 d6 ?5 z. F1 N, {
  147.     (and (<= 1 y) (<= y size)
    $ u& G8 v' M; `" ]4 b
  148.          (<= 1 x) (<= x size))))
    ) O9 C+ m( s/ O$ d
  149. 6 n* X1 s/ a6 g. S1 m* R9 A
  150. (defun board-toggle (y x)" o% o) S3 ]2 g( \% b
  151.   (when (board-contains-p y x)% [  d! a, @8 f
  152.     (goto-line (1+ y))
    ) a% Q! ]; {3 t+ ]2 L8 i
  153.     (beginning-of-line)
    : l+ v1 q5 w1 P6 o- U+ e
  154.     (forward-char x); F0 g, }/ g, r" r, L/ ?7 J8 Y
  155.     (insert (if (= *wechat-5x5-white-chess* (following-char))
    " M) F+ a& y4 W& s
  156.                 *wechat-5x5-black-chess*( [0 g. k! d* B  F! R$ z& W1 x9 H- Z
  157.               *wechat-5x5-white-chess*))/ ~$ }  p3 P. ]% c4 [
  158.     (delete-char 1)))% P) b7 |$ {/ v. h
  159. $ _  m& U# W8 j. @; ?  b
  160. (defun board-put (y x)
      ^# o4 k( ?: p' Q. g' A/ E% M5 I
  161.   (dolist (dir '((-1 0) (0 -1) (0 0) (0 1) (1 0))). X& m  w0 f3 l, o6 s  ~. A: n$ n- ~
  162.     (board-toggle (+ y (first dir))
    - Q8 L5 E# x6 W( \8 N& V
  163.                   (+ x (second dir))))), m0 n. L6 |; q$ Q. x" t+ a# F: ?
  164. & y1 l; m7 l6 |- G+ _5 L
  165. (defun game-over-p ()
    , h; J7 K8 O: W9 f
  166.   (beginning-of-buffer)
    3 M1 Z' c4 K5 J) z1 R7 l* q/ |% p
  167.   (not (search-forward (string *wechat-5x5-white-chess*) nil t))): T' |! T0 c- p6 k# Z2 {
  168. 3 e+ J* j1 x$ h: S# A; f* u/ {9 `& Z
  169. (defun board-show ()9 ~7 Y( C/ ]" V1 s4 r4 \
  170.   (with-board& w3 O' y4 r& G" U7 W( j; C. L" C
  171.    (concat (buffer-string)5 Y; d: t* W2 g& A& c, P1 z
  172.            (if (game-over-p)' J( X5 d. C5 B5 X1 ~  B8 u
  173.               (format "共%d步,输入任意内容返回大厅" (session "step"))$ G* `/ G5 e, N( A' i3 r- T
  174.             (format "第%d步" (1+ (session "step")))))))
    $ L3 L; b/ }& J0 e( @5 W( C( \
  175.   y8 N2 v! U: f# y
  176. (defun board-position-parse (cmd). a% g1 x9 w( f1 M" h/ x
  177.   (if (= (length cmd) 2)  d* I3 W$ X. ~  D" z2 E# D
  178.       (list (string-to-int (substring cmd 0 1))
    : W& ]* X7 n, z: L9 v. T6 L5 t3 M: ]
  179.             (string-to-int (substring cmd 1 2)))
    0 J, @7 G( c! V6 A! N/ o
  180.     '(0 0)))
    7 s" X' k' n- N$ W; |
  181. 2 U9 a* ^& K' s
  182. ;;; 游戏房间/ ~+ d4 \* h& M' U
  183. (defun game-room-init (cmd)
    ) B8 k4 t( I) y7 N4 U, h
  184.   (let* ((middle (string-to-int cmd))2 ?! ~! P: F- p5 g7 \# T0 k2 f- T! S
  185.          (size (1- (* 2 middle))))2 W& x* y1 ]" w* F. w
  186.     (with-board: m7 E9 J7 V9 Q* a+ `- y" W
  187.      (board-init size)
    : I8 i& }+ J+ z, S7 f7 m; ?
  188.      (board-put middle middle)))
    ( ^6 u5 |( i+ V  S" a5 P* ?
  189.   'game-room)
    ; O# y4 k  u$ g/ @& c* q

  190. * V6 `2 b1 z+ i% W
  191. (def-room game-room! x$ Y, v: f+ L( k( N
  192.   #'board-show
    , u$ L* R+ b4 A2 F
  193.   (t (lambda (cmd)! |& D- ~: R2 ^0 ?( c
  194.          (with-board
    ( s- }' v; _& h$ j. r# L; |7 h
  195.           (if (game-over-p)
    7 l. g3 D8 h  g  T% }
  196.               'living-room
    % m& O1 k( }1 G. f3 Y- X
  197.             (destructuring-bind (y x) (board-position-parse cmd)
    & N6 m# H5 i# o
  198.               (when (board-contains-p y x)& ^' o. b" o1 ~* ]) }0 {
  199.                 (board-toggle y x)
    & W2 q5 X0 Q( f9 b. g
  200.                 (session "step" (1+ (session "step"))))  u7 t' P+ w/ H$ ]
  201.               'game-room))))))</pre>
复制代码
13173139_sUYP.jpg




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

使用道具 举报

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

支付宝扫一扫打赏

微信扫一扫打赏

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