module Main where
import Test.Hspec
import Simulation
import Data.Array
main :: IO ()
main = hspec spec
spec :: Spec
spec = do
describe "stepCell" $ do
it "revives dead cells if num_neighbors is 3" $ do
(stepCell Dead 3) `shouldBe` Alive
it "does not revive dead cells ∀ num_neighbors in [0,3) ∪ (3,8]" $ do
mapM_ (\x -> (stepCell Dead x) `shouldBe` Dead) ([0..2] ++ [4..8])
it "kills live cells ∀ num_neighbors in [0,1] ∪ [4,8]" $ do
mapM_ (\x -> (stepCell Alive x) `shouldBe` Dead) ([0..1] ++ [4..8])
it "keeps cells alive ∀ num_neighbors in [2,3]" $ do
mapM_ (\x -> (stepCell Alive x) `shouldBe` Alive) [2,3]
describeStep
describeStep :: Spec
describeStep = do
describe "step" $ do
it "should handle a very simple board" $ do
let start = [
[0, 0, 0],
[0, 1, 0],
[0, 0, 0]
]
let expected_end = [
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
]
shouldBe True $
test_game start 1 expected_end
it "Should progress correctly" $ do
let zeroth_step = [
[0, 1, 0],
[1, 1, 1],
[0, 1, 0]
]
let first_step = [
[1, 1, 1],
[1, 0, 1],
[1, 1, 1]
]
let second_step = [
[1, 0, 1],
[0, 0, 0],
[1, 0, 1]
]
let board = toBoard zeroth_step
let two_steps = fmap toTestFormat $ (take 3 . iterate step) board
shouldBe two_steps [zeroth_step, first_step, second_step]
test_game :: [[Int]] -> Int -> [[Int]] -> Bool
test_game input n expected = toTestFormat (iterate step board !! n) == expected
where
board = toBoard input
-- Everything below is glue to convert to and from the test
-- format (which is easier to type and see visually), and
-- the actual type of the board
toTestFormat :: Board -> [[Int]]
toTestFormat board = (fmap . fmap) stateToInt $ elems (fmap elems (bGrid board))
toBoard :: [[Int]] -> Board
toBoard input = Board {bGrid = grid, bRows = rows, bCols = cols}
where
grid = listArray (0, length input-1) $ map rowToArray input
rowToArray row = listArray (0, length row-1) $ map intToState row
rows = length input
-- This is a partial function, but it's only used for
-- tests and an empty array isn't valid input
cols = length (input !! 0)
stateToInt :: State -> Int
stateToInt Alive = 1
stateToInt Dead = 0
intToState :: Int -> State
intToState 1 = Alive
intToState _ = Dead