Tweaked some parsing code
[advent-of-code-21.git] / advent22 / Main.hs
1 -- Writeup at https://work.njae.me.uk/2021/12/29/advent-of-code-2021-day-22/
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
9 import Control.Lens
10 import Data.List
11
12 type Coord = V3 Int
13
14 data Parity = On | Off deriving (Eq, Ord, Show)
15
16 data Cuboid = Cuboid
17 { _bounds :: (Coord, Coord)
18 , _parity :: Parity
19 , _time :: Int
20 }
21 deriving (Ord, Eq, Show)
22 makeLenses ''Cuboid
23
24 -- Main
25
26 main :: IO ()
27 main =
28 do text <- TIO.readFile "data/advent22.txt"
29 let cuboids = successfulParse text
30 print $ part1 cuboids
31 print $ part2 cuboids
32
33 part1 cuboids = sweepX (filter isLocal cuboids)
34 part2 cuboids = sweepX cuboids
35
36 isLocal :: Cuboid -> Bool
37 isLocal cuboid = all (>= (- 50)) ls && all (<= 50) hs
38 where ls = [cuboid ^. bounds . _1 . c | c <- [_x, _y, _z]] :: [Int]
39 hs = [cuboid ^. bounds . _2 . c | c <- [_x, _y, _z]] :: [Int]
40
41 straddles :: (Lens' (V3 Int) Int) -> Int -> Cuboid -> Bool
42 straddles f here cuboid =
43 ((cuboid ^. bounds . _1 . f) <= here) && ((cuboid ^. bounds . _2 . f) >= here)
44
45 events :: (Lens' (V3 Int) Int) -> [Cuboid] -> [Int]
46 events f cuboids = nub $ sort $ ls ++ hs
47 where ls = map (^. bounds . _1 . f) cuboids
48 hs = map ((+1) . (^. bounds . _2 . f)) cuboids
49
50 isActive :: [Cuboid] -> Bool
51 isActive [] = False
52 isActive cs = ((last scs) ^. parity) == On
53 where scs = sortOn (^. time) cs
54
55 sweepX :: [Cuboid] -> Int
56 sweepX cuboids = sum $ map (volumeSize cuboids) $ segment evs
57 where evs = events _x cuboids
58
59 volumeSize :: [Cuboid] -> (Int, Int) -> Int
60 volumeSize cuboids (here, there) = (sweepY cuboidsHere) * (there - here)
61 where cuboidsHere = filter (straddles _x here) cuboids
62
63 -- assume for a given x
64 sweepY :: [Cuboid] -> Int
65 sweepY cuboids = sum $ map (areaSize cuboids) $ segment evs
66 where evs = events _y cuboids
67
68 areaSize :: [Cuboid] -> (Int, Int) -> Int
69 areaSize cuboids (here, there) = (sweepZ cuboidsHere) * (there - here)
70 where cuboidsHere = filter (straddles _y here) cuboids
71
72 -- assume for a given x and y.
73 sweepZ :: [Cuboid] -> Int
74 sweepZ cuboids = sum $ map (segmentSize cuboids) $ segment evs
75 where evs = events _z cuboids
76
77 segmentSize :: [Cuboid] -> (Int, Int) -> Int
78 segmentSize cuboids (here, there)
79 | isActive $ filter (straddles _z here) cuboids = (there - here)
80 | otherwise = 0
81
82 segment :: [Int] -> [(Int, Int)]
83 -- segment evs = if null evs then [] else zip evs $ tail evs
84 segment [] = []
85 segment evs@(_ : tevs) = zip evs tevs
86
87 -- Parse the input file
88
89 cuboidsP = timeify <$> cuboidP `sepBy` endOfLine
90 where timeify cuboids = map (\(c, n) -> c & time .~ n) $ zip cuboids [0..]
91
92 cuboidP = cubify <$> (partiyP <* " ") <*> (boundsP `sepBy` ",")
93 where cubify p ranges =
94 Cuboid { _parity = p
95 , _bounds = ( vecify (map fst ranges)
96 , vecify (map snd ranges)
97 )
98 , _time = 0
99 }
100 vecify [c1, c2, c3] = V3 c1 c2 c3
101
102 -- partiyP = ("on" *> pure On) <|> ("off" *> pure Off)
103 partiyP = (On <$ "on") <|> (Off <$ "off")
104
105 boundsP = (,) <$> (("x" <|> "y" <|> "z") *> "=" *> signed decimal) <*> (".." *> signed decimal)
106
107 -- successfulParse :: Text -> (Integer, [Maybe Integer])
108 successfulParse input =
109 case parseOnly cuboidsP input of
110 Left _err -> [] -- TIO.putStr $ T.pack $ parseErrorPretty err
111 Right cuboids -> cuboids