module Cellular exposing (..) import Html exposing (..) import Html.Events exposing (..) import Html.Attributes exposing (id, class, classList, style) import Array import Random import Time exposing (Time, second) type Cell = Alive | Dead type alias Board = Array.Array (Array.Array Cell) type alias Rule = Int -> Cell -> Cell type alias Model = { board : Board , rule : Rule } initModel : Model initModel = { board = Array.empty --, board = modify 4 4 Alive <| modify 5 5 Alive <| modify 6 5 Alive <| modify 6 4 Alive <| modify 6 3 Alive <| Array.repeat h <| Array.repeat w Dead , rule = (\livingNeighbours cell -> case cell of Alive -> if livingNeighbours < 2 || livingNeighbours > 3 then Dead else Alive _ -> if livingNeighbours == 3 then Alive else Dead) } type Msg = NoOp | Step | SetBoard Board viewRow row = tr [] <| Array.toList <| Array.map (\c -> let cellClass = case c of Alive -> "alive" Dead -> "dead" in td [ class ("cell " ++ cellClass) ] [] ) row viewBoard : Board -> Html Msg viewBoard board = table [] <| Array.toList <| Array.map (\r -> viewRow r) board view : Model -> Html Msg view model = div [ onClick Step ] [ viewBoard model.board ] modify : Int -> Int -> Cell -> Board -> Board modify x y newState board = case Array.get y board of Just oldRow -> let newRow = Array.set x newState oldRow newBoard = Array.set y newRow board in newBoard Nothing -> board valueAt : Int -> Int -> Board -> Cell valueAt r c board = Maybe.withDefault Dead <| Array.get c <| Maybe.withDefault Array.empty (Array.get r board) surroundings : Int -> Int -> Board -> Int surroundings r c board = let living cell = case cell of Alive -> 1 _ -> 0 ul = living <| valueAt (r - 1) (c - 1) board uc = living <| valueAt (r - 1) c board ur = living <| valueAt (r - 1) (c + 1) board ml = living <| valueAt r (c - 1) board mr = living <| valueAt r (c + 1) board ll = living <| valueAt (r + 1) (c - 1) board lc = living <| valueAt (r + 1) c board lr = living <| valueAt (r + 1) (c + 1) board in ul + uc + ur + ml + mr + ll + lc + lr replaceRow : Rule -> Board -> Int -> Array.Array Cell -> Array.Array Cell replaceRow rule board rn row = let prevRow = Maybe.withDefault Array.empty <| Array.get (rn - 1) board nextRow = Maybe.withDefault Array.empty <| Array.get (rn + 1) board in Array.indexedMap (\cn cell -> rule (surroundings rn cn board) cell ) row step : Rule -> Board -> Board step rule board = Array.indexedMap (replaceRow rule board) board update : Msg -> Model -> (Model, Cmd Msg) update msg model = case msg of Step -> ( { model | board = step model.rule model.board }, Cmd.none ) SetBoard board -> ( { model | board = board }, Cmd.none ) _ -> ( model, Cmd.none ) generateRandom : Int -> Int -> Random.Generator Board generateRandom nr nc = Random.map Array.fromList <| Random.list nr <| Random.map Array.fromList <| Random.list nc <| Random.map (\n -> if n == 1 then Alive else Dead) (Random.int 1 4) subscriptions : Model -> Sub Msg subscriptions model = Sub.batch [ Time.every (second*3) (\_ -> Step) ] main = Html.program { init = (initModel, Random.generate SetBoard (generateRandom 50 100)) , view = view , update = update , subscriptions = subscriptions }