Last active
June 19, 2017 03:37
-
-
Save sonnym/ddb7ef3d9d458d2d2e40bf4ef91b2da8 to your computer and use it in GitHub Desktop.
Game of Life in Elm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import List | |
import Random | |
import Time exposing (Time) | |
import Tuple exposing (first, second) | |
import Html exposing (Html, Attribute, div, text) | |
import Html.Attributes exposing (style) | |
type alias Grid = List (List Bool) | |
type Msg = Initialize Grid | Tick Time | |
cellSize = 5 | |
(columns, rows) = (35, 35) | |
main = | |
Html.program | |
{ init = init | |
, view = view | |
, update = update | |
, subscriptions = subscriptions | |
} | |
init : (Grid, Cmd Msg) | |
init = | |
([[]], Random.generate | |
Initialize | |
(Random.map (groupInto columns) (Random.list (rows * columns) Random.bool))) | |
view : Grid -> Html msg | |
view grid = div [ ] (List.map row grid) | |
update : Msg -> Grid -> (Grid, Cmd Msg) | |
update msg state = | |
case msg of | |
Initialize initial -> (initial, Cmd.none) | |
Tick _ -> (evolve state, Cmd.none) | |
subscriptions : Grid -> Sub Msg | |
subscriptions _ = Time.every Time.second Tick | |
row : List Bool -> Html msg | |
row row = div [ style [ ("clear", "both") ] ] (List.map cell row) | |
cell : Bool -> Html msg | |
cell on = div [ cellStyle on ] [ text " " ] | |
cellStyle : Bool -> Attribute msg | |
cellStyle on = | |
style | |
[ ("background", if on then "black" else "white") | |
, ("width", toString cellSize ++ "px") | |
, ("height", toString cellSize ++ "px") | |
, ("float", "left") | |
] | |
groupInto : Int -> List a -> List (List a) | |
groupInto n lst = | |
if List.length lst == 0 then | |
[] | |
else | |
(List.take n lst) :: (groupInto n (List.drop n lst)) | |
evolve : Grid -> Grid | |
evolve generation = | |
List.indexedMap (\y row -> | |
List.indexedMap (\x _ -> | |
descend generation x y) row) generation | |
descend : Grid -> Int -> Int -> Bool | |
descend grid x y = | |
List.concatMap (\n -> List.map (\m -> (x + n, y + m)) | |
[-1, 0, 1]) [-1, 0, 1] | |
|> List.filter (\p -> (first p) > -1 && (first p) < columns && | |
(second p) > -1 && (second p) < rows && | |
(not ((first p) == x && (second p) == y))) | |
|> List.filter (\p -> (valueAt (first p) False | |
(valueAt (second p) [] grid))) | |
|> List.length | |
|> (\l -> ((valueAt x False (valueAt y [] grid)) | |
&& l > 1 && l < 4) || l == 3) | |
valueAt : Int -> a -> List a -> a | |
valueAt i default lst = | |
Maybe.withDefault default (List.head (List.drop i lst)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import List | |
import Random | |
import Time exposing (Time) | |
import Tuple exposing (first, second) | |
import Json.Decode as Json | |
import Html exposing (Html, Attribute, div, label, button, input, text) | |
import Html.Events exposing (onClick, onMouseOut, on, targetValue) | |
import Html.Attributes as Attr exposing (style) | |
type alias Grid = List (List Bool) | |
type alias Model = | |
{ grid : Grid | |
, rows : Int | |
, columns : Int | |
, cellSize : Int | |
, density : Float | |
, tickRate : Int | |
} | |
type Msg = Initialize Grid | |
| Tick Time | |
| Restart | |
| UpdateSize Dimension String | |
| UpdateDensity String | |
| UpdateTickRate String | |
| ToggleCell Int Int | |
type Dimension = Rows | Columns | |
model : Model | |
model = | |
{ grid = [[]] | |
, cellSize = 5 | |
, columns = 35 | |
, rows = 35 | |
, density = 0.5 | |
, tickRate = 1 | |
} | |
main = | |
Html.program | |
{ init = init | |
, view = view | |
, update = update | |
, subscriptions = subscriptions | |
} | |
init : (Model, Cmd Msg) | |
init = (model, seed model.rows model.columns model.density) | |
view : Model -> Html Msg | |
view model = | |
div [ ] | |
[ div [ ] [ button [ onClick Restart ] [ text "Restart Simulation" ] ] | |
, div [ ] | |
[ label [ ] [ text ("Grid Rows (" ++ toString model.rows ++ ")") ] | |
, input | |
[ Attr.type_ "range" | |
, Attr.value (toString model.rows) | |
, Attr.min "10" | |
, Attr.max "200" | |
, onChange (UpdateSize Rows) | |
] [ ] | |
] | |
, div [ ] | |
[ label [ ] [ text ("Grid Columns (" ++ toString model.columns ++ ")") ] | |
, input | |
[ Attr.type_ "range" | |
, Attr.value (toString model.columns) | |
, Attr.min "10" | |
, Attr.max "200" | |
, onChange (UpdateSize Columns) | |
] [ ] | |
] | |
, div [ ] | |
[ label [ ] [ text ("Population Density (" ++ toString model.density ++ ")") ] | |
, input | |
[ Attr.type_ "range" | |
, Attr.value (toString model.density) | |
, Attr.min "0" | |
, Attr.max "1" | |
, Attr.step ".01" | |
, onChange UpdateDensity | |
] [ ] | |
] | |
, div [ ] | |
[ label [ ] [ text ("Tick Rate (" ++ toString model.tickRate ++ " hz)") ] | |
, input | |
[ Attr.type_ "range" | |
, Attr.value (toString model.tickRate) | |
, Attr.min "1" | |
, Attr.max "10" | |
, Attr.step "1" | |
, onChange UpdateTickRate | |
] [ ] | |
] | |
, div [ ] (List.indexedMap (row model.cellSize) model.grid) | |
] | |
update : Msg -> Model -> (Model, Cmd Msg) | |
update msg state = | |
case msg of | |
Initialize initial -> | |
({ state | grid = initial }, Cmd.none) | |
Tick _ -> | |
({ state | grid = evolve state }, Cmd.none) | |
Restart -> | |
(state, seed state.rows state.columns state.density) | |
UpdateSize dim size -> | |
case dim of | |
Rows -> | |
let | |
rows = Result.withDefault state.rows (String.toInt size) | |
in | |
({ state | rows = rows }, seed rows state.columns state.density) | |
Columns -> | |
let | |
columns = Result.withDefault state.columns (String.toInt size) | |
in | |
({ state | columns = columns }, seed state.rows columns state.density) | |
UpdateDensity val -> | |
let | |
density = Result.withDefault state.density (String.toFloat val) | |
in | |
({ state | density = density }, seed state.rows state.columns density) | |
UpdateTickRate val -> | |
let | |
tickRate = Result.withDefault state.tickRate (String.toInt val) | |
in | |
({ state | tickRate = tickRate }, Cmd.none) | |
ToggleCell x y -> | |
({ state | grid = setAt x y True state.grid }, Cmd.none) | |
subscriptions : Model -> Sub Msg | |
subscriptions state = | |
Time.every (Time.millisecond * (1000 / (toFloat state.tickRate))) Tick | |
seed : Int -> Int -> Float -> Cmd Msg | |
seed rows columns density = | |
Random.map (\n -> n < density) (Random.float 0 1) | |
|> Random.list (rows * columns) | |
|> Random.map (groupInto columns) | |
|> Random.generate Initialize | |
row : Int -> Int -> List Bool -> Html Msg | |
row size column row = | |
div | |
[ style [ ("clear", "both") ] ] | |
(List.indexedMap (cell size column) row) | |
cell : Int -> Int -> Int -> Bool -> Html Msg | |
cell size x y on = | |
div | |
[ cellStyle size on | |
, onMouseOut (ToggleCell x y) | |
] | |
[ text " " ] | |
cellStyle : Int -> Bool -> Attribute msg | |
cellStyle cellSize on = | |
style | |
[ ("background", if on then "black" else "white") | |
, ("width", toString cellSize ++ "px") | |
, ("height", toString cellSize ++ "px") | |
, ("float", "left") | |
] | |
onChange : (String -> msg) -> Attribute msg | |
onChange tagger = | |
on "change" (Json.map tagger targetValue) | |
groupInto : Int -> List a -> List (List a) | |
groupInto n lst = | |
if List.length lst == 0 then | |
[] | |
else | |
(List.take n lst) :: (groupInto n (List.drop n lst)) | |
evolve : Model -> Grid | |
evolve ({grid} as model) = | |
List.indexedMap (\y row -> | |
List.indexedMap (\x _ -> | |
descend model x y) row) grid | |
descend : Model -> Int -> Int -> Bool | |
descend {grid, rows, columns} x y = | |
List.concatMap (\n -> List.map (\m -> (x + n, y + m)) | |
[-1, 0, 1]) [-1, 0, 1] | |
|> List.filter (\p -> (first p) > -1 && (first p) < columns && | |
(second p) > -1 && (second p) < rows && | |
(not ((first p) == x && (second p) == y))) | |
|> List.filter (\p -> (valueAt (first p) False | |
(valueAt (second p) [] grid))) | |
|> List.length | |
|> (\l -> ((valueAt x False (valueAt y [] grid)) | |
&& l > 1 && l < 4) || l == 3) | |
valueAt : Int -> a -> List a -> a | |
valueAt i default lst = | |
Maybe.withDefault default (List.head (List.drop i lst)) | |
setAt : Int -> Int -> a -> List (List a) -> List (List a) | |
setAt x y val lst = | |
let | |
inner = valueAt x [] lst | |
updated = (List.take y inner) ++ (val :: (List.drop (y + 1) inner)) | |
in | |
(List.take x lst) ++ (updated :: List.drop (x + 1) lst) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import List | |
import Random | |
import Time exposing (Time) | |
import Tuple exposing (first, second) | |
import Html exposing (Html, Attribute, div, text) | |
import Html.Attributes exposing (style) | |
type alias Grid = List (List Bool) | |
type alias Model = | |
{ grid : Grid | |
, rows : Int | |
, columns : Int | |
, cellSize : Int | |
} | |
type Msg = Initialize Grid | Tick Time | |
model : Model | |
model = | |
{ grid = [[]] | |
, cellSize = 5 | |
, columns = 35 | |
, rows = 35 | |
} | |
main = | |
Html.program | |
{ init = init | |
, view = view | |
, update = update | |
, subscriptions = subscriptions | |
} | |
init : (Model, Cmd Msg) | |
init = | |
(model, Random.generate | |
Initialize | |
(Random.map | |
(groupInto model.columns) | |
(Random.list (model.rows * model.columns) Random.bool))) | |
view : Model -> Html msg | |
view model = div [ ] (List.map (row model.cellSize) model.grid) | |
update : Msg -> Model -> (Model, Cmd Msg) | |
update msg state = | |
case msg of | |
Initialize initial -> | |
({ state | grid = initial }, Cmd.none) | |
Tick _ -> | |
({ state | grid = evolve state }, Cmd.none) | |
subscriptions : Model -> Sub Msg | |
subscriptions _ = Time.every Time.second Tick | |
row : Int -> List Bool -> Html msg | |
row size row = | |
div [ style [ ("clear", "both") ] ] (List.map (cell size) row) | |
cell : Int -> Bool -> Html msg | |
cell size on = div [ cellStyle size on ] [ text " " ] | |
cellStyle : Int -> Bool -> Attribute msg | |
cellStyle cellSize on = | |
style | |
[ ("background", if on then "black" else "white") | |
, ("width", toString cellSize ++ "px") | |
, ("height", toString cellSize ++ "px") | |
, ("float", "left") | |
] | |
groupInto : Int -> List a -> List (List a) | |
groupInto n lst = | |
if List.length lst == 0 then | |
[] | |
else | |
(List.take n lst) :: (groupInto n (List.drop n lst)) | |
evolve : Model -> Grid | |
evolve ({grid} as model) = | |
List.indexedMap (\y row -> | |
List.indexedMap (\x _ -> | |
descend model x y) row) grid | |
descend : Model -> Int -> Int -> Bool | |
descend {grid, rows, columns} x y = | |
List.concatMap (\n -> List.map (\m -> (x + n, y + m)) | |
[-1, 0, 1]) [-1, 0, 1] | |
|> List.filter (\p -> (first p) > -1 && (first p) < columns && | |
(second p) > -1 && (second p) < rows && | |
(not ((first p) == x && (second p) == y))) | |
|> List.filter (\p -> (valueAt (first p) False | |
(valueAt (second p) [] grid))) | |
|> List.length | |
|> (\l -> ((valueAt x False (valueAt y [] grid)) | |
&& l > 1 && l < 4) || l == 3) | |
valueAt : Int -> a -> List a -> a | |
valueAt i default lst = | |
Maybe.withDefault default (List.head (List.drop i lst)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import List | |
import Random | |
import Time exposing (Time) | |
import Tuple exposing (first, second) | |
import Html exposing (Html, Attribute, div, label, button, text) | |
import Html.Events exposing (onClick) | |
import Html.Attributes exposing (style) | |
type alias Grid = List (List Bool) | |
type alias Model = | |
{ grid : Grid | |
, rows : Int | |
, columns : Int | |
, cellSize : Int | |
} | |
type Msg = Initialize Grid | |
| Tick Time | |
| Restart | |
model : Model | |
model = | |
{ grid = [[]] | |
, cellSize = 5 | |
, columns = 35 | |
, rows = 35 | |
} | |
main = | |
Html.program | |
{ init = init | |
, view = view | |
, update = update | |
, subscriptions = subscriptions | |
} | |
init : (Model, Cmd Msg) | |
init = (model, seed model.rows model.columns) | |
view : Model -> Html Msg | |
view model = | |
div [ ] | |
[ div [ ] [ button [ onClick Restart ] [ text "Restart Simulation" ] ] | |
, div [ ] (List.map (row model.cellSize) model.grid) | |
] | |
update : Msg -> Model -> (Model, Cmd Msg) | |
update msg state = | |
case msg of | |
Initialize initial -> | |
({ state | grid = initial }, Cmd.none) | |
Tick _ -> | |
({ state | grid = evolve state }, Cmd.none) | |
Restart -> | |
(state, seed state.rows state.columns) | |
subscriptions : Model -> Sub Msg | |
subscriptions _ = Time.every Time.second Tick | |
seed : Int -> Int -> Cmd Msg | |
seed rows columns = | |
Random.generate Initialize | |
(Random.map | |
(groupInto columns) | |
(Random.list (rows * columns) Random.bool)) | |
row : Int -> List Bool -> Html msg | |
row size row = | |
div [ style [ ("clear", "both") ] ] (List.map (cell size) row) | |
cell : Int -> Bool -> Html msg | |
cell size on = div [ cellStyle size on ] [ text " " ] | |
cellStyle : Int -> Bool -> Attribute msg | |
cellStyle cellSize on = | |
style | |
[ ("background", if on then "black" else "white") | |
, ("width", toString cellSize ++ "px") | |
, ("height", toString cellSize ++ "px") | |
, ("float", "left") | |
] | |
groupInto : Int -> List a -> List (List a) | |
groupInto n lst = | |
if List.length lst == 0 then | |
[] | |
else | |
(List.take n lst) :: (groupInto n (List.drop n lst)) | |
evolve : Model -> Grid | |
evolve ({grid} as model) = | |
List.indexedMap (\y row -> | |
List.indexedMap (\x _ -> | |
descend model x y) row) grid | |
descend : Model -> Int -> Int -> Bool | |
descend {grid, rows, columns} x y = | |
List.concatMap (\n -> List.map (\m -> (x + n, y + m)) | |
[-1, 0, 1]) [-1, 0, 1] | |
|> List.filter (\p -> (first p) > -1 && (first p) < columns && | |
(second p) > -1 && (second p) < rows && | |
(not ((first p) == x && (second p) == y))) | |
|> List.filter (\p -> (valueAt (first p) False | |
(valueAt (second p) [] grid))) | |
|> List.length | |
|> (\l -> ((valueAt x False (valueAt y [] grid)) | |
&& l > 1 && l < 4) || l == 3) | |
valueAt : Int -> a -> List a -> a | |
valueAt i default lst = | |
Maybe.withDefault default (List.head (List.drop i lst)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import List | |
import Random | |
import Time exposing (Time) | |
import Tuple exposing (first, second) | |
import Json.Decode as Json | |
import Html exposing (Html, Attribute, div, label, button, input, text) | |
import Html.Events exposing (onClick, on, targetValue) | |
import Html.Attributes as Attr exposing (style) | |
type alias Grid = List (List Bool) | |
type alias Model = | |
{ grid : Grid | |
, rows : Int | |
, columns : Int | |
, cellSize : Int | |
} | |
type Msg = Initialize Grid | |
| Tick Time | |
| Restart | |
| UpdateSize Dimension String | |
type Dimension = Rows | Columns | |
model : Model | |
model = | |
{ grid = [[]] | |
, cellSize = 5 | |
, columns = 35 | |
, rows = 35 | |
} | |
main = | |
Html.program | |
{ init = init | |
, view = view | |
, update = update | |
, subscriptions = subscriptions | |
} | |
init : (Model, Cmd Msg) | |
init = (model, seed model.rows model.columns) | |
view : Model -> Html Msg | |
view model = | |
div [ ] | |
[ div [ ] [ button [ onClick Restart ] [ text "Restart Simulation" ] ] | |
, div [ ] | |
[ label [ ] [ text ("Grid Rows (" ++ toString model.rows ++ ")") ] | |
, input | |
[ Attr.type_ "range" | |
, Attr.value (toString model.rows) | |
, Attr.min "10" | |
, Attr.max "200" | |
, onChange (UpdateSize Rows) | |
] [ ] | |
] | |
, div [ ] | |
[ label [ ] [ text ("Grid Columns (" ++ toString model.columns ++ ")") ] | |
, input | |
[ Attr.type_ "range" | |
, Attr.value (toString model.columns) | |
, Attr.min "10" | |
, Attr.max "200" | |
, onChange (UpdateSize Columns) | |
] [ ] | |
] | |
, div [ ] (List.map (row model.cellSize) model.grid) | |
] | |
update : Msg -> Model -> (Model, Cmd Msg) | |
update msg state = | |
case msg of | |
Initialize initial -> | |
({ state | grid = initial }, Cmd.none) | |
Tick _ -> | |
({ state | grid = evolve state }, Cmd.none) | |
Restart -> | |
(state, seed state.rows state.columns) | |
UpdateSize dim size -> | |
case dim of | |
Rows -> | |
let | |
rows = Result.withDefault state.rows (String.toInt size) | |
in | |
({ state | rows = rows }, seed rows state.columns) | |
Columns -> | |
let | |
columns = Result.withDefault state.columns (String.toInt size) | |
in | |
({ state | columns = columns }, seed state.rows columns) | |
subscriptions : Model -> Sub Msg | |
subscriptions _ = Time.every Time.second Tick | |
seed : Int -> Int -> Cmd Msg | |
seed rows columns = | |
Random.generate Initialize | |
(Random.map | |
(groupInto columns) | |
(Random.list (rows * columns) Random.bool)) | |
row : Int -> List Bool -> Html msg | |
row size row = | |
div [ style [ ("clear", "both") ] ] (List.map (cell size) row) | |
cell : Int -> Bool -> Html msg | |
cell size on = div [ cellStyle size on ] [ text " " ] | |
cellStyle : Int -> Bool -> Attribute msg | |
cellStyle cellSize on = | |
style | |
[ ("background", if on then "black" else "white") | |
, ("width", toString cellSize ++ "px") | |
, ("height", toString cellSize ++ "px") | |
, ("float", "left") | |
] | |
onChange : (String -> msg) -> Attribute msg | |
onChange tagger = | |
on "change" (Json.map tagger targetValue) | |
groupInto : Int -> List a -> List (List a) | |
groupInto n lst = | |
if List.length lst == 0 then | |
[] | |
else | |
(List.take n lst) :: (groupInto n (List.drop n lst)) | |
evolve : Model -> Grid | |
evolve ({grid} as model) = | |
List.indexedMap (\y row -> | |
List.indexedMap (\x _ -> | |
descend model x y) row) grid | |
descend : Model -> Int -> Int -> Bool | |
descend {grid, rows, columns} x y = | |
List.concatMap (\n -> List.map (\m -> (x + n, y + m)) | |
[-1, 0, 1]) [-1, 0, 1] | |
|> List.filter (\p -> (first p) > -1 && (first p) < columns && | |
(second p) > -1 && (second p) < rows && | |
(not ((first p) == x && (second p) == y))) | |
|> List.filter (\p -> (valueAt (first p) False | |
(valueAt (second p) [] grid))) | |
|> List.length | |
|> (\l -> ((valueAt x False (valueAt y [] grid)) | |
&& l > 1 && l < 4) || l == 3) | |
valueAt : Int -> a -> List a -> a | |
valueAt i default lst = | |
Maybe.withDefault default (List.head (List.drop i lst)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import List | |
import Random | |
import Time exposing (Time) | |
import Tuple exposing (first, second) | |
import Json.Decode as Json | |
import Html exposing (Html, Attribute, div, label, button, input, text) | |
import Html.Events exposing (onClick, on, targetValue) | |
import Html.Attributes as Attr exposing (style) | |
type alias Grid = List (List Bool) | |
type alias Model = | |
{ grid : Grid | |
, rows : Int | |
, columns : Int | |
, cellSize : Int | |
, density : Float | |
} | |
type Msg = Initialize Grid | |
| Tick Time | |
| Restart | |
| UpdateSize Dimension String | |
| UpdateDensity String | |
type Dimension = Rows | Columns | |
model : Model | |
model = | |
{ grid = [[]] | |
, cellSize = 5 | |
, columns = 35 | |
, rows = 35 | |
, density = 0.5 | |
} | |
main = | |
Html.program | |
{ init = init | |
, view = view | |
, update = update | |
, subscriptions = subscriptions | |
} | |
init : (Model, Cmd Msg) | |
init = (model, seed model.rows model.columns model.density) | |
view : Model -> Html Msg | |
view model = | |
div [ ] | |
[ div [ ] [ button [ onClick Restart ] [ text "Restart Simulation" ] ] | |
, div [ ] | |
[ label [ ] [ text ("Grid Rows (" ++ toString model.rows ++ ")") ] | |
, input | |
[ Attr.type_ "range" | |
, Attr.value (toString model.rows) | |
, Attr.min "10" | |
, Attr.max "200" | |
, onChange (UpdateSize Rows) | |
] [ ] | |
] | |
, div [ ] | |
[ label [ ] [ text ("Grid Columns (" ++ toString model.columns ++ ")") ] | |
, input | |
[ Attr.type_ "range" | |
, Attr.value (toString model.columns) | |
, Attr.min "10" | |
, Attr.max "200" | |
, onChange (UpdateSize Columns) | |
] [ ] | |
] | |
, div [ ] | |
[ label [ ] [ text ("Population Density (" ++ toString model.density ++ ")") ] | |
, input | |
[ Attr.type_ "range" | |
, Attr.value (toString model.density) | |
, Attr.min "0" | |
, Attr.max "1" | |
, Attr.step ".01" | |
, onChange UpdateDensity | |
] [ ] | |
] | |
, div [ ] (List.map (row model.cellSize) model.grid) | |
] | |
update : Msg -> Model -> (Model, Cmd Msg) | |
update msg state = | |
case msg of | |
Initialize initial -> | |
({ state | grid = initial }, Cmd.none) | |
Tick _ -> | |
({ state | grid = evolve state }, Cmd.none) | |
Restart -> | |
(state, seed state.rows state.columns state.density) | |
UpdateSize dim size -> | |
case dim of | |
Rows -> | |
let | |
rows = Result.withDefault state.rows (String.toInt size) | |
in | |
({ state | rows = rows }, seed rows state.columns state.density) | |
Columns -> | |
let | |
columns = Result.withDefault state.columns (String.toInt size) | |
in | |
({ state | columns = columns }, seed state.rows columns state.density) | |
UpdateDensity val -> | |
let | |
density = Result.withDefault state.density (String.toFloat val) | |
in | |
({ state | density = density }, seed state.rows state.columns density) | |
subscriptions : Model -> Sub Msg | |
subscriptions _ = Time.every Time.second Tick | |
seed : Int -> Int -> Float -> Cmd Msg | |
seed rows columns density = | |
Random.map (\n -> n < density) (Random.float 0 1) | |
|> Random.list (rows * columns) | |
|> Random.map (groupInto columns) | |
|> Random.generate Initialize | |
row : Int -> List Bool -> Html msg | |
row size row = | |
div [ style [ ("clear", "both") ] ] (List.map (cell size) row) | |
cell : Int -> Bool -> Html msg | |
cell size on = div [ cellStyle size on ] [ text " " ] | |
cellStyle : Int -> Bool -> Attribute msg | |
cellStyle cellSize on = | |
style | |
[ ("background", if on then "black" else "white") | |
, ("width", toString cellSize ++ "px") | |
, ("height", toString cellSize ++ "px") | |
, ("float", "left") | |
] | |
onChange : (String -> msg) -> Attribute msg | |
onChange tagger = | |
on "change" (Json.map tagger targetValue) | |
groupInto : Int -> List a -> List (List a) | |
groupInto n lst = | |
if List.length lst == 0 then | |
[] | |
else | |
(List.take n lst) :: (groupInto n (List.drop n lst)) | |
evolve : Model -> Grid | |
evolve ({grid} as model) = | |
List.indexedMap (\y row -> | |
List.indexedMap (\x _ -> | |
descend model x y) row) grid | |
descend : Model -> Int -> Int -> Bool | |
descend {grid, rows, columns} x y = | |
List.concatMap (\n -> List.map (\m -> (x + n, y + m)) | |
[-1, 0, 1]) [-1, 0, 1] | |
|> List.filter (\p -> (first p) > -1 && (first p) < columns && | |
(second p) > -1 && (second p) < rows && | |
(not ((first p) == x && (second p) == y))) | |
|> List.filter (\p -> (valueAt (first p) False | |
(valueAt (second p) [] grid))) | |
|> List.length | |
|> (\l -> ((valueAt x False (valueAt y [] grid)) | |
&& l > 1 && l < 4) || l == 3) | |
valueAt : Int -> a -> List a -> a | |
valueAt i default lst = | |
Maybe.withDefault default (List.head (List.drop i lst)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import List | |
import Random | |
import Time exposing (Time) | |
import Tuple exposing (first, second) | |
import Json.Decode as Json | |
import Html exposing (Html, Attribute, div, label, button, input, text) | |
import Html.Events exposing (onClick, on, targetValue) | |
import Html.Attributes as Attr exposing (style) | |
type alias Grid = List (List Bool) | |
type alias Model = | |
{ grid : Grid | |
, rows : Int | |
, columns : Int | |
, cellSize : Int | |
, density : Float | |
, tickRate : Int | |
} | |
type Msg = Initialize Grid | |
| Tick Time | |
| Restart | |
| UpdateSize Dimension String | |
| UpdateDensity String | |
| UpdateTickRate String | |
type Dimension = Rows | Columns | |
model : Model | |
model = | |
{ grid = [[]] | |
, cellSize = 5 | |
, columns = 35 | |
, rows = 35 | |
, density = 0.5 | |
, tickRate = 1 | |
} | |
main = | |
Html.program | |
{ init = init | |
, view = view | |
, update = update | |
, subscriptions = subscriptions | |
} | |
init : (Model, Cmd Msg) | |
init = (model, seed model.rows model.columns model.density) | |
view : Model -> Html Msg | |
view model = | |
div [ ] | |
[ div [ ] [ button [ onClick Restart ] [ text "Restart Simulation" ] ] | |
, div [ ] | |
[ label [ ] [ text ("Grid Rows (" ++ toString model.rows ++ ")") ] | |
, input | |
[ Attr.type_ "range" | |
, Attr.value (toString model.rows) | |
, Attr.min "10" | |
, Attr.max "200" | |
, onChange (UpdateSize Rows) | |
] [ ] | |
] | |
, div [ ] | |
[ label [ ] [ text ("Grid Columns (" ++ toString model.columns ++ ")") ] | |
, input | |
[ Attr.type_ "range" | |
, Attr.value (toString model.columns) | |
, Attr.min "10" | |
, Attr.max "200" | |
, onChange (UpdateSize Columns) | |
] [ ] | |
] | |
, div [ ] | |
[ label [ ] [ text ("Population Density (" ++ toString model.density ++ ")") ] | |
, input | |
[ Attr.type_ "range" | |
, Attr.value (toString model.density) | |
, Attr.min "0" | |
, Attr.max "1" | |
, Attr.step ".01" | |
, onChange UpdateDensity | |
] [ ] | |
] | |
, div [ ] | |
[ label [ ] [ text ("Tick Rate (" ++ toString model.tickRate ++ " hz)") ] | |
, input | |
[ Attr.type_ "range" | |
, Attr.value (toString model.tickRate) | |
, Attr.min "1" | |
, Attr.max "10" | |
, Attr.step "1" | |
, onChange UpdateTickRate | |
] [ ] | |
] | |
, div [ ] (List.map (row model.cellSize) model.grid) | |
] | |
update : Msg -> Model -> (Model, Cmd Msg) | |
update msg state = | |
case msg of | |
Initialize initial -> | |
({ state | grid = initial }, Cmd.none) | |
Tick _ -> | |
({ state | grid = evolve state }, Cmd.none) | |
Restart -> | |
(state, seed state.rows state.columns state.density) | |
UpdateSize dim size -> | |
case dim of | |
Rows -> | |
let | |
rows = Result.withDefault state.rows (String.toInt size) | |
in | |
({ state | rows = rows }, seed rows state.columns state.density) | |
Columns -> | |
let | |
columns = Result.withDefault state.columns (String.toInt size) | |
in | |
({ state | columns = columns }, seed state.rows columns state.density) | |
UpdateDensity val -> | |
let | |
density = Result.withDefault state.density (String.toFloat val) | |
in | |
({ state | density = density }, seed state.rows state.columns density) | |
UpdateTickRate val -> | |
let | |
tickRate = Result.withDefault state.tickRate (String.toInt val) | |
in | |
({ state | tickRate = tickRate }, Cmd.none) | |
subscriptions : Model -> Sub Msg | |
subscriptions state = | |
Time.every (Time.millisecond * (1000 / (toFloat state.tickRate))) Tick | |
seed : Int -> Int -> Float -> Cmd Msg | |
seed rows columns density = | |
Random.map (\n -> n < density) (Random.float 0 1) | |
|> Random.list (rows * columns) | |
|> Random.map (groupInto columns) | |
|> Random.generate Initialize | |
row : Int -> List Bool -> Html msg | |
row size row = | |
div [ style [ ("clear", "both") ] ] (List.map (cell size) row) | |
cell : Int -> Bool -> Html msg | |
cell size on = div [ cellStyle size on ] [ text " " ] | |
cellStyle : Int -> Bool -> Attribute msg | |
cellStyle cellSize on = | |
style | |
[ ("background", if on then "black" else "white") | |
, ("width", toString cellSize ++ "px") | |
, ("height", toString cellSize ++ "px") | |
, ("float", "left") | |
] | |
onChange : (String -> msg) -> Attribute msg | |
onChange tagger = | |
on "change" (Json.map tagger targetValue) | |
groupInto : Int -> List a -> List (List a) | |
groupInto n lst = | |
if List.length lst == 0 then | |
[] | |
else | |
(List.take n lst) :: (groupInto n (List.drop n lst)) | |
evolve : Model -> Grid | |
evolve ({grid} as model) = | |
List.indexedMap (\y row -> | |
List.indexedMap (\x _ -> | |
descend model x y) row) grid | |
descend : Model -> Int -> Int -> Bool | |
descend {grid, rows, columns} x y = | |
List.concatMap (\n -> List.map (\m -> (x + n, y + m)) | |
[-1, 0, 1]) [-1, 0, 1] | |
|> List.filter (\p -> (first p) > -1 && (first p) < columns && | |
(second p) > -1 && (second p) < rows && | |
(not ((first p) == x && (second p) == y))) | |
|> List.filter (\p -> (valueAt (first p) False | |
(valueAt (second p) [] grid))) | |
|> List.length | |
|> (\l -> ((valueAt x False (valueAt y [] grid)) | |
&& l > 1 && l < 4) || l == 3) | |
valueAt : Int -> a -> List a -> a | |
valueAt i default lst = | |
Maybe.withDefault default (List.head (List.drop i lst)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment