h2048-0.4.0.0: test/Game/H2048/CoreSpec.hs
{-# LANGUAGE TupleSections #-}
module Game.H2048.CoreSpec
( spec
, listToGameBoard
, gameBoardToList
) where
import Control.Monad
import Data.Bifunctor
import Test.Hspec
import qualified Data.IntMap as IM
import qualified Data.Map as M
import qualified Data.Vector as V
import qualified Data.Set as S
import Game.H2048.Core
listToGameBoard :: (Int, Int) -> [[Int]] -> GameBoard
listToGameBoard (rows,cols) grid =
M.fromList $ zip coords (convert <$> concat grid) >>= unwrap
where
coords = (,) <$> [0..rows-1] <*> [0..cols-1]
convert v =
if v == 0
then Nothing
else Just (unsafeIntToCell v)
unwrap (coord, m) = case m of
Nothing -> []
Just m' -> [(coord, m')]
gameBoardToList :: (Int, Int) -> GameBoard -> [[Int]]
gameBoardToList (rows,cols) gb =
(\r -> convert r <$> [0..cols-1]) <$> [0..rows-1]
where
convert rInd cInd = case M.lookup (rInd,cInd) gb of
Nothing -> 0
Just c -> cellToInt c
spec :: Spec
spec = do
let gr = standardGameRule
describe "merge" $ do
specify "no merge" $ do
merge (Cell 1) (Cell 2) `shouldBe` Nothing
merge (Cell 6) (Cell 5) `shouldBe` Nothing
specify "merges" $ do
merge (Cell 1) (Cell 1) `shouldBe` Just (Cell 2)
merge (Cell 4) (Cell 4) `shouldBe` Just (Cell 5)
describe "mergeWithScore" $
specify "examples" $ do
mergeWithScore gr (Cell 1) (Cell 1)
`shouldBe` Just (Cell 2, 4)
mergeWithScore gr (Cell 4) (Cell 4)
`shouldBe` Just (Cell 5, 32)
describe "mergeLines" $
specify "examples" $ do
let mergeLine' =
first (fmap cellToInt)
. mergeLine gr
. fmap unsafeIntToCell
mergeLine' [] `shouldBe` ([], 0)
mergeLine' [1,1] `shouldBe` ([2], 2)
mergeLine' [2,2,4] `shouldBe` ([4,4], 4)
mergeLine' [4,4,4] `shouldBe` ([8,4], 8)
mergeLine' [4,8,8] `shouldBe` ([4,16], 16)
mergeLine' [4,2,2,8] `shouldBe` ([4,4,8], 4)
mergeLine' [2,4,4,2] `shouldBe` ([2,8,2], 8)
mergeLine' [2,2,2,2] `shouldBe` ([4,4], 8)
mergeLine' [1,2] `shouldBe` ([1,2], 0)
describe "computeDistrib" $
specify "examples" $
computeDistrib (IM.fromList [(1,2),(2,5),(3,7),(4,6)])
`shouldBe` V.fromList [(1,2),(2,7),(3,14),(4,20)]
describe "allCoords" $ do
specify "standard" $
(S.toAscList . S.fromList . allCoords $ gr) `shouldBe`
allCoords gr
specify "non-square" $
let gr' = gr { _grDim = (4,5) }
in (S.toAscList . S.fromList . allCoords $ gr') `shouldBe`
allCoords gr'
describe "dirToCoordsGroups" $ do
specify "standard game examples" $ do
let testCase dir expected =
dirToCoordsGroups gr dir
`shouldBe` expected
testCase DUp $
fmap (\c -> fmap (,c) [0..3]) [0..3]
testCase DDown $
fmap (\c -> fmap (,c) [3,2..0]) [0..3]
testCase DLeft $
fmap (\r -> fmap (r,) [0..3]) [0..3]
testCase DRight $
fmap (\r -> fmap (r,) [3,2..0]) [0..3]
specify "non-square examples" $ do
let testCase dir expected =
dirToCoordsGroups (gr {_grDim = (3,5)}) dir
`shouldBe` expected
testCase DUp $
fmap (\c -> fmap (,c) [0..2]) [0..4]
testCase DDown $
fmap (\c -> fmap (,c) [2,1,0]) [0..4]
testCase DLeft $
fmap (\r -> fmap (r,) [0..4]) [0..2]
testCase DRight $
fmap (\r -> fmap (r,) [4,3..0]) [0..2]
describe "applyMove" $
describe "examples" $ do
let toGB = listToGameBoard (_grDim gr)
-- used for testing while describing board as a list
testCase listBd dir expected =
(fmap . first) (gameBoardToList (_grDim gr))
(applyMove gr dir (toGB listBd))
`shouldBe` expected
-- for making it convenient to test for multiple
-- directions on the same board configuration.
testCases descPref listBd expects =
forM_ expects $ \(dir, expectedListBd) ->
specify (descPref <> ", " <> show dir) $
testCase listBd dir expectedListBd
testCases "case #0"
[ [2,2,2,2]
, [2,4,4,2]
, [2,4,4,2]
, [2,2,2,2]
]
[ ( DUp
, Just ([ [4,2,2,4]
, [4,8,8,4]
, [0,2,2,0]
, [0,0,0,0]
], 32))
, ( DDown
, Just ([ [0,0,0,0]
, [0,2,2,0]
, [4,8,8,4]
, [4,2,2,4]
], 32))
, ( DLeft
, Just ([ [4,4,0,0]
, [2,8,2,0]
, [2,8,2,0]
, [4,4,0,0]
], 32))
, ( DRight
, Just ([ [0,0,4,4]
, [0,2,8,2]
, [0,2,8,2]
, [0,0,4,4]
], 32))
]
testCases "case #1"
[ [2,2,4,4]
, [2,2,4,4]
, [8,8,0,0]
, [8,8,0,0]
]
[ ( DUp
, Just ([ [ 4, 4 ,8, 8]
, [16,16, 0, 0]
, [ 0, 0, 0, 0]
, [ 0, 0, 0, 0]
], 56))
, ( DDown
, Just ([ [ 0, 0, 0, 0]
, [ 0, 0, 0, 0]
, [ 4, 4, 0, 0]
, [16,16, 8, 8]
], 56))
, ( DLeft
, Just ([ [ 4, 8, 0, 0]
, [ 4, 8, 0, 0]
, [16, 0, 0, 0]
, [16, 0, 0, 0]
], 56))
, ( DRight
, Just ([ [ 0, 0, 4, 8]
, [ 0, 0, 4 ,8]
, [ 0, 0, 0,16]
, [ 0, 0, 0,16]
], 56))
]
testCases "case #2"
[ [0,0,0,0]
, [2,0,0,0]
, [4,4,0,0]
, [8,2,8,0]
]
[ ( DUp
, Just ([ [2,4,8,0]
, [4,2,0,0]
, [8,0,0,0]
, [0,0,0,0]
], 0))
, ( DDown
, Nothing)
, ( DLeft
, Just ([ [0,0,0,0]
, [2,0,0,0]
, [8,0,0,0]
, [8,2,8,0]
], 8))
, ( DRight
, Just ([ [0,0,0,0]
, [0,0,0,2]
, [0,0,0,8]
, [0,8,2,8]
], 8))
]
testCases "no more moves"
[ [2,4,2,4]
, [4,2,4,2]
, [8,4,8,4]
, [2,8,4,2]
] $ fmap (,Nothing) [DUp, DDown, DLeft, DRight]
testCases "uninitialized"
(replicate 4 (replicate 4 0)) $
fmap (,Nothing) [DUp, DDown, DLeft, DRight]