content: {-# 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
  import qualified Data.Map.Lazy as M
  ---------------------------
  -- CARD
  ---------------------------
  
  data CardType = Action
                | Attack
                | Reaction
                | Treasure
                | Victory
                | Duration
                deriving (Show, Eq, Ord)
  
  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, Ord)
  
  data Card = Card {
                _name     :: String,
                _cost     :: Int,
                _cardType :: [CardType],
                _effects  :: [CardEffect]
  } deriving (Show, Eq, Ord)
  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],
                      -- | all the cards still in play.
                      _cards   :: M.Map Card Int,
                      -- | round number
                      _round   :: Int,
                      _verbose :: Bool
  } 
  makeLenses ''GameState
  
  instance Show GameState where
    show gs = "GameState {players: " ++ show (_players gs)
              ++ ", cards: " ++ show (M.mapKeys _name (_cards gs))
              ++ ", round: " ++ show (_round gs)
              ++ ", verbose: " ++ show (_verbose gs) ++ "}"
  
  -- 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)
  