51c7a8e23f8e7d0af1fcc25b6993fa7ec3628893
[advent-of-code-21.git] / advent22 / Main.hs
1 -- Writeup at https://work.njae.me.uk/2021/12/21/advent-of-code-2021-day-19/
2
3 import Data.Text ()
4 import qualified Data.Text.IO as TIO
5 import Data.Attoparsec.Text -- hiding (take, takeWhile)
6 import Control.Applicative
7
8 import Linear -- (V3(..), (^+^), (^-^))
9 -- import Linear.V3
10 -- import Data.Ix
11 import Control.Lens
12 import Data.List
13
14 type Coord = V3 Int
15
16 data Parity = On | Off deriving (Eq, Ord, Show)
17
18 data Cuboid = Cuboid
19 { _bounds :: (Coord, Coord)
20 , _parity :: Parity
21 , _time :: Int
22 }
23 deriving (Ord, Eq, Show)
24 makeLenses ''Cuboid
25
26 -- Main
27
28 main :: IO ()
29 main =
30 do text <- TIO.readFile "data/advent22.txt"
31 let cuboids = successfulParse text
32 print $ part1 cuboids
33 print $ part2 cuboids
34 -- print $ part2 transScanners
35
36
37 part1 cuboids = sweepX (filter isLocal cuboids)
38 part2 cuboids = sweepX cuboids
39
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]
44
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)
48
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
53
54 isActive :: [Cuboid] -> Bool
55 isActive [] = False
56 isActive cs = ((last scs) ^. parity) == On
57 where scs = sortOn (^. time) cs
58
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
63
64 volumeSize :: [Cuboid] -> (Int, Int) -> Int
65 volumeSize cuboids (here, there) = (sweepY cuboidsHere) * (there - here)
66 where cuboidsHere = filter (straddles _x here) cuboids
67
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
72
73 areaSize :: [Cuboid] -> (Int, Int) -> Int
74 areaSize cuboids (here, there) = (countActive cuboidsHere) * (there - here)
75 where cuboidsHere = filter (straddles _y here) cuboids
76
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
82
83 segmentSize :: [Cuboid] -> (Int, Int) -> Int
84 segmentSize cuboids (here, there)
85 | isActive $ filter (straddles _z here) cuboids = (there - here)
86 | otherwise = 0
87
88
89 -- Parse the input file
90
91 cuboidsP = timeify <$> cuboidP `sepBy` endOfLine
92 where timeify cuboids = map (\(c, n) -> c & time .~ n) $ zip cuboids [0..]
93
94 cuboidP = cubify <$> (partiyP <* " ") <*> (boundsP `sepBy` ",")
95 where cubify p ranges =
96 Cuboid { _parity = p
97 , _bounds = ( vecify (map fst ranges)
98 , vecify (map snd ranges)
99 )
100 , _time = 0
101 }
102 vecify [c1, c2, c3] = V3 c1 c2 c3
103
104 partiyP = ("on" *> pure On) <|> ("off" *> pure Off)
105
106 boundsP = (,) <$> (("x" <|> "y" <|> "z") *> "=" *> signed decimal) <*> (".." *> signed decimal)
107
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