import Data.List.Split (splitOn) import qualified Data.Set as S type Coord = (Int, Int) readCoord :: String -> Coord readCoord s = read ("(" ++ s ++ ")") readLine :: String -> [Coord] readLine xs = map readCoord (splitOn " -> " xs) interpolate :: Coord -> Coord -> S.Set Coord interpolate (a,b) (x,y) | a == x && b == y = S.singleton (a,b) | a == x && b < y = S.insert (a,b) (interpolate (a,b+1) (x,y)) | a == x && b > y = S.insert (a,b) (interpolate (a,b-1) (x,y)) | a < x && b == y = S.insert (a,b) (interpolate (a+1,b) (x,y)) | a > x && b == y = S.insert (a,b) (interpolate (a-1,b) (x,y)) interpolateLine :: [Coord] -> S.Set Coord interpolateLine [x,y] = interpolate x y interpolateLine (x:y:xs) = S.union (interpolate x y) (interpolateLine (y:xs)) part1 :: Coord -> S.Set Coord -> Int -> Int part1 (x,y) occupied limit | y > limit = 0 | S.notMember (x,y+1) occupied = part1 (x, y+1) occupied limit | S.notMember (x-1, y+1) occupied = part1 (x-1, y+1) occupied limit | S.notMember (x+1, y+1) occupied = part1 (x+1, y+1) occupied limit | otherwise = 1 + part1 (500,0) (S.insert (x,y) occupied) limit part2 :: Coord -> S.Set Coord -> Int -> Int part2 (x,y) occupied limit | S.member (500,0) occupied = 0 | y+1 == limit+2 = 1 + part2 (500,0) (S.insert (x,y) occupied) limit | S.notMember (x,y+1) occupied = part2 (x, y+1) occupied limit | S.notMember (x-1, y+1) occupied = part2 (x-1, y+1) occupied limit | S.notMember (x+1, y+1) occupied = part2 (x+1, y+1) occupied limit | otherwise = 1 + part2 (500,0) (S.insert (x,y) occupied) limit main :: IO () main = do x <- map readLine . lines <$> getContents let rocks = foldr1 S.union (map interpolateLine x) let start = (500,0) let lowest = maximum (map snd (S.toList rocks)) putStr "part 1: " print $ part1 (500,0) rocks lowest putStr "part 2: " print $ part2 (500,0) rocks lowest