Day 1 - Intro Problem
As a reminder, these writeups won't be super detailed, since I have to do one every day. I'll try to focus on the key ideas though, and I'll always link to my code!
Subscribe to Monday Morning Haskell!
Problem Overview
This year we're dealing with elves. Each elf is carrying some snack items with a certain number of calories. Our input has one calorie count per line, and an empty line denotes that we have reached the end of one elf's snack collection and started another.
1000
2000
3000
4000
5000
6000
7000
8000
9000
10000
For the first part, we just want to find the elf with the most calories. This is the 4th elf, with a total of 24000
calories (7000+8000+9000
).
For the second part, we want the sum of calories from the three elves with the most. So we take the 24000
from the elf with the most, and add the 3rd elf (11000
calories) and the 5th elf (10000
calories). This gives a total of 45000
.
Solution Approach and Insights
Nothing complicated here. Once we parse into list-of-lists-of-ints, we just use map sum
and either take the maximum or the sum of the top 3.
Relevant Utilities
Function parseFile
Parsing the Input
Here's our parsing code. One nuance...I needed to add an extra empty line to the given inputs in order to make this parse work. Dealing with empty line separators is a little tricky with megaparsec (or at least I haven't mastered the right pattern yet), because the "chunk separator" is the same as the "line separator" within each chunk (eol
parser).
parseInput :: (MonadLogger m) => ParsecT Void Text m [[Int]]
parseInput =
sepEndBy1 parseIntLines eol
where
parseIntLines = some parseIntLine
parseIntLine = do
i <- parsePositiveNumber
eol
return i
Getting the Solution
As above, nothing complicated here. Use map sum
and take the maximum
.
processInputEasy :: (MonadLogger m) => [[Int]] -> m Int
processInputEasy intLists = return $ maximum (map sum intLists)
With the hard part, we sort
, reverse
, take 3
, and then take another sum
.
processInputHard :: (MonadLogger m) => [[Int]] -> m Int
processInputHard intLists = return $ sum $ take 3 $ reverse $ sort (map sum intLists)
Answering the Question
And no additional processing is needed - we have our answer! (My standard template has the answer always wrapped in Maybe
to account for failure cases).
solveEasy :: FilePath -> IO (Maybe Int)
solveEasy fp = runStdoutLoggingT $ do
input <- parseFile parseInput fp
Just <$> processInputEasy input
solveHard :: FilePath -> IO (Maybe Int)
solveHard fp = runStdoutLoggingT $ do
input <- parseFile parseInput fp
Just <$> processInputHard input