-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSnakeGame.hs
152 lines (131 loc) · 3.92 KB
/
SnakeGame.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
module SnakeGame
( SnakeGame
, SnakeState ( Air, Food, Snake )
, SnakeOperation ( U, D, L, R )
, gameMap
, mapSize
, dead
, nextSnakeState
, randSnakeGame
, Rand
) where
import Data.Array
import Data.Maybe
import System.Random
import Utils
import Randomize
{-
- Constants of the game
-}
snakeGrowth = 3 :: Int
data SnakeState = Air | Food | Snake Int
isFood, isSnake, isAir :: SnakeState -> Bool
isSnake (Snake _) = True
isSnake _ = False
isFood Food = True
isFood _ = False
isAir Air = True
isAir _ = False
data SnakeOperation = U | D | L | R
data SnakeGame = SnakeGame
{ gameMap :: Array (Int,Int) SnakeState
, mapSize :: (Int,Int)
, headPos :: (Int,Int)
, snakeLen:: Int
, ticks :: Int
, dead :: Bool
}
randSnakeGame :: (Int, Int) -> Rand SnakeGame
randSnakeGame size@(width, height) = do
let x0 = width `div` 3
let x1 = width - x0 - 1
let y0 = height `div` 3
let y1 = height - y0 - 1
xs <- getRandomR (x0, x1)
ys <- getRandomR (y0, y1)
(xf, yf) <- genNeq (xs,ys) ((x0,x1),(y0,y1))
return $ newSnakeGame size (xs, ys) (xf, yf)
genNeq :: (Int,Int) -> ((Int,Int),(Int,Int)) -> Rand (Int,Int)
genNeq p@(x, y) r@((a0,a1), (b0,b1)) = do
a <- getRandomR (a0, a1)
b <- getRandomR (b0, b1)
if (a, b) /= p
then return (a, b)
else genNeq p r
newSnakeGame size@(width, height) pos@(x, y) food@(x1, y1)=
SnakeGame
{ headPos = pos
, mapSize = size
, ticks = 0
, snakeLen= 3
, gameMap = newMap
, dead = False
}
where
(w, h) = (width - 1, height - 1)
emptyMap = listArray ((0,0), (w,h)) (repeat Air)
newMap = emptyMap // [(pos, Snake 0), (food, Food)]
operateOffset :: (Int, Int) -> SnakeOperation -> (Int, Int)
operateOffset (x, y) L = (x-1, y)
operateOffset (x, y) R = (x+1, y)
operateOffset (x, y) U = (x, y-1)
operateOffset (x, y) D = (x, y+1)
nextSnakeState ::
SnakeGame -> SnakeOperation -> Rand SnakeGame
nextSnakeState game oper =
if game .> dead then
return game
else
if collideWall || collideSnake then
return $ game {
dead = True
}
else if collideFood then do
nextMap <- mapMoveSnake m0 1
return $ game {
snakeLen = game.>snakeLen + snakeGrowth,
gameMap = nextMap,
ticks = time,
headPos = nextPos
}
else do
nextMap <- mapMoveSnake m0 0
return $ game {
gameMap = nextMap,
ticks = time,
headPos = nextPos
}
where
pos @(px, py) = game .> headPos
nextPos@(nx, ny) = operateOffset pos oper
size @(sx, sy) = game .> mapSize
time = (game .> ticks) + 1
m0 = game .> gameMap
collideWall = not (inRange (bounds m0) nextPos)
collideSnake= isSnake (m0!nextPos)
collideFood = isFood (m0!nextPos)
newFood :: Rand (Int, Int)
newFood = do
randX <- getRandomR (0, sx-1)
randY <- getRandomR (0, sy-1)
let foodPos = (randX, randY)
if isAir (m0!foodPos) && foodPos /= nextPos
then return foodPos
else newFood
mapMoveSnake m foodCnt = do
diffs <- diffsRand
return (m // diffs)
where
update i@(x,y) point foods
| i == nextPos = Just (i, Snake time)
| elem i foods = Just (i, Food)
| otherwise =
case point of
Snake t0 -> if t0 > time - (game.>snakeLen)
then Nothing
else Just (i, Air)
_ -> Nothing
diffsRand = do
foods <- sequence (replicate foodCnt newFood)
return $ catMaybes
[ update i (m!i) foods | i <- indices m ]