this post was submitted on 22 Dec 2024
14 points (93.8% liked)

Advent Of Code

980 readers
22 users here now

An unofficial home for the advent of code community on programming.dev!

Advent of Code is an annual Advent calendar of small programming puzzles for a variety of skill sets and skill levels that can be solved in any programming language you like.

AoC 2024

Solution Threads

M T W T F S S
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25

Rules/Guidelines

Relevant Communities

Relevant Links

Credits

Icon base by Lorc under CC BY 3.0 with modifications to add a gradient

console.log('Hello World')

founded 1 year ago
MODERATORS
 

Day 22: Monkey Market

Megathread guidelines

  • Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
  • You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL

FAQ

you are viewing a single comment's thread
view the rest of the comments
[โ€“] [email protected] 3 points 18 hours ago (1 children)

Haskell

I have no Idea how to optimize this and am looking forward to the other solutions that probably run in sub-single-second times. I like my solution because it was simple to write which I hadn't managed in the previous days, runs in 17 seconds with no less than 100MB of RAM.

import Control.Arrow
import Data.Bits (xor)
import Data.Ord (comparing)

import qualified Data.List as List
import qualified Data.Map as Map

parse :: String -> [Int]
parse = map read . filter (/= "") . lines

mix = xor 
prune = flip mod 16777216
priceof = flip mod 10

nextSecret step0 = do
        let step1 = prune . mix step0 $ step0 * 64
        let step2 = prune . mix step1 $ step1 `div` 32
        let step3 = prune . mix step2 $ step2 * 2048
        step3

part1 = sum . map (head . drop 2000 . iterate nextSecret)
part2 = map (iterate nextSecret
                >>> take 2001
                >>> map priceof
                >>> (id &&& tail)
                >>> uncurry (zipWith (curry (uncurry (flip (-)) &&& snd)))
                >>> map (take 4) . List.tails
                >>> filter ((==4) . length)
                >>> map (List.map fst &&& snd . List.last)
                >>> List.foldl (\ m (s, p) -> Map.insertWith (flip const) s p m) Map.empty
                )
        >>> Map.unionsWith (+)
        >>> Map.assocs
        >>> List.maximumBy (comparing snd)

main = getContents
        >>= print
        . (part1 &&& part2)
        . parse
[โ€“] [email protected] 3 points 16 hours ago

Haha, same! Mine runs in a bit under 4s compiled, but uses a similar 100M-ish peak. Looks like we used the same method.

Maybe iterate all the secrets in parallel, and keep a running note of the best sequences so far? I'm not sure how you'd decide when to throw away old candidates, though. Sequences might match one buyer early and another really late.