-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGameOfLife.elm
286 lines (219 loc) · 8.79 KB
/
GameOfLife.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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
module Main exposing (..)
import Html exposing (Html, div, span, table, tr, td, button, text)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Time exposing (Time, millisecond)
main : Program Never Model Msg
main =
Html.program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
grid_width : Int
grid_width =
100
grid_height : Int
grid_height =
60
frequency : Float
frequency =
100 * millisecond
cell_size : String
cell_size =
"5px"
-- MODEL
type alias Row a =
List a
type alias Matrix a =
List (Row a)
type alias Model =
{ matrix : Matrix Bool
, playing : Bool
}
-- TODO-4: Add random true elements
init : ( Model, Cmd Msg )
init =
( { matrix = List.repeat grid_height (List.repeat grid_width False)
, playing = False
}
, Cmd.none
)
-- UPDATE
-- map_matrix: Matrix a -> (method -> a -> b) -> Matrix b
-- map_matrix matrix method =
-- ( List.map (\r -> (List.map (\c -> method c)) r) matrix )
--
-- indexed_map_matrix: Matrix a -> (method -> Int -> Int -> a -> b) -> Matrix b
-- indexed_map_matrix matrix method =
-- ( List.map (\r y -> (List.map (\c x -> method x y c)) r) matrix )
type Msg
= Tick Time
| Set Int Int Bool
| Toggle
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Tick newTime ->
let
zip_row : List a -> List b -> List ( a, b )
zip_row row1 row2 =
case ( row1, row2 ) of
( x :: row1Back, y :: row2Back ) ->
( x, y ) :: zip_row row1Back row2Back
( _, _ ) ->
[]
zip_matrix : Matrix a -> Matrix b -> Matrix ( a, b )
zip_matrix mat1 mat2 =
case ( mat1, mat2 ) of
( row1 :: mat1Back, row2 :: mat2Back ) ->
(zip_row row1 row2) :: zip_matrix mat1Back mat2Back
( _, _ ) ->
[]
next_generation : Matrix Bool -> Matrix Bool
next_generation matrix =
let
integer_row : List Bool -> List Int
integer_row row =
case row of
cell :: rowBack ->
(if cell then
1
else
0
)
:: integer_row rowBack
_ ->
[]
int_matrix : Matrix Int
int_matrix =
List.map (\row -> integer_row row) matrix
shift_down : Matrix Int -> Matrix Int
shift_down matrix =
List.append [ List.repeat grid_width 0 ] (List.take (grid_height - 1) matrix)
shift_up : Matrix Int -> Matrix Int
shift_up matrix =
List.append (List.drop 1 matrix) [ List.repeat grid_width 0 ]
shift_row_left : List Int -> List Int
shift_row_left row =
List.append (List.drop 1 row) [ 0 ]
shift_row_right : List Int -> List Int
shift_row_right row =
0 :: (List.take (grid_width - 1) row)
shift_left : Matrix Int -> Matrix Int
shift_left matrix =
List.map shift_row_left matrix
shift_right : Matrix Int -> Matrix Int
shift_right matrix =
List.map shift_row_right matrix
sum_rows : List ( Int, Int ) -> List Int
sum_rows rows =
case rows of
( row1, row2 ) :: rowBack ->
row1 + row2 :: sum_rows rowBack
_ ->
[]
sum_matrixes : Matrix Int -> Matrix Int -> Matrix Int
sum_matrixes m1 m2 =
List.map sum_rows (zip_matrix m1 m2)
sum_matrixes8 : Matrix Int -> Matrix Int -> Matrix Int -> Matrix Int -> Matrix Int -> Matrix Int -> Matrix Int -> Matrix Int -> Matrix Int
sum_matrixes8 m1 m2 m3 m4 m5 m6 m7 m8 =
sum_matrixes m1 m2
|> sum_matrixes m3
|> sum_matrixes m4
|> sum_matrixes m5
|> sum_matrixes m6
|> sum_matrixes m7
|> sum_matrixes m8
neighbors : Matrix Int
neighbors =
sum_matrixes8 (shift_up int_matrix) (shift_down int_matrix) (shift_left int_matrix) (shift_right int_matrix) (shift_left (shift_up int_matrix)) (shift_right (shift_up int_matrix)) (shift_left (shift_down int_matrix)) (shift_right (shift_down int_matrix))
next_generation_row : List ( Bool, Int ) -> List Bool
next_generation_row row =
case row of
( cell, neighbors ) :: rowBack ->
if neighbors == 3 then
True :: next_generation_row rowBack
else if neighbors < 2 || neighbors > 3 then
False :: next_generation_row rowBack
else
cell :: next_generation_row rowBack
_ ->
[]
next_generation_matrix : Matrix Bool -> Matrix Int -> Matrix Bool
next_generation_matrix matrix neighbors =
-- TODO apply transformation
List.map next_generation_row (zip_matrix matrix neighbors)
in
next_generation_matrix matrix neighbors
in
( { model
| matrix =
(if model.playing then
next_generation model.matrix
else
model.matrix
)
}
, Cmd.none
)
Set x y new_value ->
let
set_value : Int -> Int -> Bool -> Bool
set_value x2 y2 current =
if x2 == x && y2 == y then
new_value
else
current
in
( { model | matrix = List.indexedMap (\x2 r -> (List.indexedMap (\y2 c -> set_value x2 y2 c)) r) model.matrix }
, Cmd.none
)
Toggle ->
( { model | playing = not model.playing }
, Cmd.none
)
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
Time.every frequency Tick
-- VIEW
-- TODO-2 Add some control (reset, step-by-step, time-travel, resize...)
-- TODO-Make the toggles work as long as the mouse is pressed
view : Model -> Html Msg
view model =
let
cell : Int -> Int -> Bool -> Html Msg
cell x y is_alive =
td
[ style
[ ( "background-color"
, if is_alive then
"green"
else
"#DDD"
)
, ( "width", cell_size )
, ( "height", cell_size )
]
, onClick (Set x y (not is_alive))
]
[]
row : Int -> Row Bool -> Html Msg
row x elements =
tr [] (List.indexedMap (\y c -> cell x y c) elements)
grid : Matrix Bool -> Html Msg
grid elements =
table [] (List.indexedMap (\x r -> row x r) elements)
textPlay : Bool -> String
textPlay isPlaying =
if isPlaying then
"Pause"
else
"Play"
in
div []
[ div [] [ grid model.matrix ]
, button [ onClick Toggle ] [ text (textPlay model.playing) ]
]