In [1]:
inputLines = lines <$> readFile "input/day03.txt"
inputLine1 = head <$> inputLines
inputLine2 = head . tail <$> inputLines

In [2]:
import qualified Data.Set as Set
import Data.List.Split
import Control.Lens
import Data.Function

Calculate the (x, y) points which belong to a path


In [3]:
calculatePath :: String -> [(Int, Int)]
calculatePath path =
    let
        parseDirection 'U' = over _2 succ
        parseDirection 'D' = over _2 pred
        parseDirection 'L' = over _1 pred
        parseDirection 'R' = over _1 succ
        
        parseSegment (direction:distance) = replicate (read distance) (parseDirection direction)
        
        moves = concatMap parseSegment $ splitOn "," path
    in
        tail $ scanl (flip ($)) (0, 0) moves

Calculate the intersection points of two paths


In [4]:
pathIntersections :: String -> String -> [(Int, Int)]
pathIntersections path1 path2 = Set.toList $ on Set.intersection pointsForPath path1 path2
    where
        pointsForPath = Set.fromList . calculatePath

Verify given example


In [5]:
pathIntersections "R8,U5,L5,D3" "U7,R6,D4,L4"


[(3,3),(6,5)]

Part 1

Find the intersection point which has the smallest Manhattan distance from the origin


In [6]:
manhattanDistance = uncurry ((+) `on` abs)

closestIntersectionDistance :: String -> String -> Int
closestIntersectionDistance path1 path2 = minimum . map manhattanDistance $ pathIntersections path1 path2

Verify given examples


In [7]:
closestIntersectionDistance "R8,U5,L5,D3" "U7,R6,D4,L4"


6

In [8]:
closestIntersectionDistance "R75,D30,R83,U83,L12,D49,R71,U7,L72" "U62,R66,U55,R34,D71,R55,D58,R83"


159

In [9]:
closestIntersectionDistance "R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51" "U98,R91,D20,R16,D67,R40,U7,R15,U6,R7"


135

Solution


In [10]:
closestIntersectionDistance <$> inputLine1 <*> inputLine2


2129

Part 2

Now we are interested in the intersection point which has the fewest combined steps from the origin for both paths.


In [11]:
import qualified Data.Map as Map

In [12]:
pathIntersectionsWithCombinedDistance :: String -> String -> [((Int, Int), Int)]
pathIntersectionsWithCombinedDistance path1 path2 = Map.toList $ on (Map.intersectionWith (+)) pointsForPathWithDistance path1 path2
    where
        pointsForPathWithDistance path = Map.fromList (zip (calculatePath path) [1..])

In [13]:
minimumCombinedDistanceForIntersections :: String -> String -> Int
minimumCombinedDistanceForIntersections path1 path2 = minimum . map snd $ pathIntersectionsWithCombinedDistance path1 path2

Verify given examples


In [14]:
minimumCombinedDistanceForIntersections "R8,U5,L5,D3" "U7,R6,D4,L4"


30

In [15]:
minimumCombinedDistanceForIntersections "R75,D30,R83,U83,L12,D49,R71,U7,L72" "U62,R66,U55,R34,D71,R55,D58,R83"


610

In [16]:
minimumCombinedDistanceForIntersections "R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51" "U98,R91,D20,R16,D67,R40,U7,R15,U6,R7"


410

Solution


In [17]:
minimum . map snd <$> (pathIntersectionsWithCombinedDistance <$> inputLine1 <*> inputLine2)


134662