a one dimensional cellular automata, using comonads
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

137 lines
3.8KB

  1. {-# LANGUAGE DeriveGeneric #-}
  2. module Automata where
  3. import Comonad
  4. import Spaces.Space1
  5. import Spaces.Space2
  6. import System.Random
  7. import GHC.Generics
  8. import Control.DeepSeq
  9. import Data.Maybe
  10. -----------------------
  11. -- cellular automata --
  12. -----------------------
  13. -- the states our cells can be in
  14. -- may need to provide an ordering
  15. -- may need to generalise the number
  16. -- of states
  17. data CellState = Rock | Paper | Scissors
  18. deriving (Eq, Bounded, Enum, Generic)
  19. instance NFData CellState
  20. instance Random CellState where
  21. random g = case randomR (fromEnum (minBound :: CellState), fromEnum (maxBound :: CellState)) g of
  22. (r, g') -> (toEnum r, g')
  23. randomR (a,b) g = case randomR (fromEnum a, fromEnum b) g of
  24. (r, g') -> (toEnum r, g')
  25. -- how the states are displayed on screen
  26. -- this should probably be input to a function
  27. -- rather than hardcoded
  28. instance Show CellState
  29. where
  30. show Rock = "⬤"
  31. show Paper = " "
  32. show Scissors = "_"
  33. -- -- a rule stating how a cell is determined
  34. -- rule :: Space CellState -> CellState
  35. -- rule (Space (l:_) _ (r:_))
  36. -- | l == r = Dead
  37. -- | otherwise = Alive
  38. --
  39. -- -- a second rule for example
  40. -- rule2 :: Space CellState -> CellState
  41. -- rule2 (Space (l1:l2:_) m (r1:r2:_))
  42. -- | m == Alive && numAlive == 1 = Dead
  43. -- | m == Alive && numAlive == 4 = Dead
  44. -- | m == Dead && numAlive == 3 = Alive
  45. -- | otherwise = m
  46. -- where
  47. -- ns = [l1, l2, r1, r2]
  48. -- numAlive = length $ filter (== Alive) ns
  49. --
  50. -- rule3 :: Space CellState -> CellState
  51. -- rule3 (Space (l:_) m (r:_))
  52. -- | l == r = m
  53. -- | otherwise = if m == Alive then Dead else Alive
  54. ------------------------
  55. -- grabbing neighbors --
  56. ------------------------
  57. -- we want to be able to create a list of (Maybe CellState)
  58. -- representing each neighbor, this way it will work on the
  59. -- edges, and also we can fix the position of ecah neighbor
  60. -- so that rules can be directional also.
  61. grabNeighbors :: Space2 CellState -> [(Maybe CellState)]
  62. grabNeighbors s = let
  63. tl = grabTopLeft s
  64. t = grabTop s
  65. tr = grabTopRight s
  66. l = grabLeft s
  67. r = grabRight s
  68. bl = grabBotLeft s
  69. b = grabBot s
  70. br = grabBotRight s
  71. in [tl, t, tr, l, r, bl, b, br]
  72. grabTemplate :: (Space2 CellState -> Maybe (Space2 CellState))
  73. -> Space2 CellState -> Maybe CellState
  74. grabTemplate f s = case f s of
  75. Nothing -> Nothing
  76. Just x -> Just $ extract x
  77. grabTop, grabBot, grabLeft, grabRight :: Space2 CellState -> Maybe CellState
  78. grabTop = grabTemplate up2
  79. grabBot = grabTemplate down2
  80. grabLeft = grabTemplate left2
  81. grabRight = grabTemplate right2
  82. maycom :: (a -> Maybe a) -> (a -> Maybe a) -> a -> Maybe a
  83. maycom f g s = do
  84. x <- f s
  85. y <- g x
  86. return y
  87. grabTopLeft, grabTopRight, grabBotLeft, grabBotRight :: Space2 CellState -> Maybe CellState
  88. grabTopLeft = grabTemplate (maycom up2 left2)
  89. grabTopRight = grabTemplate (maycom up2 right2)
  90. grabBotLeft = grabTemplate (maycom down2 left2)
  91. grabBotRight = grabTemplate (maycom down2 right2)
  92. filtJust :: [(Maybe a)] -> [a]
  93. filtJust [] = []
  94. filtJust (Nothing:as) = filtJust as
  95. filtJust ((Just a):as) = a:(filtJust as)
  96. numMatch :: CellState -> [(Maybe CellState)] -> Int
  97. numMatch c = length . (filter (== c)) . filtJust
  98. --------------
  99. -- 2d rules --
  100. --------------
  101. conway :: Space2 CellState -> CellState
  102. conway s = case extract s of
  103. Rock -> Paper
  104. Paper -> if numSci == 3 then Scissors else Paper
  105. Scissors -> if numSci == 2 || numSci == 3 then Scissors else Paper
  106. where
  107. numSci = numMatch Scissors ns
  108. ns = grabNeighbors s
  109. rps :: Space2 CellState -> CellState
  110. rps s
  111. = case extract s of
  112. Rock -> if (numNs Paper) > 2 then Paper else Rock
  113. Paper -> if (numNs Scissors) > 2 then Scissors else Paper
  114. Scissors -> if (numNs Rock) > 2 then Rock else Scissors
  115. where
  116. numNs c = numMatch c $ grabNeighbors s