chessIO-0.9.1.0: test/perft/Perft.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE NamedFieldPuns #-}
module Main where
import Control.Parallel.Strategies
import Data.MonoTraversable
import Data.Foldable
import Data.Maybe
import qualified Data.Vector.Unboxed as Vector
import Data.Monoid
import Data.Time.Clock
import Data.Traversable
import GHC.Generics (Generic)
import Game.Chess
import System.Directory
import System.Exit
import System.IO
type Depth = Int
type Testsuite = [(Position, [(Depth, PerftResult)])]
main :: IO ()
main = do
start <- getCurrentTime
exists <- doesFileExist "test/perft/perftsuite.epd"
result <- if exists
then do
suite <- readTestSuite "test/perft/perftsuite.epd"
runTestSuite suite
else do
fmap (Just . fold) . for [0..6] $ \n -> do
let r = perft n startpos
putStrLn $ showResult n r
hFlush stdout
pure r
end <- getCurrentTime
case result of
Just PerftResult{nodes} -> putStrLn $
"nps: " <>
show (floor (realToFrac (fromIntegral nodes) / realToFrac (diffUTCTime end start)))
_ -> pure ()
putStrLn $ "Time: " <> show (diffUTCTime end start)
exitWith $ if isJust result then ExitSuccess else ExitFailure 1
data PerftResult = PerftResult { nodes :: !Integer } deriving (Eq, Generic, Show)
instance NFData PerftResult
instance Semigroup PerftResult where
PerftResult n1 <> PerftResult n2 = PerftResult $ n1 + n2
instance Monoid PerftResult where
mempty = PerftResult 0
showResult :: Depth -> PerftResult -> String
showResult depth PerftResult{nodes} = show depth <> " " <> show nodes
perft :: Depth -> Position -> PerftResult
perft 0 _ = PerftResult 1
perft 1 p = PerftResult . fromIntegral . Vector.length $ legalPlies' p
perft n p
| n < 5
= Vector.foldMap' (perft (pred n) . unsafeDoPly p) $ legalPlies' p
| otherwise
= fold . parMap rdeepseq (perft (pred n) . unsafeDoPly p) $ legalPlies p
runTestSuite :: Testsuite -> IO (Maybe PerftResult)
runTestSuite = fmap (getAp . foldMap Ap) . traverse (uncurry (test mempty)) where
test sum pos ((depth, expected) : more)
| result == expected
= do
putStrLn $ "OK " <> fen <> " ;D" <> show depth <> " "
<> show (nodes expected)
hFlush stdout
test (sum <> result) pos more
| otherwise
= do
putStrLn $ "FAIL " <> fen <> " ;D" <> show depth <> " "
<> show (nodes expected) <> " /= " <> show (nodes result)
pure Nothing
where result = perft depth pos
fen = toFEN pos
test sum _ [] = pure (Just sum)
readTestSuite :: FilePath -> IO Testsuite
readTestSuite fp = do
epd <- readFile fp
pure $ fmap readData . (\ws -> (fromJust (fromFEN (unwords $ take 6 ws)), drop 6 ws)) . words <$> lines epd
where
readData [] = []
readData ((';':'D':d):v:xs) = (read d, PerftResult $ read v) : readData xs
readData _ = error "Failed to parse test suite"