1 ! Copyright (C) 2015 Sankaranarayanan Viswanathan.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors arrays assocs combinators kernel make math
4 math.vectors random sequences sets sorting ;
8 SYMBOLS: :left :right :up :down ;
10 SYMBOLS: :head :body :tail ;
12 CONSTANT: snake-game-dim { 12 10 }
15 snake snake-loc snake-dir food-loc
16 { next-turn-dir initial: f }
17 { score integer initial: 0 }
18 { paused? boolean initial: t }
19 { game-over? boolean initial: f } ;
24 C: <snake-part> snake-part
26 : <snake> ( -- snake )
28 :left :head <snake-part> ,
29 :left :body <snake-part> ,
30 :left :tail <snake-part> ,
33 : <snake-game> ( -- snake-game )
36 { 5 4 } clone >>snake-loc
38 { 1 1 } clone >>food-loc ;
40 : ?roll-over ( x max -- x )
42 { [ 2dup >= ] [ 2drop 0 ] }
43 { [ over neg? ] [ nip 1 - ] }
47 : move-loc ( loc dir -- loc )
53 } at v+ snake-game-dim [ ?roll-over ] 2map ;
55 : opposite-dir ( dir -- dir )
63 : game-loc>index ( loc -- n )
64 first2 snake-game-dim first * + ;
66 : index>game-loc ( n -- loc )
67 snake-game-dim first /mod swap 2array ;
69 : grow-snake ( snake dir -- snake )
70 opposite-dir :head <snake-part> prefix
71 dup second :body >>type drop ;
73 : move-snake ( snake dir -- snake )
74 [ dup but-last [ dir>> ] map ] dip
75 opposite-dir prefix [ >>dir ] 2map ;
77 : all-indices ( -- points )
78 snake-game-dim product <iota> ;
80 : snake-occupied-locs ( snake head-loc -- points )
81 [ dir>> move-loc ] accumulate nip ;
83 : snake-occupied-indices ( snake head-loc -- points )
84 snake-occupied-locs [ game-loc>index ] map natural-sort ;
86 : snake-unoccupied-indices ( snake head-loc -- points )
87 [ all-indices ] 2dip snake-occupied-indices without ;
89 : snake-will-eat-food? ( snake-game -- ? )
90 [ food-loc>> ] [ snake-loc>> ] [ snake-dir>> ] tri move-loc = ;
92 : increase-score ( snake-game -- snake-game )
93 [ 1 + ] change-score ;
95 : update-snake-shape ( snake-game growing? -- snake-game )
96 [ dup snake-dir>> ] dip
97 '[ _ _ [ grow-snake ] [ move-snake ] if ] change-snake ;
99 : update-snake-loc ( snake-game -- snake-game )
100 dup snake-dir>> '[ _ move-loc ] change-snake-loc ;
102 : generate-food ( snake-game -- snake-game )
103 dup [ snake>> ] [ snake-loc>> ] bi
104 snake-unoccupied-indices random index>game-loc
107 : game-in-progress? ( snake-game -- ? )
108 [ game-over?>> ] [ paused?>> ] bi or not ;
110 : ?handle-pending-turn ( snake-game -- )
111 dup next-turn-dir>> [
116 : snake-will-eat-itself? ( snake-game -- ? )
117 [ snake>> ] [ snake-loc>> ] [ snake-dir>> ] tri move-loc
118 [ snake-occupied-locs rest ] keep swap member? ;
120 : game-over ( snake-game -- )
121 t >>game-over? drop ;
123 : update-snake ( snake-game -- )
124 dup snake-will-eat-food? {
125 [ [ increase-score ] when ]
126 [ update-snake-shape ]
127 [ drop update-snake-loc ]
128 [ [ generate-food ] when ]
131 : do-game-step ( snake-game -- )
132 dup game-in-progress? [
133 dup ?handle-pending-turn
134 dup snake-will-eat-itself?
135 [ game-over ] [ update-snake ] if