This repository has been archived by the owner on Jul 11, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
game.hs
executable file
·161 lines (128 loc) · 4.08 KB
/
game.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
153
154
155
156
157
158
159
160
161
{-# LANGUAGE TemplateHaskell #-}
import Lib.HelperFunctions
import Lib.Snake
import Lib.V2
import Lib.World
import Control.Lens
import Data.Maybe
import Debug.Trace
import Graphics.Gloss
import Graphics.Gloss.Interface.Pure.Game
import Prelude hiding (head)
import qualified Data.Map as Map
import qualified Data.Set as Set
-- CONSTANTS
k_WINDOW_SIDE_LENGTH :: Int
k_WINDOW_SIDE_LENGTH = 400
k_PAD_UP :: V2
k_PAD_UP = [0, 1]
k_PAD_LEFT :: V2
k_PAD_LEFT = [-1, 0]
k_PAD_DOWN :: V2
k_PAD_DOWN = [0, -1]
k_PAD_RIGHT :: V2
k_PAD_RIGHT = [1, 0]
k_HEAD_SIDE_LENGTH :: Float
k_HEAD_SIDE_LENGTH = fromIntegral k_WINDOW_SIDE_LENGTH / 20.0
k_BODY_UNIT_RADIUS :: Float
k_BODY_UNIT_RADIUS = k_HEAD_SIDE_LENGTH / 2
k_FRUIT_RADIUS :: Float
k_FRUIT_RADIUS = k_HEAD_SIDE_LENGTH / 2
k_SNAKE_SPEED :: Int
k_SNAKE_SPEED = round k_HEAD_SIDE_LENGTH
k_INITIAL_SNAKE :: Snake
k_INITIAL_SNAKE =
Snake
[20, 40]
[[0, 40]]
[1, 0]
k_INITIAL_FRUIT :: V2
k_INITIAL_FRUIT = [(-k_WINDOW_SIDE_LENGTH) `quot` 4, (-k_WINDOW_SIDE_LENGTH) `quot` 4]
k_INITIAL_WORLD :: World
k_INITIAL_WORLD =
World
0
Set.empty
k_INITIAL_SNAKE
k_INITIAL_FRUIT
-- DRAWING
-- Draws the world
-- . Creates the picture list based on the snake's body
createPictureList :: [V2] -> [Picture]
createPictureList [] = []
createPictureList (v:vs) =
Translate
(vX + k_BODY_UNIT_RADIUS)
(vY + k_BODY_UNIT_RADIUS)
(circleSolid k_BODY_UNIT_RADIUS)
: createPictureList vs
where
vX = fromIntegral (v !! 0) :: Float
vY = fromIntegral (v !! 1) :: Float
-- . The draw function itself
draw :: World -> Picture
draw w =
pictures $
Translate
((snakeHead 0) + k_HEAD_SIDE_LENGTH / 2)
((snakeHead 1) + k_HEAD_SIDE_LENGTH / 2)
(rectangleSolid k_HEAD_SIDE_LENGTH k_HEAD_SIDE_LENGTH)
: Translate
((snakeFruit 0) + k_FRUIT_RADIUS)
((snakeFruit 1) + k_FRUIT_RADIUS)
(color red (circleSolid k_FRUIT_RADIUS))
: createPictureList (w ^. snake ^. body)
where
snakeHead x = fromIntegral ((w ^. snake ^. head) !! x) :: Float
snakeFruit x = fromIntegral ((w ^. fruit) !! 0) :: Float
-- INPUT HANDLING
-- Updates the world after certain inputs
input :: Event -> World -> World
input (EventKey key Up _ _) = keyDown %~ Set.delete key
input (EventKey key Down _ _) =
(keyDown %~ Set.insert key) . (Map.findWithDefault id key (Map.fromList [
(Char 'w' , checkPadChange k_PAD_UP ),
(Char 'a' , checkPadChange k_PAD_LEFT ),
(Char 's' , checkPadChange k_PAD_DOWN ),
(Char 'd' , checkPadChange k_PAD_RIGHT),
(SpecialKey KeyUp , checkPadChange k_PAD_UP ),
(SpecialKey KeyLeft , checkPadChange k_PAD_LEFT ),
(SpecialKey KeyDown , checkPadChange k_PAD_DOWN ),
(SpecialKey KeyRight, checkPadChange k_PAD_RIGHT)]))
input _ = id
-- STATE HANDLING
-- Advances the world to the next state each frame
-- . Checks the next state
checkNextState :: World -> World
checkNextState w =
if anyTrue (map (areV2Equal (w ^. snake ^. head)) (w ^. snake ^. body))
then k_INITIAL_WORLD
else w &
if (w ^. snake ^. head) == (w ^. fruit)
then
generateNewFruit k_WINDOW_SIDE_LENGTH
. updateSnakeWithFruit k_WINDOW_SIDE_LENGTH k_SNAKE_SPEED
else
updateSnakeWithoutFruit k_WINDOW_SIDE_LENGTH k_SNAKE_SPEED
-- TODO: create new method to check if head and last body are perpendicular to avoid hit bug
-- . The step function itself
step :: Float -> World -> World
step dt w =
trace
(show (w ^. fruit, w ^. snake ^. head))
(w &
(time +~ dt)
. checkNextState
)
-- MAIN MODULE
-- Where the magic happens
main :: IO ()
main =
play
(InWindow "snake game" (k_WINDOW_SIDE_LENGTH, k_WINDOW_SIDE_LENGTH) (100, 100))
white
15
k_INITIAL_WORLD
draw
input
step