Part 1 of today’s Advent of Code problem, “Monkey Market”, was a simple implementation task. Part 2 showed promise, but apart from the convoluted statement hurdle, it turned out to be simple implementation as well. At least there’s no 2D grid involved. This is still literate Haskell, so let’s have a few imports to get started.
import Control.Arrow ((&&&))
import Data.Bits (xor)
import Data.List (tails)
import qualified Data.Map.Strict as Map
So each buyer shifts from a secret to the next using a simple formula. Let’s write a function for it.
nextSecret :: Int -> Int
=
nextSecret . (mix <*> (2048 *)) .
prune . (mix <*> (`div` 32)) .
prune . (mix <*> (64 *)) prune
Yes, there’s a bit of abuse of the (->)
reader monad
instance in there. Got to keep it interesting for me as well.
Anyway, the function uses two more bits of support code.
mix :: Int -> Int -> Int
= xor
mix
prune :: Int -> Int
= (`mod` 16777216) prune
And that’s more or less all we need to wrap up part 1.
part1 :: [Int] -> Int
= sum . map ((!! 2000) . iterate nextSecret) part1
Part 2 seemed a lot more interesting at first. Because I had noticed neither the 2000 limit for price change observation nor the 4 limit for the window length.
So let’s compute, for a starting secret, the full list of involved change window and associated price.
prefixPrices :: Int -> [([Int],Int)]
=
prefixPrices secret let prices = (`mod` 10) <$> iterate nextSecret secret
= take 2000 $ zipWith (-) (tail prices) prices
changes = filter ((== 4) . length) $ take 4 <$> tails changes
prefixes in zip prefixes (drop 4 prices)
Let’s gather all of those into a map. The seller will trigger for the first time it observes the change sequence, so we want left bias here.
ppMap :: Int -> Map.Map [Int] Int
= Map.fromListWith (flip const) . prefixPrices ppMap
We can now sum those results across all buyers and keep the largest.
part2 :: [Int] -> Int
= maximum . Map.unionsWith (+) . map ppMap part2
A bit of wrapping…
parse :: String -> [Int]
= map read . lines
parse
main :: IO ()
= interact $ show . (part1 &&& part2) . parse main
…and we’re done.
This concludes today’s problem, a rather boring one if you ask me. See you tomorrow!