-
Notifications
You must be signed in to change notification settings - Fork 2
/
Main.elm
226 lines (173 loc) · 4.66 KB
/
Main.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
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
module Main exposing (..)
import Time exposing (Time, second)
import Keyboard.Extra
import Random
import Html
import Types exposing (..)
import WebGLViews
import Views
import Window exposing (Size)
import Task
initialKeyboard : Keyboard.Extra.Model
initialKeyboard =
Tuple.first Keyboard.Extra.init
init : ( Model, Cmd Msg )
init =
( Model
[ ( 0, 0 ) ]
( 0, 0 )
( 0, 0 )
( 1, 0 )
initialKeyboard
(Size 0 0)
, Cmd.batch
[ generateNewApple
, Task.perform Resize Window.size
]
)
main : Program Never Model Msg
main =
Html.program
{ init = init
, view = metaView
, update = update
, subscriptions = subscriptions
}
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.batch
[ Sub.map KeyboardMsg Keyboard.Extra.subscriptions
, Time.every (second / config.fps) TickControl
, Time.every (second / config.tps) Tick
, Window.resizes Resize
]
dropLastVertebra =
List.reverse
<< List.drop 1
<< List.reverse
calculateNewVertebra ( x, y ) ( directionX, directionY ) =
( directionX + x
, directionY + y
)
addNewVertebra : ( Int, Int ) -> List ( Int, Int ) -> List ( Int, Int )
addNewVertebra direction snake =
let
currentHead =
snake
|> List.head
|> Maybe.withDefault ( 0, 0 )
newVertebra =
calculateNewVertebra currentHead direction
in
(newVertebra :: snake)
collision =
List.member
generateNewApple =
Random.generate NewApple
(Random.pair
(Random.int 1 config.max)
(Random.int 1 config.max)
)
between minimum maximum value =
value >= minimum && value <= maximum
out minimum maximum ( x, y ) =
let
betweenBorders =
between minimum maximum
in
(not <| betweenBorders y)
|| (not <| betweenBorders x)
collisionWithHimselfOrWall snake =
case snake of
head :: tail ->
collision head tail || out 0 config.max head
[] ->
False
moveSnake : Model -> ( Model, Cmd Msg )
moveSnake ({ direction, snake, apple } as model) =
let
movedSnake =
snake
|> addNewVertebra direction
appleEaten =
movedSnake
|> collision apple
finalSnake =
if appleEaten then
movedSnake
else
movedSnake |> dropLastVertebra
in
if collisionWithHimselfOrWall movedSnake then
init
else
( { model | snake = finalSnake }
, if appleEaten then
generateNewApple
else
Cmd.none
)
applyKeyboard ( arrowX, arrowY ) (( snakeDirectionX, snakeDirectionY ) as snakeDirection) =
if arrowX /= 0 && snakeDirectionX == 0 then
( arrowX, 0 )
else if arrowY /= 0 && snakeDirectionY == 0 then
( 0, -arrowY )
else
snakeDirection
updateDirection : Model -> Model
updateDirection model =
let
newDirection =
model.direction
|> applyKeyboard model.arrows
in
{ model | direction = newDirection }
handleKeyboard : Model -> Keyboard.Extra.Msg -> ( Model, Cmd Msg )
handleKeyboard model keyMsg =
let
( keyboardModel, keyboardCmd ) =
Keyboard.Extra.update keyMsg model.keyboardModel
arrows =
Keyboard.Extra.arrows keyboardModel
newArrows : ( Int, Int )
newArrows =
( arrows.x, arrows.y )
in
( { model
| keyboardModel = keyboardModel
, arrows = newArrows
}
, Cmd.map KeyboardMsg keyboardCmd
)
-- asciiView model =
-- Html.pre []
-- [ Html.text ""
-- ]
--
metaView model =
Html.div []
[ Views.view model
, WebGLViews.view model
-- , asciiView model
]
addNewApple : ( Int, Int ) -> Model -> ( Model, Cmd Msg )
addNewApple newApple model =
if collision newApple model.snake then
( model, generateNewApple )
else
( { model | apple = newApple }, Cmd.none )
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
KeyboardMsg keyMsg ->
handleKeyboard model keyMsg
TickControl time ->
( model |> updateDirection, Cmd.none )
Tick time ->
model
|> moveSnake
NewApple newApple ->
model
|> addNewApple newApple
Resize size ->
( { model | size = size }, Cmd.none )