{-# Language QuasiQuotes, ImportQualifiedPost, TemplateHaskell #-}
{-|
Module      : Main
Description : Day 2 solution
Copyright   : (c) Eric Mertens, 2023
License     : ISC
Maintainer  : emertens@gmail.com

<https://adventofcode.com/2023/day/2>

This problem has us observe multiple rounds of a game where marbles
are drawn from a bag. We have to compute the fewest number of each
color marble that are possible to support each game trace.

>>> :{
:main + "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green
"
:}
8
2286

-}
module Main (main) where

import Advent (format, stageTH)
import Data.Map (Map)
import Data.Map qualified as Map

data C = Cred | Cgreen | Cblue deriving (C -> C -> Bool
(C -> C -> Bool) -> (C -> C -> Bool) -> Eq C
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: C -> C -> Bool
== :: C -> C -> Bool
$c/= :: C -> C -> Bool
/= :: C -> C -> Bool
Eq, Eq C
Eq C =>
(C -> C -> Ordering)
-> (C -> C -> Bool)
-> (C -> C -> Bool)
-> (C -> C -> Bool)
-> (C -> C -> Bool)
-> (C -> C -> C)
-> (C -> C -> C)
-> Ord C
C -> C -> Bool
C -> C -> Ordering
C -> C -> C
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: C -> C -> Ordering
compare :: C -> C -> Ordering
$c< :: C -> C -> Bool
< :: C -> C -> Bool
$c<= :: C -> C -> Bool
<= :: C -> C -> Bool
$c> :: C -> C -> Bool
> :: C -> C -> Bool
$c>= :: C -> C -> Bool
>= :: C -> C -> Bool
$cmax :: C -> C -> C
max :: C -> C -> C
$cmin :: C -> C -> C
min :: C -> C -> C
Ord, Int -> C -> ShowS
[C] -> ShowS
C -> String
(Int -> C -> ShowS) -> (C -> String) -> ([C] -> ShowS) -> Show C
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> C -> ShowS
showsPrec :: Int -> C -> ShowS
$cshow :: C -> String
show :: C -> String
$cshowList :: [C] -> ShowS
showList :: [C] -> ShowS
Show)

stageTH

-- | Parse the input and print the answers to both parts.
--
-- >>> :main
-- 2169
-- 60948
main :: IO ()
IO ()
main =
 do input <- [format|2023 2 (Game %d: (%d @C)&(, )&(; )%n)*|]
    let summaries = [(Int
i, [[(Int, C)]] -> Map C Int
summarizeGame [[(Int, C)]]
rounds) | (Int
i, [[(Int, C)]]
rounds) <- [(Int, [[(Int, C)]])]
input]
    print (sum [i | (i, summary) <- summaries, Map.isSubmapOfBy (<=) summary part1])
    print (sum [product summary | (_, summary) <- summaries])

-- | Find the minimum marbles needed to play a whole game
summarizeGame :: [[(Int, C)]] -> Map C Int
summarizeGame :: [[(Int, C)]] -> Map C Int
summarizeGame [[(Int, C)]]
rs = (Int -> Int -> Int) -> [Map C Int] -> Map C Int
forall (f :: * -> *) k a.
(Foldable f, Ord k) =>
(a -> a -> a) -> f (Map k a) -> Map k a
Map.unionsWith Int -> Int -> Int
forall a. Ord a => a -> a -> a
max [[(Int, C)] -> Map C Int
summarizeRound [(Int, C)]
r | [(Int, C)]
r <- [[(Int, C)]]
rs]

-- | Find the minimum marbles needed to play round in a game
summarizeRound :: [(Int, C)] -> Map C Int
summarizeRound :: [(Int, C)] -> Map C Int
summarizeRound [(Int, C)]
r = [(C, Int)] -> Map C Int
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(C
color, Int
count) | (Int
count, C
color) <- [(Int, C)]
r]

-- | Marbles available in part 1
part1 :: Map C Int
part1 :: Map C Int
part1 = [(C, Int)] -> Map C Int
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(C
Cred, Int
12), (C
Cgreen, Int
13), (C
Cblue, Int
14)]