advent-of-code-2023/src/Day2.hs

112 lines
3.2 KiB
Haskell

{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -Wno-unused-do-bind #-}
module Day2 where
import Data.Attoparsec.ByteString.Char8 as A
import Data.Either (fromRight)
import Data.String (fromString)
import Data.Maybe
-- import Debug.Trace
main :: IO ()
main = do
dayInput <- readFile "inputs/day2.input"
putStrLn "Part 1 result:"
print $ part1 dayInput
putStrLn "Part 2 result:"
print $ part2 dayInput
data GameSpec =
GameSpec { gameSpecId :: Int, gameSpecInstances :: [GameInstance] }
deriving (Show)
data GameInstance = GameInstance
{ redDice :: Maybe Int
, greenDice :: Maybe Int
, blueDice :: Maybe Int
}
deriving (Show, Ord, Eq)
data Dice = RedDice Int
| GreenDice Int
| BlueDice Int
deriving Show
-- Returns a *new* maximal game instance, and not necessarily an existing game
-- instance
maxCubes :: [GameInstance] -> GameInstance
maxCubes gix = GameInstance (maxRed gix) (maxGreen gix) (maxBlue gix)
where
maxRed = maximum . map redDice
maxGreen = maximum . map greenDice
maxBlue = maximum . map blueDice
-- Determine which games would have been possible if the bag had been loaded
-- with only 12 red cubes, 13 green cubes, and 14 blue cubes.
part1 :: String -> String
part1 = show
. sum
. mapMaybe (possibleGame1 (GameInstance (Just 12) (Just 13) (Just 14)))
. parseInput
possibleGame1 :: GameInstance -> GameSpec -> Maybe Int
possibleGame1 spec actual =
if maxCubes [spec, maxCubes $ gameSpecInstances actual] == spec
then Just $ gameSpecId actual
else Nothing
part2 :: String -> String
part2 = show
. sum
. map possibleGame2
. parseInput
-- Fewest number of cubes of each color
possibleGame2 :: GameSpec -> Int
possibleGame2 actual = red * green * blue
where
maxInstance = maxCubes $ gameSpecInstances actual
red = fromMaybe 0 $ redDice maxInstance
green = fromMaybe 0 $ greenDice maxInstance
blue = fromMaybe 0 $ blueDice maxInstance
parseInput :: String -> [GameSpec]
parseInput = fromRight [] . parseOnly input . fromString
where
input :: Parser [GameSpec]
input = line `sepBy` endOfLine
-- Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
line :: Parser GameSpec
line = GameSpec <$> gameId <*> gameInstances
gameId :: Parser Int
gameId = "Game" *> space *> decimal <* ": "
gameInstances :: Parser [GameInstance]
gameInstances = sepBy gameInstance "; "
gameInstance :: Parser GameInstance
gameInstance =
sortThatShit <$> sepBy1
( choice [ RedDice <$> (decimal :: Parser Int) <* space <* "red"
, GreenDice <$> (decimal :: Parser Int) <* space <* "green"
, BlueDice <$> (decimal :: Parser Int) <* space <* "blue"
] :: Parser Dice
) ", "
where
sortThatShit :: [Dice] -> GameInstance
sortThatShit dx = GameInstance
(lookup "red" axs)
(lookup "green" axs)
(lookup "blue" axs)
where
axs = map toPair dx
toPair :: Dice -> (String,Int)
toPair (RedDice i) = ("red", i)
toPair (GreenDice i) = ("green", i)
toPair (BlueDice i) = ("blue", i)