#!/usr/bin/env nix-shell
#!nix-shell -i runghc -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ ])"
import Control.Applicative ((<*))
import Data.Map (Map, elems, fromListWith)
import Text.Parsec
import Text.Parsec.Char
import Text.Parsec.Combinator
import Text.Parsec.String (Parser)
main = do
input <- getContents
case parseString input of
Left err -> print err
Right games -> do
part1PrintValidGamesMaxCubes games
part2PrintMinimumRequiredCubes games
part1PrintValidGamesMaxCubes :: [Game] -> IO ()
part1PrintValidGamesMaxCubes games = do
print $ sum $ map gameId (filter checkGameIsValid games)
part2PrintMinimumRequiredCubes :: [Game] -> IO ()
part2PrintMinimumRequiredCubes games = do
print $ sum $ map (product . elems . getMinimumCubesRequiredForGame) games
getMinimumCubesRequiredForGame :: Game -> Map String Int
getMinimumCubesRequiredForGame game = fromListWith max $ concat (rounds game)
checkGameIsValid :: Game -> Bool
checkGameIsValid game = all (all isCubeAmountAllowed) (rounds game)
isCubeAmountAllowed :: (String, Int) -> Bool
isCubeAmountAllowed (colour, amount) = amount <= cubesAllowed colour
cubesAllowed "red" = 12
cubesAllowed "green" = 13
cubesAllowed "blue" = 14
cubesAllowed _ = 0
data Game = Game
{ gameId :: Int,
rounds :: [[(String, Int)]]
}
deriving (Show)
parseString :: String -> Either ParseError [Game]
parseString = parse fullParser ""
fullParser :: Parser [Game]
fullParser = gameParser `sepBy` char '\n'
gameParser :: Parser Game
gameParser = do
_ <- string "Game "
gameId <- many1 digit <* char ':' <* spaces
rounds <- roundParser `sepBy` (char ';' <* spaces)
return
Game
{ gameId = read gameId,
rounds
}
roundParser :: Parser [(String, Int)]
roundParser = cubeNumberParser `sepBy` (char ',' <* spaces)
cubeNumberParser :: Parser (String, Int)
cubeNumberParser = do
amount <- many1 digit <* spaces
colour <- many1 letter
return (colour, read amount)