packages feed

game-of-life-0.1.0.5: test/Spec.hs

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