1 -- Writeup at https://work.njae.me.uk/2021/12/21/advent-of-code-2021-day-19/
4 import qualified Data.Text.IO as TIO
5 import Data.Attoparsec.Text -- hiding (take, takeWhile)
6 import Control.Applicative
8 import Linear -- (V3(..), (^+^), (^-^))
16 data Parity = On | Off deriving (Eq, Ord, Show)
19 { _bounds :: (Coord, Coord)
23 deriving (Ord, Eq, Show)
30 do text <- TIO.readFile "data/advent22.txt"
31 let cuboids = successfulParse text
34 -- print $ part2 transScanners
37 part1 cuboids = sweepX (filter isLocal cuboids)
38 part2 cuboids = sweepX cuboids
40 isLocal :: Cuboid -> Bool
41 isLocal cuboid = all (>= (- 50)) ls && all (<= 50) hs
42 where ls = [cuboid ^. bounds . _1 . c | c <- [_x, _y, _z]] :: [Int]
43 hs = [cuboid ^. bounds . _2 . c | c <- [_x, _y, _z]] :: [Int]
45 straddles :: (Lens' (V3 Int) Int) -> Int -> Cuboid -> Bool
46 straddles f here cuboid =
47 ((cuboid ^. bounds . _1 . f) <= here) && ((cuboid ^. bounds . _2 . f) >= here)
49 events :: (Lens' (V3 Int) Int) -> [Cuboid] -> [Int]
50 events f cuboids = nub $ sort $ ls ++ hs
51 where ls = map (^. bounds . _1 . f) cuboids
52 hs = map ((+1) . (^. bounds . _2 . f)) cuboids
54 isActive :: [Cuboid] -> Bool
56 isActive cs = ((last scs) ^. parity) == On
57 where scs = sortOn (^. time) cs
59 sweepX :: [Cuboid] -> Int
60 sweepX cuboids = sum $ map (volumeSize cuboids) segments
61 where evs = events _x cuboids
62 segments = if null evs then [] else zip evs $ tail evs
64 volumeSize :: [Cuboid] -> (Int, Int) -> Int
65 volumeSize cuboids (here, there) = (sweepY cuboidsHere) * (there - here)
66 where cuboidsHere = filter (straddles _x here) cuboids
68 sweepY :: [Cuboid] -> Int
69 sweepY cuboids = sum $ map (areaSize cuboids) segments
70 where evs = events _y cuboids
71 segments = if null evs then [] else zip evs $ tail evs
73 areaSize :: [Cuboid] -> (Int, Int) -> Int
74 areaSize cuboids (here, there) = (countActive cuboidsHere) * (there - here)
75 where cuboidsHere = filter (straddles _y here) cuboids
77 -- assume for a given x and y.
78 countActive :: [Cuboid] -> Int
79 countActive cuboids = sum $ map (segmentSize cuboids) segments
80 where evs = events _z cuboids
81 segments = if null evs then [] else zip evs $ tail evs
83 segmentSize :: [Cuboid] -> (Int, Int) -> Int
84 segmentSize cuboids (here, there)
85 | isActive $ filter (straddles _z here) cuboids = (there - here)
89 -- Parse the input file
91 cuboidsP = timeify <$> cuboidP `sepBy` endOfLine
92 where timeify cuboids = map (\(c, n) -> c & time .~ n) $ zip cuboids [0..]
94 cuboidP = cubify <$> (partiyP <* " ") <*> (boundsP `sepBy` ",")
95 where cubify p ranges =
97 , _bounds = ( vecify (map fst ranges)
98 , vecify (map snd ranges)
102 vecify [c1, c2, c3] = V3 c1 c2 c3
104 partiyP = ("on" *> pure On) <|> ("off" *> pure Off)
106 boundsP = (,) <$> (("x" <|> "y" <|> "z") *> "=" *> signed decimal) <*> (".." *> signed decimal)
108 -- successfulParse :: Text -> (Integer, [Maybe Integer])
109 successfulParse input =
110 case parseOnly cuboidsP input of
111 Left _err -> [] -- TIO.putStr $ T.pack $ parseErrorPretty err
112 Right cuboids -> cuboids