-
Notifications
You must be signed in to change notification settings - Fork 0
/
Snake.elm
133 lines (96 loc) · 5.62 KB
/
Snake.elm
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
import Keyboard
import Window
import Touch
widthSquare = 10
widthCanvas = 320
heightCanvas = 480
gridColor = rgba 0 0 0 0.7
canvasBackgroundColor = rgba 0 0 255 0.1
snakeColor = rgba 100 0 0 0.5
verticalNumberOfCells = 24
horizontalNumberOfCells = 16
halfVerticalCells = verticalNumberOfCells `div` 2
halfHorizontalCells = horizontalNumberOfCells `div` 2
data SnakeDirection = Up|Down|Right|Left|None
type Vec = (Int,Int)
type Snake = {segments : [Vec],direction : SnakeDirection}
type GameState = {snake : Snake}
defaultGame = { snake = defaultSnake }
defaultSnake = { segments = [(4,8),(4,7),(4,6)], direction = Down }
data Event = GameTick (Float) | KeyboardInput {x : Int, y : Int}
boundsX : Int -> Int
boundsX x = if | x < -halfHorizontalCells -> halfHorizontalCells - 1
| x >= halfHorizontalCells -> -halfHorizontalCells
| otherwise -> x
boundsY : Int -> Int
boundsY y = if | y < -halfVerticalCells -> halfVerticalCells - 1
| y >= halfVerticalCells -> -halfVerticalCells
| otherwise -> y
moveSnakeHead : Vec -> SnakeDirection -> [Vec]
moveSnakeHead (x,y) direction = let (newX,newY) =
case direction of
Up -> (x,y-1)
Down -> (x,y+1)
Right -> (x+1,y)
Left -> (x-1,y)
in [(boundsX newX,boundsY newY)]
moveSnakeRest : [Vec] -> Vec -> [Vec]
moveSnakeRest segments prevSeg = let seg = head segments
segs = tail segments
in if | segs == [] -> [prevSeg]
| otherwise -> [prevSeg] ++ moveSnakeRest segs seg
moveSnake : Snake -> Snake
moveSnake snake = let h = head snake.segments
in {snake | segments <- ( moveSnakeHead h snake.direction ++ moveSnakeRest (tail snake.segments) h) }
getDirection input = if | input.x == -1 -> Left
| input.x == 1 -> Right
| input.y == 1 -> Down
| input.y == -1 -> Up
| otherwise -> None
changeDirection snake input = { snake | direction <- let newdirection = getDirection input
in if | newdirection == None -> snake.direction
| otherwise -> newdirection }
stepGame : Event -> GameState -> GameState
stepGame event g = case event of
GameTick _ -> {g | snake <- moveSnake g.snake }
KeyboardInput input -> {g | snake <- changeDirection g.snake input}
otherwise -> g
{----------------------------------------------------------
Draw the grid
-----------------------------------------------------------}
drawVerticalLineAtIndex squareSide index = traced (solid gridColor) <|
segment (index * squareSide,-1000) (index * squareSide,1000)
drawHorizontalLineAtIndex squareSide index = traced (solid gridColor) <|
segment (-1000,index * squareSide) (1000,index * squareSide)
drawGrid (canvasWidth,canvasHeight) squareSide = let widthRatio = toFloat (canvasWidth)/(2 * squareSide)
heightRatio = toFloat(canvasHeight)/(2 * squareSide)
verticalLines = map (drawVerticalLineAtIndex squareSide) [(-widthRatio)..(widthRatio)]
horizontalLines = map (drawHorizontalLineAtIndex squareSide) [(-heightRatio)..(heightRatio)]
in verticalLines ++ horizontalLines
{----------------------------------------------------------
Draw the Snake
-----------------------------------------------------------}
drawSnake : Snake -> Float -> [Form]
drawSnake snake squareSide = map (drawSnakeSegment squareSide) <| snake.segments
drawSnakeSegment : Float -> Vec -> Form
drawSnakeSegment squareSide (x,y) = square squareSide |> filled snakeColor |>
move ((toFloat x) * squareSide + (squareSide/2),
(toFloat y) * squareSide + (squareSide/2))
drawGame : (Int,Int) -> GameState -> Element
drawGame (windowWidth,windowHeight) gameState = let squareSide = getSquareSide (windowWidth,windowHeight)
(canvasWidth,canvasHeight) = getCanvasSize (windowWidth,windowHeight)
in color canvasBackgroundColor <| collage canvasWidth canvasHeight
<| (drawGrid (canvasWidth,canvasHeight) squareSide) ++
drawSnake gameState.snake squareSide
getSquareSide : (Int,Int) -> Float
getSquareSide (width,height) = toFloat(height `div` verticalNumberOfCells)
getCanvasSize (width,height) = let side = height `div` verticalNumberOfCells
in (side * horizontalNumberOfCells,side * verticalNumberOfCells)
delta = fps 6
inputdelta = Keyboard.arrows
getTouches = Touch.touches
gameSignal = merges [lift GameTick delta,lift KeyboardInput inputdelta]
currentGameState = foldp (stepGame) defaultGame <| gameSignal
getSingleTouch touches = if | length touches > 0 -> Just (head touches)
| otherwise -> Nothing
main = drawGame <~ Window.dimensions ~ currentGameState