Advent of Code 2023 day 15
Day 15 looked like it would be another day with just basic list processing, but that wasn't to be.
Part 1
This was simply the implementation of the hashing function. I split the input string on commas, then apply the hash to each element. No data structures or parsing required.
part1 :: Text -> Int
part1 = sum . fmap (hash . unpack) . splitOn ","
hash :: String -> Int
hash s = foldl' go 0 s
where go current c = ((current + ord c) * 17) `mod` 256
Part 2
This is where I need some data structures. I defined an Instruction
, a Lens
, and a Facility
(the collection of boxes).
data Instruction = Remove String | Insert String Int deriving (Show, Eq)
data Lens = Lens {lensLabel :: String, lensPower :: Int} deriving (Show, Eq)
type Facility = M.IntMap [Lens]
Parsing instructions is uncomplicated.
instructionsP = instructionP `sepBy` ","
instructionP = removeP <|> insertP
removeP = Remove <$> (many1 letter) <* "-"
insertP = Insert <$> ((many1 letter) <* "=") <*> decimal
The key here is processing an instruction to adjust the facility.
Removing a lens takes the existing lenses in a box and removes all that match the label in the instruction.
Inserting a lens has two cases. If any lens with this label already exists in the given box, replace it with the new one. Otherwise, add the new lens to the end.
process :: Facility -> Instruction -> Facility
process facility (Remove s) =
M.adjust (filter ((/= s) . lensLabel))
(hash s)
facility
process facility (Insert s p)
| any ((== s) . lensLabel) content = M.insert hs content' facility
| otherwise = M.insert hs (content ++ [lens]) facility
where hs = hash s
content = M.findWithDefault [] hs facility
lens = Lens s p
content' = fmap replaceLens content
replaceLens l
| lensLabel l == s = lens
| otherwise = l
Finding the power of the faciltiy is the sum of all the powers of a box. Finding the power of a box uses zipWith
to do the product of lens and position, then multiplies the whole thing with the box number.
powerCell :: Int -> [Lens] -> Int
powerCell boxNum lenses =
(boxNum + 1) * (sum $ zipWith (*) [1..] (fmap lensPower lenses))
power :: Facility -> Int
power = sum . M.elems . M.mapWithKey powerCell
The solution is finding the power after processing the instructions.
part2 :: [Instruction] -> Int
part2 = power . processAll
Code
You can get the code from my locally-hosted Git repo, or from Gitlab.