From ae18f71ccf8176de79bc5f62c82940971a27576a Mon Sep 17 00:00:00 2001 From: Jordan Doyle Date: Wed, 6 Dec 2023 02:10:37 +0000 Subject: [PATCH] Add day 2 --- 2.hs | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100755 2.hs diff --git a/2.hs b/2.hs new file mode 100755 index 0000000..68ba85b --- /dev/null +++ b/2.hs @@ -0,0 +1,84 @@ +#!/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) + +{- https://adventofcode.com/2023/day/2 -} + +main = do + input <- getContents + case parseString input of + Left err -> print err + Right games -> do + part1PrintValidGamesMaxCubes games + part2PrintMinimumRequiredCubes games + +-- print the sum of game ids that can be played with `cubesAllowed` cubes +part1PrintValidGamesMaxCubes :: [Game] -> IO () +part1PrintValidGamesMaxCubes games = do + print $ sum $ map gameId (filter checkGameIsValid games) + +-- print the sum of the "power" required to play each game (which is just the product of max(amount) per colour) +part2PrintMinimumRequiredCubes :: [Game] -> IO () +part2PrintMinimumRequiredCubes games = do + print $ sum $ map (product . elems . getMinimumCubesRequiredForGame) games + +-- fold every round in a game into map where string is a colour and int is the max cubes for the colour +getMinimumCubesRequiredForGame :: Game -> Map String Int +getMinimumCubesRequiredForGame game = fromListWith max $ concat (rounds game) + +-- check if every colourset pulled within a game is within the bounds of `cubesAllowed` +checkGameIsValid :: Game -> Bool +checkGameIsValid game = all (all isCubeAmountAllowed) (rounds game) + +-- check if the given colour, amount tuple is within the allowed range +isCubeAmountAllowed :: (String, Int) -> Bool +isCubeAmountAllowed (colour, amount) = amount <= cubesAllowed colour + +-- consts set by the task +cubesAllowed "red" = 12 +cubesAllowed "green" = 13 +cubesAllowed "blue" = 14 +cubesAllowed _ = 0 + +data Game = Game + { gameId :: Int, + rounds :: [[(String, Int)]] + } + deriving (Show) + +-- parse `Game [n]: [n] [colour], [n] [colour], ...; [n] [colour]; Game [n]...` +parseString :: String -> Either ParseError [Game] +parseString = parse fullParser "" + +fullParser :: Parser [Game] +fullParser = gameParser `sepBy` char '\n' + +-- parse a single game +gameParser :: Parser Game +gameParser = do + _ <- string "Game " + gameId <- many1 digit <* char ':' <* spaces + rounds <- roundParser `sepBy` (char ';' <* spaces) + + return + Game + { gameId = read gameId, + rounds + } + +-- parse all the colour, count tuples in a given round +roundParser :: Parser [(String, Int)] +roundParser = cubeNumberParser `sepBy` (char ',' <* spaces) + +-- parse a single colour, count tuple +cubeNumberParser :: Parser (String, Int) +cubeNumberParser = do + amount <- many1 digit <* spaces + colour <- many1 letter + return (colour, read amount) -- libgit2 1.7.2