Optimised day 19
[advent-of-code-22.git] / advent18 / Main.hs
1 -- Writeup at https://work.njae.me.uk/2022/12/19/advent-of-code-2022-day-18/
2
3 import AoC
4 import Data.Text (Text)
5 import qualified Data.Text.IO as TIO
6 import Data.Attoparsec.Text hiding (take, D)
7 -- import Control.Applicative
8 import qualified Data.Set as S
9 import Linear
10 import Control.Lens
11 import Data.Ix
12 import Data.Maybe
13
14 type Droplet = V3 Int
15 type Lava = S.Set Droplet
16
17 main :: IO ()
18 main =
19 do dataFileName <- getDataFileName
20 text <- TIO.readFile dataFileName
21 let lava = successfulParse text
22 print $ part1 lava
23 print $ part2 lava
24
25 part1, part2 :: Lava -> Int
26 part1 lava = surfaceArea lava
27
28 part2 lava = surfaceArea enclosed
29 where box = boundingBox lava
30 steam = floodFill lava box (S.singleton $ head $ range box) S.empty
31 enclosed = (S.fromList (range box)) `S.difference` steam
32
33 surfaceArea :: Lava -> Int
34 surfaceArea lava = sum $ fmap countFree $ S.elems lava
35 where countFree droplet = 6 - (S.size $ S.intersection lava (neigbours droplet))
36
37 neigbours :: Droplet -> Lava
38 neigbours here = S.fromList [ here & d %~ (+) n | d <- [_x, _y, _z], n <- [- 1, 1]]
39
40 boundingBox :: Lava -> (Droplet, Droplet)
41 boundingBox lava = ((V3 minX minY minZ), (V3 maxX maxY maxZ))
42 where minX = (fromJust $ minimumOf (folded . _x) lava) - 1
43 minY = (fromJust $ minimumOf (folded . _y) lava) - 1
44 minZ = (fromJust $ minimumOf (folded . _z) lava) - 1
45 maxX = (fromJust $ maximumOf (folded . _x) lava) + 1
46 maxY = (fromJust $ maximumOf (folded . _y) lava) + 1
47 maxZ = (fromJust $ maximumOf (folded . _z) lava) + 1
48
49 floodFill :: Lava -> (Droplet, Droplet) -> Lava -> Lava -> Lava
50 floodFill lava box boundary found
51 | S.null boundary = found
52 | otherwise = floodFill lava box (S.union nbrs $ S.delete here boundary) (S.insert here found)
53 where here = S.findMin boundary
54 nbrs = S.filter (`S.notMember` lava)
55 $ S.filter (`S.notMember` boundary)
56 $ S.filter (`S.notMember` found)
57 $ S.filter (inRange box)
58 $ neigbours here
59
60 -- Parse the input file
61
62 lavaP :: Parser Lava
63 dropletP :: Parser Droplet
64
65 lavaP = S.fromList <$> dropletP `sepBy` endOfLine
66 dropletP = V3 <$> (decimal <* ",") <*> (decimal <* ",") <*> decimal
67
68 successfulParse :: Text -> Lava
69 successfulParse input =
70 case parseOnly lavaP input of
71 Left _err -> S.empty -- TIO.putStr $ T.pack $ parseErrorPretty err
72 Right lava -> lava