Day 14: Restroom Redoubt
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
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
You are viewing a single thread.
View all comments 3 points
Haskell
Part 2 could be improved significantly now that I know what to look for, but this is the (very inefficient) heuristic I eventually found the answer with.
Solution
import Control.Arrow
import Data.Char
import Data.List
import Data.Map qualified as Map
import Data.Maybe
import Text.Parsec
(w, h) = (101, 103)
readInput :: String -> [((Int, Int), (Int, Int))]
readInput = either (error . show) id . parse (robot `endBy` newline) ""
where
robot = (,) <$> (string "p=" >> coords) <*> (string " v=" >> coords)
coords = (,) <$> num <* char ',' <*> num
num = read <$> ((++) <$> option "" (string "-") <*> many1 digit)
runBots :: [((Int, Int), (Int, Int))] -> [[(Int, Int)]]
runBots = transpose . map botPath
where
botPath (p, (vx, vy)) = iterate (incWrap w vx *** incWrap h vy) p
incWrap s d = (`mod` s) . (+ d)
safetyFactor :: [(Int, Int)] -> Int
safetyFactor = product . Map.fromListWith (+) . map (,1) . mapMaybe quadrant
where
cx = w `div` 2
cy = h `div` 2
quadrant (x, y)
| x == cx || y == cy = Nothing
| otherwise = Just (x `div` (cx + 1), y `div` (cy + 1))
render :: [(Int, Int)] -> [String]
render bots =
let counts = Map.fromListWith (+) $ map (,1) bots
in flip map [0 .. h - 1] $ \y ->
flip map [0 .. w - 1] $ \x ->
maybe '.' intToDigit $ counts Map.!? (x, y)
isImage :: [String] -> Bool
isImage = (> 4) . length . filter hasRun
where
hasRun = any ((> 3) . length) . filter head . group . map (/= '.')
main = do
positions <- runBots . readInput <$> readFile "input14"
print . safetyFactor $ positions !! 100
let (Just (t, image)) = find (isImage . snd) $ zip [0 ..] $ map render positions
print t
mapM_ putStrLn image