diff --git a/spec/Main.hs b/spec/Main.hs index 9376db0..b416565 100644 --- a/spec/Main.hs +++ b/spec/Main.hs @@ -1,23 +1,41 @@ -import Test.Hspec -import Dominion -import Dominion.Cards -import qualified Dominion.Types as T -import Control.Applicative +import Control.Applicative +import Control.Lens hiding (has, indices, uses) +import Control.Monad.State hiding (state) +import Dominion +import qualified Dominion.Cards as CA +import Dominion.Internal +import qualified Dominion.Types as T +import Prelude hiding (log) +import Test.Hspec -bigMoney playerId = playerId `buysByPreference` [province, gold, duchy, silver, copper] -stupidStrategy playerId = playerId `buysByPreference` [province, gold] +-- | Use this to run a strategy once and inspect the gamestate. +runOnce :: T.Player -> T.Strategy -> IO T.GameState +runOnce player strategy = do + state <- makeGameState [] [player] + snd <$> runStateT (game [strategy]) state + +-- | Give a hand of cards and a function. You play one round +-- with this given hand of cards. The function returns True or False. +withHand :: [T.Card] -> (T.PlayerId -> T.Dominion Bool) -> IO Bool +withHand hand func = do + let player = T.Player "testPlayer" (7 `cardsOf` CA.copper ++ 3 `cardsOf` CA.estate) [] hand 1 1 0 + state <- makeGameState [] [player] + fst <$> runStateT (func 0) state + +bigMoney playerId = playerId `buysByPreference` [CA.province, CA.gold, CA.duchy, CA.silver, CA.copper] +stupidStrategy playerId = playerId `buysByPreference` [CA.province, CA.gold] bigMoney2 playerId = do roundNum <- getRound if (roundNum < 6) - then playerId `buysByPreference` [province, gold, silver] + then playerId `buysByPreference` [CA.province, CA.gold, CA.silver] else bigMoney playerId bigMoneySmithy playerId = do - playerId `plays` smithy + playerId `plays` CA.smithy roundNum <- getRound if (roundNum < 6) - then playerId `buysByPreference` [province, gold, smithy, silver] + then playerId `buysByPreference` [CA.province, CA.gold, CA.smithy, CA.silver] else bigMoney playerId io = flip shouldReturn @@ -38,5 +56,13 @@ main = do it "bigMoneySmithy should win 1.1 times as often as bigMoney2" $ do io True $ do - winners <- map T.winner <$> dominionWithOpts [Cards [smithy]] ["sherlock" `uses` bigMoneySmithy, "watson" `uses` bigMoney2] + winners <- map T.winner <$> dominionWithOpts [Cards [CA.smithy]] ["sherlock" `uses` bigMoneySmithy, "watson" `uses` bigMoney2] return $ (count "sherlock" winners) >= (1.1 * (count "watson" winners)) + + -- silly spec, shows an example of how to use `withHand` + it "market should add to the players cards, buys, money, and actions" $ do + io True $ do + withHand [CA.market, CA.copper, CA.copper, CA.estate, CA.estate] $ \playerId -> do + playerId `plays` CA.market + player <- getPlayer playerId + return $ player ^. T.actions == 1 && player ^. T.extraMoney == 1 && player ^. T.buys == 2 diff --git a/src/Dominion.hs b/src/Dominion.hs index 07fd0c9..1295558 100644 --- a/src/Dominion.hs +++ b/src/Dominion.hs @@ -40,13 +40,9 @@ dominion = dominionWithOpts [] -- > dominionWithOpts [Iterations 5, Log True] ["adit" `uses` bigMoney, "maggie" `uses` bigMoney] dominionWithOpts :: [T.Option] -> [(T.Player, T.Strategy)] -> IO [T.Result] dominionWithOpts options list = do - actionCards_ <- deckShuffle CA.allActionCards - let actionCards = take (10 - length requiredCards) actionCards_ ++ requiredCards - cards = M.fromList ([(CA.copper, 60), (CA.silver, 40), (CA.gold, 30), - (CA.estate, 12), (CA.duchy, 12), (CA.province, 12)] - ++ [(c, 10) | c <- actionCards]) - when verbose_ $ putStrLn $ "Playing with: " ++ (join ", " . map T._name $ actionCards) - results <- forM [1..iterations] $ \i -> run (T.GameState (rotate i players) cards 1 verbose_) (rotate i strategies) + results <- forM [1..iterations] $ \i -> do + gameState <- makeGameState options (rotate i players) + run gameState (rotate i strategies) let winnerNames = map T.winner results forM_ players $ \player -> do let name = player ^. T.playerName @@ -54,9 +50,7 @@ dominionWithOpts options list = do return results where (players, strategies) = unzip list iterations = fromMaybe 1000 (findIteration options) - verbose_ = fromMaybe False (findLog options) - requiredCards = take 10 $ fromMaybe [] (findCards options) - + -- | Player buys a card. Example: -- -- > playerId `buys` smithy diff --git a/src/Dominion/Internal.hs b/src/Dominion/Internal.hs index 372cebd..1ab42d1 100644 --- a/src/Dominion/Internal.hs +++ b/src/Dominion/Internal.hs @@ -9,11 +9,12 @@ module Dominion.Internal ( import Control.Applicative import Control.Arrow import Control.Lens hiding (has, indices) -import Control.Monad (liftM) +import Control.Monad (liftM) import Control.Monad.State hiding (state) import Data.List import Data.Map.Lazy ((!)) import qualified Data.Map.Lazy as M +import Data.Maybe import Data.Ord import qualified Dominion.Cards as CA import qualified Dominion.Types as T @@ -158,6 +159,17 @@ playTurn playerId strategy = do -- even if its not their turn. setupForTurn playerId +makeGameState :: [T.Option] -> [T.Player] -> IO T.GameState +makeGameState options players = do + actionCards_ <- deckShuffle CA.allActionCards + let requiredCards = take 10 $ fromMaybe [] (findCards options) + verbose = fromMaybe False (findLog options) + actionCards = take (10 - length requiredCards) actionCards_ ++ requiredCards + cards = M.fromList ([(CA.copper, 60), (CA.silver, 40), (CA.gold, 30), + (CA.estate, 12), (CA.duchy, 12), (CA.province, 12)] + ++ [(c, 10) | c <- actionCards]) + return $ T.GameState players cards 1 verbose + game :: [T.Strategy] -> T.Dominion () game strategies = do state <- get