Day 9 was back to using some familiar favourites: lenses to look inside records, and the `V2`

type from `Linear`

for points.

## Data and parsing

I defined a few data types for representing the problem: a `V2`

for positions, a `Rope`

record for the rope, and some `Direction`

s for the input format. The rope as the head and a list of all the knots; part 1 had just a single knot.

```
type Position = V2 Int
type Trace = S.Set Position
type Path = [Position]
data Rope = Rope
{ _headK :: Position
, _knots :: [Position]
, _trace :: Trace
} deriving (Show, Eq)
makeLenses ''Rope
data Direction = U Int | R Int | D Int | L Int
deriving (Show, Eq, Ord)
newRope :: Int -> Rope
newRope n = Rope { _headK = V2 0 0, _knots = replicate n (V2 0 0), _trace = S.singleton (V2 0 0) }
```

Parsing the input was simple enough. The instructions were expanded into sequences of individual steps, because knots are updated at each step.

```
pathP = directionP `sepBy` endOfLine
directionP = upP <|> leftP <|> downP <|> rightP
upP = U <$> ("U " *> decimal)
leftP = L <$> ("L " *> decimal)
downP = D <$> ("D " *> decimal)
rightP = R <$> ("R " *> decimal)
expandPath :: [Direction] -> Path
expandPath = concatMap expandStep
where expandStep (U n) = replicate n (V2 0 1)
expandStep (L n) = replicate n (V2 -1 0)
expandStep (D n) = replicate n (V2 0 -1)
expandStep (R n) = replicate n (V2 1 0)
```

## Expressing the problem

Next was definition of a couple of functions to encode aspects of the problem. `manhattan`

is the Manhattan distance between two points. Two points are `touching`

if they have a Manhattan distance of 1 or less. Finally is the definition of how to move point 1 `towards`

point 2.

```
manhattan :: Position -> Position -> Int
manhattan p1 p2 = max dx dy
where V2 dx dy = abs $ p1 ^-^ p2
touching :: Position -> Position -> Bool
touching p1 p2 = (manhattan p1 p2) <= 1
towards :: Position -> Position -> Position
towards p1 p2 = signum $ p2 ^-^ p1
```

## Solution

And finally, we can move to the solution. The core is the `knotStep`

function which updates the position of a knot, given its current position and the knot ahead of it.

```
knotStep :: (Position, [Position]) -> Position -> (Position, [Position])
knotStep (h, ks) kt = (kt', (kt':ks))
where kt' = if kt `touching` h
then kt
else kt ^+^ (kt `towards` h)
```

The slightly odd signature is because this is used in a `foldl`

to update all the knots in sequence (in `ropeStep`

), within *another* `foldl`

to update the rope head with all the defined steps (in `ropeSteps`

).

```
ropeSteps :: Rope -> Path -> Rope
ropeSteps rope steps = foldl' ropeStep rope steps
ropeStep :: Rope -> Position -> Rope
ropeStep rope step = rope & headK .~ h
& knots .~ (reverse kts)
& trace %~ S.insert kt
where h = (rope ^. headK) ^+^ step
(kt, kts) = foldl' knotStep (h, []) $ rope ^. knots
```

All that's needed is to initialise the rope with the required number of knots.

```
part1, part2 :: Path -> Int
part1 steps = S.size $ rope ^. trace
where rope = ropeSteps (newRope 1) steps
part2 steps = S.size $ rope ^. trace
where rope = ropeSteps (newRope 9) steps
```

## Code

You can get the code from my locally-hosted Git repo, or from Gitlab.