module Solutions.GuildsOfRavnica8 where

import Control.Monad.Except (throwError, when)
import Control.Lens
import qualified Data.Set as S

import Dovin.V1

-- http://www.possibilitystorm.com/089-guilds-of-ravnica-season-puzzle-7-2/
solution :: GameMonad ()
solution :: GameMonad ()
solution = do
  let black :: [Char]
black = [Char]
"black"

  -- This solutions relies on triggering Diamond Mare to gain life, which in
  -- turns triggers Epicure of Blood to cause the opponent to lose life. This
  -- helper can wrap cast actions with that combination.
  let withTriggers :: ([Char]
 -> ExceptT
      [Char] (ReaderT Env (StateT Board (WriterT [Step] Identity))) a)
-> [Char] -> GameMonad ()
withTriggers = \[Char]
-> ExceptT
     [Char] (ReaderT Env (StateT Board (WriterT [Step] Identity))) a
action [Char]
name -> do
        [Char]
-> ExceptT
     [Char] (ReaderT Env (StateT Board (WriterT [Step] Identity))) a
action [Char]
name
        [Char] -> GameMonad ()
trigger [Char]
"Diamond Mare"
        Card
c <- [Char] -> CardMatcher -> GameMonad Card
requireCard [Char]
name CardMatcher
forall a. Monoid a => a
mempty

        Bool -> GameMonad () -> GameMonad ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ([Char] -> Card -> Bool
hasAttribute [Char]
black Card
c) (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ do
          Int -> GameMonad ()
gainLife Int
1
          [Char] -> GameMonad ()
trigger [Char]
"Epicure of Blood"
          Player -> GameMonad () -> GameMonad ()
as Player
Opponent (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ Int -> GameMonad ()
loseLife Int
1

  -- Helper function to keep track of which permanent types have been cast
  -- using Muldrotha's ability.
  let castWithMuldrotha :: [Char] -> [Char] -> [Char] -> GameMonad ()
castWithMuldrotha = \[Char]
ptype [Char]
mana [Char]
cn -> do
        let ptypes :: Set [Char]
ptypes = [[Char]] -> Set [Char]
forall a. Ord a => [a] -> Set a
S.fromList [[Char]
"artifact", [Char]
"creature", [Char]
"land", [Char]
"enchantment"]
        let counterName :: [Char]
counterName = [Char]
"muldrotha-" [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
ptype

        Bool -> GameMonad () -> GameMonad ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [Char] -> Set [Char] -> Bool
forall a. Ord a => a -> Set a -> Bool
S.member [Char]
ptype Set [Char]
ptypes) (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$
          [Char] -> GameMonad ()
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError ([Char] -> GameMonad ()) -> [Char] -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ [Char]
"Invalid permanent type: " [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
ptype

        Int
n <- Getting Int Board Int
-> ExceptT
     [Char] (ReaderT Env (StateT Board (WriterT [Step] Identity))) Int
forall s (m :: * -> *) a. MonadState s m => Getting a s a -> m a
use ((HashMap [Char] Int -> Const Int (HashMap [Char] Int))
-> Board -> Const Int Board
Lens' Board (HashMap [Char] Int)
counters ((HashMap [Char] Int -> Const Int (HashMap [Char] Int))
 -> Board -> Const Int Board)
-> ((Int -> Const Int Int)
    -> HashMap [Char] Int -> Const Int (HashMap [Char] Int))
-> Getting Int Board Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Index (HashMap [Char] Int)
-> Lens'
     (HashMap [Char] Int) (Maybe (IxValue (HashMap [Char] Int)))
forall m. At m => Index m -> Lens' m (Maybe (IxValue m))
at [Char]
Index (HashMap [Char] Int)
counterName ((Maybe Int -> Const Int (Maybe Int))
 -> HashMap [Char] Int -> Const Int (HashMap [Char] Int))
-> ((Int -> Const Int Int) -> Maybe Int -> Const Int (Maybe Int))
-> (Int -> Const Int Int)
-> HashMap [Char] Int
-> Const Int (HashMap [Char] Int)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Iso' (Maybe Int) Int
forall a. Eq a => a -> Iso' (Maybe a) a
non Int
0)

        if Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0 then
          [Char] -> GameMonad ()
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError ([Char] -> GameMonad ()) -> [Char] -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ [Char]
"Already cast card of type with Muldrotha: " [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
ptype
        else
          do
            CardLocation -> [Char] -> [Char] -> GameMonad ()
castFromLocation (Player
Active, Location
Graveyard) [Char]
mana [Char]
cn
            [Char] -> GameMonad ()
resolve [Char]
cn
            ASetter Board Board (Maybe Int) (Maybe Int)
-> Maybe Int -> GameMonad ()
forall s (m :: * -> *) a b.
MonadState s m =>
ASetter s s a b -> b -> m ()
assign ((HashMap [Char] Int -> Identity (HashMap [Char] Int))
-> Board -> Identity Board
Lens' Board (HashMap [Char] Int)
counters ((HashMap [Char] Int -> Identity (HashMap [Char] Int))
 -> Board -> Identity Board)
-> ((Maybe Int -> Identity (Maybe Int))
    -> HashMap [Char] Int -> Identity (HashMap [Char] Int))
-> ASetter Board Board (Maybe Int) (Maybe Int)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Index (HashMap [Char] Int)
-> Lens'
     (HashMap [Char] Int) (Maybe (IxValue (HashMap [Char] Int)))
forall m. At m => Index m -> Lens' m (Maybe (IxValue m))
at [Char]
Index (HashMap [Char] Int)
counterName) (Int -> Maybe Int
forall a. a -> Maybe a
Just Int
1)

  [Char] -> GameMonad () -> GameMonad ()
forall a. [Char] -> GameMonad a -> GameMonad a
step [Char]
"Initial state" (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ do
    Player -> GameMonad () -> GameMonad ()
as Player
Opponent (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ Int -> GameMonad ()
setLife Int
7

    CardLocation -> GameMonad () -> GameMonad ()
withLocation (Player
Active, Location
Play) (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ do
      (Int, Int) -> [Char] -> GameMonad ()
addCreature (Int
4, Int
4) [Char]
"Epicure of Blood"
      (Int, Int) -> [Char] -> GameMonad ()
addCreature (Int
6, Int
6) [Char]
"Muldrotha, the Gravetide"

      Int -> [Char] -> GameMonad ()
addLands Int
3 [Char]
"Memorial to Folly"
      Int -> [Char] -> GameMonad ()
addLands Int
4 [Char]
"Watery Grave"
      Int -> [Char] -> GameMonad ()
addLands Int
4 [Char]
"Overgrown Tomb"

    CardLocation -> GameMonad () -> GameMonad ()
withLocation (Player
Active, Location
Graveyard) (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ do
      [Char] -> GameMonad () -> GameMonad ()
withAttribute [Char]
artifact (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ (Int, Int) -> [Char] -> GameMonad ()
addCreature (Int
1, Int
3) [Char]
"Diamond Mare"
      [Char] -> GameMonad ()
addLand [Char]
"Detection Tower"
      [Char] -> GameMonad ()
addArtifact [Char]
"Mox Amber"
      [Char] -> GameMonad () -> GameMonad ()
withAttribute [Char]
black (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ (Int, Int) -> [Char] -> GameMonad ()
addCreature (Int
1, Int
2) [Char]
"Vicious Conquistador"
      (Int, Int) -> [Char] -> GameMonad ()
addCreature (Int
1, Int
4) [Char]
"Sailor of Means"

    CardLocation -> GameMonad () -> GameMonad ()
withLocation (Player
Active, Location
Hand) (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ [Char] -> GameMonad () -> GameMonad ()
withAttribute [Char]
black (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ do
      [Char] -> GameMonad ()
addSorcery [Char]
"March of the Drowned"
      [Char] -> GameMonad ()
addSorcery [Char]
"Gruesome Menagerie"
      [Char] -> GameMonad ()
addAura [Char]
"Dead Weight"
      [Char] -> GameMonad ()
addSorcery [Char]
"Find"

  [Char] -> GameMonad () -> GameMonad ()
forall a. [Char] -> GameMonad a -> GameMonad a
step [Char]
"Detection Tower, Mox Amber, Diamond Mare from graveyard" (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ do
    [Char] -> [Char] -> [Char] -> GameMonad ()
castWithMuldrotha [Char]
"land" [Char]
"" [Char]
"Detection Tower"
    [Char] -> [Char] -> [Char] -> GameMonad ()
castWithMuldrotha [Char]
"artifact" [Char]
"" [Char]
"Mox Amber"
    [Char] -> [Char] -> GameMonad ()
tapForMana [Char]
"1" [Char]
"Detection Tower"
    [Char] -> [Char] -> GameMonad ()
tapForMana [Char]
"1" [Char]
"Mox Amber"
    [Char] -> [Char] -> [Char] -> GameMonad ()
castWithMuldrotha [Char]
"creature" [Char]
"2" [Char]
"Diamond Mare"

  [Char] -> GameMonad () -> GameMonad ()
forall a. [Char] -> GameMonad a -> GameMonad a
step [Char]
"March of the Drowned on Vicious Conquistador" (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ do
    [Char] -> [Char] -> GameMonad ()
tapForMana [Char]
"B" [Char]
"Memorial to Folly 1"
    ([Char] -> GameMonad ()) -> [Char] -> GameMonad ()
forall a.
([Char]
 -> ExceptT
      [Char] (ReaderT Env (StateT Board (WriterT [Step] Identity))) a)
-> [Char] -> GameMonad ()
withTriggers ([Char] -> [Char] -> GameMonad ()
cast [Char]
"B") [Char]
"March of the Drowned"
    [Char] -> GameMonad ()
resolve [Char]
"March of the Drowned"
    Location -> [Char] -> GameMonad ()
moveTo Location
Hand [Char]
"Vicious Conquistador"

  [Char] -> GameMonad () -> GameMonad ()
forall a. [Char] -> GameMonad a -> GameMonad a
step [Char]
"Vicious Conquistador" (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ do
    [Char] -> [Char] -> GameMonad ()
tapForMana [Char]
"B" [Char]
"Memorial to Folly 2"
    ([Char] -> GameMonad ()) -> [Char] -> GameMonad ()
forall a.
([Char]
 -> ExceptT
      [Char] (ReaderT Env (StateT Board (WriterT [Step] Identity))) a)
-> [Char] -> GameMonad ()
withTriggers ([Char] -> [Char] -> GameMonad ()
cast [Char]
"B") [Char]
"Vicious Conquistador"
    [Char] -> GameMonad ()
resolve [Char]
"Vicious Conquistador"

  [Char] -> GameMonad () -> GameMonad ()
forall a. [Char] -> GameMonad a -> GameMonad a
step [Char]
"Dead Weight on Vicious Conquistador" (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ do
    [Char] -> [Char] -> GameMonad ()
tapForMana [Char]
"B" [Char]
"Memorial to Folly 3"
    ([Char] -> GameMonad ()) -> [Char] -> GameMonad ()
forall a.
([Char]
 -> ExceptT
      [Char] (ReaderT Env (StateT Board (WriterT [Step] Identity))) a)
-> [Char] -> GameMonad ()
withTriggers ([Char] -> [Char] -> GameMonad ()
cast [Char]
"B") [Char]
"Dead Weight"
    [Char] -> GameMonad ()
target [Char]
"Vicious Conquistador"
    [Char] -> GameMonad ()
resolve [Char]
"Dead Weight"
    (Int, Int) -> [Char] -> GameMonad ()
modifyStrength (-Int
2, -Int
2) [Char]
"Vicious Conquistador"
    Location -> [Char] -> GameMonad ()
moveTo Location
Graveyard [Char]
"Dead Weight"

  [Char] -> GameMonad () -> GameMonad ()
forall a. [Char] -> GameMonad a -> GameMonad a
step [Char]
"Gruesome Menagerie for Sailor of Means and Vicious Conquistador" (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ do
    [Char] -> [Char] -> GameMonad ()
tapForMana [Char]
"B" [Char]
"Watery Grave 1"
    [Char] -> [Char] -> GameMonad ()
tapForMana [Char]
"B" [Char]
"Watery Grave 2"
    [Char] -> [Char] -> GameMonad ()
tapForMana [Char]
"B" [Char]
"Watery Grave 3"
    [Char] -> [Char] -> GameMonad ()
tapForMana [Char]
"B" [Char]
"Watery Grave 4"
    [Char] -> [Char] -> GameMonad ()
tapForMana [Char]
"B" [Char]
"Overgrown Tomb 1"
    ([Char] -> GameMonad ()) -> [Char] -> GameMonad ()
forall a.
([Char]
 -> ExceptT
      [Char] (ReaderT Env (StateT Board (WriterT [Step] Identity))) a)
-> [Char] -> GameMonad ()
withTriggers ([Char] -> [Char] -> GameMonad ()
cast [Char]
"3BB") [Char]
"Gruesome Menagerie"
    [Char] -> GameMonad ()
resolve [Char]
"Gruesome Menagerie"
    CardLocation -> [Char] -> GameMonad ()
targetInLocation (Player
Active, Location
Graveyard) [Char]
"Vicious Conquistador"
    CardLocation -> [Char] -> GameMonad ()
targetInLocation (Player
Active, Location
Graveyard) [Char]
"Sailor of Means"
    Location -> [Char] -> GameMonad ()
moveTo Location
Play [Char]
"Vicious Conquistador"
    Location -> [Char] -> GameMonad ()
moveTo Location
Play [Char]
"Sailor of Means"
    CardLocation -> GameMonad () -> GameMonad ()
withLocation (Player
Active, Location
Play)
      (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ [Char] -> GameMonad () -> GameMonad ()
withAttribute [Char]
token
      (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ [Char] -> GameMonad ()
addArtifact [Char]
"Treasure"

  [Char] -> GameMonad () -> GameMonad ()
forall a. [Char] -> GameMonad a -> GameMonad a
step [Char]
"Dead Weight on Vicious Conquistador" (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ do
    [Char] -> [Char] -> GameMonad ()
tapForMana [Char]
"B" [Char]
"Overgrown Tomb 2"
    ([Char] -> GameMonad ()) -> [Char] -> GameMonad ()
forall a.
([Char]
 -> ExceptT
      [Char] (ReaderT Env (StateT Board (WriterT [Step] Identity))) a)
-> [Char] -> GameMonad ()
withTriggers ([Char] -> [Char] -> [Char] -> GameMonad ()
castWithMuldrotha [Char]
"enchantment" [Char]
"B") [Char]
"Dead Weight"
    [Char] -> GameMonad ()
target [Char]
"Vicious Conquistador"
    (Int, Int) -> [Char] -> GameMonad ()
modifyStrength (-Int
2, -Int
2) [Char]
"Vicious Conquistador"
    Location -> [Char] -> GameMonad ()
moveTo Location
Graveyard [Char]
"Dead Weight"

  [Char] -> GameMonad () -> GameMonad ()
forall a. [Char] -> GameMonad a -> GameMonad a
step [Char]
"Find for Vicous Conquistador" (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ do
    [Char] -> [Char] -> GameMonad ()
tapForMana [Char]
"B" [Char]
"Overgrown Tomb 3"
    [Char] -> [Char] -> GameMonad ()
tapForMana [Char]
"B" [Char]
"Overgrown Tomb 4"
    ([Char] -> GameMonad ()) -> [Char] -> GameMonad ()
forall a.
([Char]
 -> ExceptT
      [Char] (ReaderT Env (StateT Board (WriterT [Step] Identity))) a)
-> [Char] -> GameMonad ()
withTriggers ([Char] -> [Char] -> GameMonad ()
cast [Char]
"BB") [Char]
"Find"
    [Char] -> GameMonad ()
resolve [Char]
"Find"
    CardLocation -> [Char] -> GameMonad ()
targetInLocation (Player
Active, Location
Graveyard) [Char]
"Vicious Conquistador"
    Location -> [Char] -> GameMonad ()
moveTo Location
Hand [Char]
"Vicious Conquistador"

  [Char] -> GameMonad () -> GameMonad ()
forall a. [Char] -> GameMonad a -> GameMonad a
step [Char]
"Vicious Conquistador" (GameMonad () -> GameMonad ()) -> GameMonad () -> GameMonad ()
forall a b. (a -> b) -> a -> b
$ do
    [Char] -> [Char] -> GameMonad ()
tapForMana [Char]
"B" [Char]
"Treasure"
    [Char] -> GameMonad ()
sacrifice [Char]
"Treasure"
    ([Char] -> GameMonad ()) -> [Char] -> GameMonad ()
forall a.
([Char]
 -> ExceptT
      [Char] (ReaderT Env (StateT Board (WriterT [Step] Identity))) a)
-> [Char] -> GameMonad ()
withTriggers ([Char] -> [Char] -> GameMonad ()
cast [Char]
"B") [Char]
"Vicious Conquistador"
    [Char] -> GameMonad ()
resolve [Char]
"Vicious Conquistador"

    Player -> Int -> GameMonad ()
validateLife Player
Opponent Int
0

formatter :: Int -> Formatter
formatter :: Int -> Formatter
formatter Int
_ = FormatMonad () -> Formatter
attributeFormatter (FormatMonad () -> Formatter) -> FormatMonad () -> Formatter
forall a b. (a -> b) -> a -> b
$ do
  [Char]
-> ExceptT
     [Char] (ReaderT Env (StateT Board (WriterT [Step] Identity))) Int
-> FormatMonad ()
forall a. Show a => [Char] -> GameMonad a -> FormatMonad ()
attribute [Char]
"mana" (ExceptT
   [Char] (ReaderT Env (StateT Board (WriterT [Step] Identity))) Int
 -> FormatMonad ())
-> ExceptT
     [Char] (ReaderT Env (StateT Board (WriterT [Step] Identity))) Int
-> FormatMonad ()
forall a b. (a -> b) -> a -> b
$ CardMatcher
-> ExceptT
     [Char] (ReaderT Env (StateT Board (WriterT [Step] Identity))) Int
countCards ([Char] -> CardMatcher
matchAttribute [Char]
"land" CardMatcher -> CardMatcher -> CardMatcher
forall a. Semigroup a => a -> a -> a
<> [Char] -> CardMatcher
missingAttribute [Char]
"tapped")
  [Char]
-> ExceptT
     [Char] (ReaderT Env (StateT Board (WriterT [Step] Identity))) Int
-> FormatMonad ()
forall a. Show a => [Char] -> GameMonad a -> FormatMonad ()
attribute [Char]
"life" (ExceptT
   [Char] (ReaderT Env (StateT Board (WriterT [Step] Identity))) Int
 -> FormatMonad ())
-> ExceptT
     [Char] (ReaderT Env (StateT Board (WriterT [Step] Identity))) Int
-> FormatMonad ()
forall a b. (a -> b) -> a -> b
$ Player
-> ExceptT
     [Char] (ReaderT Env (StateT Board (WriterT [Step] Identity))) Int
countLife Player
Opponent