65 lines
1.5 KiB
Haskell
65 lines
1.5 KiB
Haskell
import Data.List
|
|
import Data.Ord (clamp)
|
|
import Lib
|
|
import Prelude hiding (Left, Right)
|
|
|
|
data Direction = Up | Down | Left | Right deriving (Show)
|
|
|
|
type Position = (Int, Int)
|
|
|
|
type Rope = [Position]
|
|
|
|
type State = (Rope, [Position])
|
|
|
|
type Motion = (Direction, Int)
|
|
|
|
type Input = [Motion]
|
|
|
|
main :: IO ()
|
|
main = aoc 2022 9 setup solve1 solve2 ["1", "2"]
|
|
|
|
solve1 :: Input -> Int
|
|
solve1 = solve 2
|
|
|
|
solve2 :: Input -> Int
|
|
solve2 = solve 10
|
|
|
|
solve :: Int -> Input -> Int
|
|
solve n = length . nub . snd . foldl step (initialRope n, []) . directions
|
|
|
|
step :: State -> Direction -> State
|
|
step (rope, visited) m = (newRope, head newRope : visited)
|
|
where
|
|
newRope = stepRope rope m
|
|
|
|
stepRope :: Rope -> Direction -> Rope
|
|
stepRope r m = scanr follow (move m $ last r) $ init r
|
|
|
|
follow :: Position -> Position -> Position
|
|
follow (x, y) (x', y')
|
|
| abs (x - x') <= 1 && abs (y - y') <= 1 = (x, y)
|
|
| otherwise = (x + clamp (-1, 1) (x' - x), y + clamp (-1, 1) (y' - y))
|
|
|
|
directions :: [Motion] -> [Direction]
|
|
directions = concatMap $ uncurry $ flip replicate
|
|
|
|
setup :: String -> Input
|
|
setup = map parseMotion . lines
|
|
|
|
parseMotion :: String -> Motion
|
|
parseMotion (d : ' ' : n) = (parseDirection d, read n)
|
|
|
|
parseDirection :: Char -> Direction
|
|
parseDirection 'U' = Up
|
|
parseDirection 'D' = Down
|
|
parseDirection 'L' = Left
|
|
parseDirection 'R' = Right
|
|
|
|
move :: Direction -> Position -> Position
|
|
move Up (x, y) = (x, y - 1)
|
|
move Down (x, y) = (x, y + 1)
|
|
move Left (x, y) = (x - 1, y)
|
|
move Right (x, y) = (x + 1, y)
|
|
|
|
initialRope :: Int -> Rope
|
|
initialRope = flip replicate (0, 0)
|