{-# LANGUAGE TemplateHaskell #-} module Dominion.Types ( -- | This module uses the `Lens` library. So you might notice that the -- fields for the constructors look strange: they all have underscores. -- Given a card, you can see the cost like this: -- -- > _cost card -- -- But you can also use a lens: -- -- > card ^. cost -- -- The lens library is very useful for modifying deeply nested data -- structures, and it's been very useful for this module. module Dominion.Types ) where import Control.Lens import Control.Monad.State --------------------------- -- CARD --------------------------- data CardType = Action | Attack | Reaction | Treasure | Victory | Duration deriving (Show, Eq) data CardEffect = CoinValue Int | VPValue Int | PlusCard Int | PlusCoin Int | PlusBuy Int | PlusAction Int | DurationDraw Int | DurationAction Int | DurationCoin Int | DurationBuy Int | TrashCards Int | TrashThisCard | GainCardUpto Int | PlayActionCard Int | AdventurerEffect | BureaucratEffect | CellarEffect | ChancellorEffect | GardensEffect | LibraryEffect | MineEffect | MoneylenderEffect | RemodelEffect | SpyEffect | ThiefEffect | OthersPlusCard Int | OthersDiscardTo Int | OthersGainCurse Int deriving (Show, Eq) data Card = Card { _name :: String, _cost :: Int, _cardType :: [CardType], _effects :: [CardEffect] } deriving (Show, Eq) makeLenses ''Card -- | Used with the `thief` card. data ThiefTrashAction = TrashOnly Card | GainTrashedCard Card -- | Some cards have a followup action associated with them. For example, -- when you play a `workshop`, you need to choose what card you're going to -- get. To use the followup action, you need to use the relevant data -- constructor. See the documentation for each card to find out how to use -- each type of `FollowupAction`. data FollowupAction = ThroneRoom Card -- | Takes a list of cards to discard. | Cellar [Card] -- | Boolean value representing whether you want to -- move your deck into the discard pile. | Chancellor Bool -- | Takes a list of cards to trash. | Chapel [Card] -- | Takes the card you want to gain. | Feast Card -- | Takes the card you want to trash. | Mine Card -- | The first card is the card you are trashing, the -- second card is the card you are gaining. | Remodel (Card, Card) -- | The first element is the list of cards you would discard for yourself, -- the second is the lsit of cards you want others to discard. | Spy ([Card], [Card]) -- | The function gets a list of treasure cards. -- had. You return either `TrashOnly` to have the player -- trash a card, or `GainTrashedCard` to gain the trashed -- card. This function is called for every other -- player in the game. | Thief ([Card] -> ThiefTrashAction) -- | Takes the card you want to gain. | Workshop Card --------------------------- -- PLAYER --------------------------- data Player = Player { _playerName :: String, _deck :: [Card], _discard :: [Card], _hand :: [Card], _actions :: Int, _buys :: Int, -- | Extra money gained from an action card (like +1 money -- from market). _extraMoney :: Int } deriving Show makeLenses ''Player type PlayerId = Int --------------------------- -- GAME STATE --------------------------- -- | This is what keeps track of all the state in the whole game. -- Get the round number like this: -- -- > state <- get -- > let roundNum = state ^. round data GameState = GameState { _players :: [Player], -- | list of all the cards still in play. _cards :: [Card], -- | round number _round :: Int, _verbose :: Bool } deriving Show makeLenses ''GameState -- The Dominion monad is just the `StateT` monad that has a `GameState` -- plus the IO monad. type Dominion a = StateT GameState IO a -- | Given a playerId, run some actions for this player. Example: -- -- > bigMoney playerId = playerId `buysByPreference` [province, gold, duchy, silver, copper] type Strategy = PlayerId -> Dominion () -- | When you use a card (either you play it or you buy something), -- you get a `PlayResult`. A `PlayResult` is either a `Left` with an error message, -- or a `Right` with a value. type PlayResult a = Either String a -- | When you play an action card that needs a decision on your part, -- `plays` will return a Followup. type Followup = (PlayerId, CardEffect) -- | You can set these options if you use `dominionWithOpts`. Example: -- -- > main = dominionWithOpts [Iterations 1, Log True, Cards [smithy]] ... data Option = -- | Number of iterations to run. Iterations Int -- | Enable logging | Log Bool -- | A list of cards that you definitely want in the game. -- Useful if you are testing a strategy that relies on -- a particular card. | Cards [Card] deriving (Show) -- | Each `PlayerResult` is a tuple of a player and their final score. type PlayerResult = (Player, Int) -- | Players and their scores. data Result = Result { playerResults :: [PlayerResult], winner :: String } deriving (Show)