Done day 18
[advent-of-code-22.git] / advent18 / Main.hs
diff --git a/advent18/Main.hs b/advent18/Main.hs
new file mode 100644 (file)
index 0000000..f16e6df
--- /dev/null
@@ -0,0 +1,72 @@
+-- Writeup at https://work.njae.me.uk/2022/12/19/advent-of-code-2022-day-18/
+
+import AoC
+import Data.Text (Text)
+import qualified Data.Text.IO as TIO
+import Data.Attoparsec.Text hiding (take, D)
+-- import Control.Applicative
+import qualified Data.Set as S
+import Linear
+import Control.Lens
+import Data.Ix
+import Data.Maybe
+
+type Droplet = V3 Int
+type Lava = S.Set Droplet
+
+main :: IO ()
+main = 
+  do  dataFileName <- getDataFileName
+      text <- TIO.readFile dataFileName
+      let lava = successfulParse text
+      print $ part1 lava
+      print $ part2 lava
+
+part1, part2 :: Lava -> Int
+part1 lava = surfaceArea lava
+
+part2 lava = surfaceArea enclosed
+  where box = boundingBox lava
+        steam = floodFill lava box (S.singleton $ head $ range box) S.empty
+        enclosed = (S.fromList (range box)) `S.difference` steam
+
+surfaceArea :: Lava -> Int
+surfaceArea lava = sum $ fmap countFree $ S.elems lava
+  where countFree droplet = 6 - (S.size $ S.intersection lava (neigbours droplet))
+
+neigbours :: Droplet -> Lava
+neigbours here = S.fromList [ here & d %~ (+) n | d <- [_x, _y, _z], n <- [- 1, 1]]
+
+boundingBox :: Lava -> (Droplet, Droplet)
+boundingBox lava = ((V3 minX minY minZ), (V3 maxX maxY maxZ))
+  where minX = (fromJust $ minimumOf (folded . _x) lava) - 1
+        minY = (fromJust $ minimumOf (folded . _y) lava) - 1
+        minZ = (fromJust $ minimumOf (folded . _z) lava) - 1
+        maxX = (fromJust $ maximumOf (folded . _x) lava) + 1
+        maxY = (fromJust $ maximumOf (folded . _y) lava) + 1
+        maxZ = (fromJust $ maximumOf (folded . _z) lava) + 1
+
+floodFill :: Lava -> (Droplet, Droplet) -> Lava -> Lava -> Lava
+floodFill lava box boundary found
+  | S.null boundary = found
+  | otherwise = floodFill lava box (S.union nbrs $ S.delete here boundary) (S.insert here found)
+  where here = S.findMin boundary
+        nbrs = S.filter (`S.notMember` lava) 
+                $ S.filter (`S.notMember` boundary) 
+                $ S.filter (`S.notMember` found) 
+                $ S.filter (inRange box) 
+                $ neigbours here
+
+-- Parse the input file
+
+lavaP :: Parser Lava
+dropletP :: Parser Droplet
+
+lavaP = S.fromList <$> dropletP `sepBy` endOfLine
+dropletP = V3 <$> (decimal <* ",") <*> (decimal <* ",") <*> decimal
+
+successfulParse :: Text -> Lava
+successfulParse input = 
+  case parseOnly lavaP input of
+    Left  _err -> S.empty -- TIO.putStr $ T.pack $ parseErrorPretty err
+    Right lava -> lava