--- /dev/null
+-- 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