Main.elm 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. module Cellular exposing (..)
  2. import Html exposing (..)
  3. import Html.Events exposing (..)
  4. import Html.Attributes exposing (id, class, classList, style)
  5. import Array
  6. import Random
  7. import Window
  8. import Task
  9. import Time exposing (Time, second)
  10. cellSize : Int
  11. cellSize = 10 + 2
  12. type Cell
  13. = Alive
  14. | Dead
  15. type alias Board = Array.Array (Array.Array Cell)
  16. type alias Rule = Int -> Cell -> Cell
  17. type alias Model =
  18. { board : Board
  19. , rule : Rule
  20. , info : String
  21. }
  22. initModel : Model
  23. initModel =
  24. { board = Array.empty
  25. --, 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
  26. , rule = (\livingNeighbours cell ->
  27. case cell of
  28. Alive ->
  29. if livingNeighbours < 2 || livingNeighbours > 3 then
  30. Dead
  31. else
  32. Alive
  33. _ ->
  34. if livingNeighbours == 3 then
  35. Alive
  36. else
  37. Dead)
  38. , info = ""
  39. }
  40. type Msg
  41. = NoOp
  42. | Step
  43. | SetBoard Board
  44. | InitializeBoard Window.Size
  45. viewRow row =
  46. tr
  47. []
  48. <|
  49. Array.toList
  50. <|
  51. Array.map (\c ->
  52. let
  53. cellClass =
  54. case c of
  55. Alive -> "alive"
  56. Dead -> "dead"
  57. in
  58. td
  59. [ class ("cell " ++ cellClass)
  60. ]
  61. []
  62. ) row
  63. viewBoard : Board -> Html Msg
  64. viewBoard board =
  65. let
  66. numCols =
  67. Array.length <| Maybe.withDefault Array.empty <| Array.get 0 board
  68. calculatedWidth = numCols * cellSize
  69. tableWidth = (toString calculatedWidth) ++ "px"
  70. in
  71. table
  72. [ style
  73. [ ("width", tableWidth)
  74. ]
  75. ]
  76. <|
  77. Array.toList
  78. <|
  79. Array.map (\r -> viewRow r) board
  80. view : Model -> Html Msg
  81. view model =
  82. div
  83. [ onClick Step
  84. ]
  85. [ viewBoard model.board
  86. , div
  87. []
  88. [ text model.info ]
  89. ]
  90. modify : Int -> Int -> Cell -> Board -> Board
  91. modify x y newState board =
  92. case Array.get y board of
  93. Just oldRow ->
  94. let
  95. newRow = Array.set x newState oldRow
  96. newBoard = Array.set y newRow board
  97. in
  98. newBoard
  99. Nothing ->
  100. board
  101. valueAt : Int -> Int -> Board -> Cell
  102. valueAt r c board =
  103. Maybe.withDefault Dead <| Array.get c <| Maybe.withDefault Array.empty (Array.get r board)
  104. surroundings : Int -> Int -> Board -> Int
  105. surroundings r c board =
  106. let
  107. living cell =
  108. case cell of
  109. Alive -> 1
  110. _ -> 0
  111. ul = living <| valueAt (r - 1) (c - 1) board
  112. uc = living <| valueAt (r - 1) c board
  113. ur = living <| valueAt (r - 1) (c + 1) board
  114. ml = living <| valueAt r (c - 1) board
  115. mr = living <| valueAt r (c + 1) board
  116. ll = living <| valueAt (r + 1) (c - 1) board
  117. lc = living <| valueAt (r + 1) c board
  118. lr = living <| valueAt (r + 1) (c + 1) board
  119. in
  120. ul + uc + ur + ml + mr + ll + lc + lr
  121. replaceRow : Rule -> Board -> Int -> Array.Array Cell -> Array.Array Cell
  122. replaceRow rule board rn row =
  123. let
  124. prevRow =
  125. Maybe.withDefault Array.empty <| Array.get (rn - 1) board
  126. nextRow =
  127. Maybe.withDefault Array.empty <| Array.get (rn + 1) board
  128. in
  129. Array.indexedMap
  130. (\cn cell ->
  131. rule (surroundings rn cn board) cell
  132. ) row
  133. step : Rule -> Board -> Board
  134. step rule board =
  135. Array.indexedMap (replaceRow rule board) board
  136. update : Msg -> Model -> (Model, Cmd Msg)
  137. update msg model =
  138. case msg of
  139. Step ->
  140. ( { model | board = step model.rule model.board }, Cmd.none )
  141. SetBoard board ->
  142. ( { model | board = board }, Cmd.none )
  143. InitializeBoard size ->
  144. let
  145. numRows = size.height // cellSize + 1
  146. numCols = size.width // cellSize + 1
  147. in
  148. ( model, Random.generate SetBoard (generateRandom numRows numCols) )
  149. _ ->
  150. ( model, Cmd.none )
  151. generateRandom : Int -> Int -> Random.Generator Board
  152. generateRandom nr nc =
  153. 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)
  154. subscriptions : Model -> Sub Msg
  155. subscriptions model =
  156. Sub.batch
  157. [ Time.every (second/3) (\_ -> Step)
  158. , Window.resizes InitializeBoard
  159. ]
  160. main =
  161. Html.program
  162. { init = (initModel, Task.perform InitializeBoard Window.size)
  163. , view = view
  164. , update = update
  165. , subscriptions = subscriptions
  166. }