1 -- Writeup at https://work.njae.me.uk/2022/12/19/advent-of-code-2022-day-17/
6 import qualified Data.Set as S
7 import qualified Data.Map.Strict as M
8 import Linear hiding (Trace, trace, distance)
12 type Position = V2 Int -- x, y; y increasing upwards
13 type Chamber = S.Set Position
14 type Rock = S.Set Position
16 data SimulationState = SimulationState
20 , _droppedCount :: Int
22 makeLenses ''SimulationState
24 instance Show SimulationState where
25 show sState = "SimState { _chamber = "
26 ++ (show $ sState ^. chamber)
28 ++ (show (take 5 (sState ^. jets)))
30 ++ (show (take 5 (sState ^. rocks)))
31 ++ ", _droppedCount = "
32 ++ (show (sState ^. droppedCount))
37 do dataFileName <- getDataFileName
38 text <- readFile dataFileName
39 let oneJetCycle = mkJets text
40 print $ part1 oneJetCycle
41 -- print $ (length oneJetCycle) * (length rockPics)
42 print $ part2 oneJetCycle
44 part1, part2 :: [Position] -> Int
45 part1 oneJetCycle = rocksHeight final -- fromMaybe -1 $ maximumOf (folded . _y) (final ^. chamber)
46 where final = simulate mkRocks (cycle oneJetCycle) 2022
48 part2 oneJetCycle = calculatedHeight -- (cycleStartState, cycleRepeatState)
49 where initState = SimulationState
51 , _jets = (cycle oneJetCycle)
55 (cycleStartState, _) = findEarliestRepeat (length oneJetCycle) initState
56 cycleRepeatState = findCycleRepeat (length oneJetCycle) cycleStartState
57 cycleStart = cycleStartState ^. droppedCount
58 cycleLength = (cycleRepeatState ^. droppedCount) - cycleStart
59 startHeight = rocksHeight cycleStartState
60 differenceHeight = (rocksHeight cycleRepeatState) - startHeight
61 afterStart = 1000000000000 - cycleStart
62 (numCycles, remainingDrops) = afterStart `divMod` cycleLength
63 finalState = (!!remainingDrops) $ iterate dropFromTop cycleStartState
64 finalHeight = rocksHeight finalState
65 calculatedHeight = finalHeight + (differenceHeight * numCycles)
67 rocksHeight :: SimulationState -> Int
68 rocksHeight state = fromMaybe -1 $ maximumOf (folded . _y) (state ^. chamber)
70 simSome :: [Position] -> Int -> Int
71 simSome oneJetCycle n = fromMaybe -1 $ maximumOf (folded . _y) (final ^. chamber)
72 where final = simulate mkRocks (cycle oneJetCycle) n
74 simulate :: [Rock] -> [Position] -> Int -> SimulationState
75 simulate rocks jets n = (!!n) $ iterate dropFromTop initState
76 where initState = SimulationState { _chamber = S.empty, _jets = jets, _rocks = rocks, _droppedCount = 0}
78 dropFromTop :: SimulationState -> SimulationState
79 dropFromTop simState = (dropRock simState (initialPlace simState))
81 & droppedCount %~ (+ 1)
83 dropRock :: SimulationState -> Rock -> SimulationState
84 dropRock simState rock
85 | rock2 == Nothing = simState & chamber %~ (S.union rock1)
87 | otherwise = dropRock (simState & jets %~ tail) $ fromJust rock2
88 where rock1 = push (simState ^. chamber) rock (head (simState ^. jets))
89 rock2 = fall (simState ^. chamber) rock1
91 initialPlace :: SimulationState -> Rock
92 initialPlace simState = S.map (^+^ (V2 2 startHeight)) rock
93 where startHeight = 4 + (fromMaybe 0 $ maximumOf (folded . _y) (simState ^. chamber))
94 rock = head $ simState ^. rocks
96 push :: Chamber -> Rock -> Position -> Rock
97 push chamber rock direction
98 -- | trace ("Before push " ++ (intercalate " : " $ [show chamber, show rock, show direction])) False = undefined
99 | disjoint && inLeft && inRight = pushedRock
101 where pushedRock = S.map (^+^ direction) rock
102 disjoint = S.null $ S.intersection pushedRock chamber
103 inLeft = (fromJust $ minimumOf (folded . _x) pushedRock) >= 0
104 inRight = (fromJust $ maximumOf (folded . _x) pushedRock) <= 6
106 fall :: Chamber -> Rock -> Maybe Rock
108 -- | trace ("Before fall " ++ (intercalate " : " $ [show chamber, show rock, show disjoint, show aboveFloor, show droppedRock])) False = undefined
109 | disjoint && aboveFloor = Just droppedRock
110 | otherwise = Nothing
111 where droppedRock = S.map (^+^ (V2 0 -1)) rock
112 disjoint = S.null $ S.intersection droppedRock chamber
113 aboveFloor = (fromJust $ minimumOf (folded . _y) droppedRock) > 0
116 findCycleRepeat :: Int -> SimulationState -> SimulationState
117 findCycleRepeat jetLength cycleStart = head $ dropWhile (differentProfiles jetLength cycleStart) hares
118 where hares = drop 1 $ iterate dropFromTop cycleStart
121 findEarliestRepeat :: Int -> SimulationState -> (SimulationState, SimulationState)
122 findEarliestRepeat jetLength simState = head $ dropWhile (uncurry (differentProfiles jetLength)) pairs
123 where tortoises = drop 1 $ iterate dropFromTop simState
124 hares = drop 1 $ iterate (dropFromTop . dropFromTop) simState
125 pairs = zip tortoises hares
127 differentProfiles :: Int -> SimulationState -> SimulationState -> Bool
128 differentProfiles jetLength t h = (simulationProfile jetLength t) /= (simulationProfile jetLength h)
130 simulationProfile :: Int -> SimulationState -> (Chamber, [Position], Rock)
131 simulationProfile jetLength state =
132 ( surfaceProfile state
133 , take jetLength $ state ^. jets
134 , head $ state ^. rocks
137 surfaceProfile :: SimulationState -> Chamber
138 surfaceProfile state = S.fromList $ map (^-^ (V2 0 peak)) rawProfile
139 where ch = state ^. chamber
140 rawProfile = [V2 i (fromMaybe -1 $ maximumOf (folded . filteredBy (_x . only i) . _y) ch) | i <- [0..6] ]
141 peak = fromJust $ maximumOf (folded . _y) rawProfile
143 showChamber :: Chamber -> String
144 showChamber chamber = unlines
145 [ [showCell x y | x <- [0..6]]
146 | y <- reverse [1..yMax]
148 where yMax = fromMaybe 0 $ maximumOf (folded . _y) chamber
150 | (V2 x y) `S.member` chamber = '#'
153 mkJets :: String -> [Position]
155 where mkJet '<' = V2 -1 0
157 mkJet _ = error "Illegal jet character"
160 mkRocks = cycle $ fmap mkRock rockPics
162 mkRock :: String -> Rock
163 mkRock rockPic = S.fromList
165 | x <- [0..((length (rockLines!!0)) - 1)]
166 , y <- [0..((length rockLines) - 1)]
167 , (rockLines!!y)!!x == '#'
169 where rockLines = reverse $ lines rockPic
172 rockPics = ["####", ".#.\n###\n.#.", "..#\n..#\n###", "#\n#\n#\n#", "##\n##"]