-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay3.hs
137 lines (114 loc) · 3.58 KB
/
Day3.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
module Day3 where
import Data.List (find, minimum)
import qualified Data.Map as Map
import Data.Maybe (fromJust)
import qualified Data.Set as Set
import Debug.Trace
import Prelude hiding (Left, Right)
import Utils (tail', wordsWhen)
data Direction
= Up
| Right
| Down
| Left
instance Show Direction where
show dir =
case dir of
Up -> "U"
Right -> "R"
Down -> "D"
Left -> "L"
data Move =
Move
{ dir :: Direction
, dist :: Int
}
instance Show Move where
show move = show (dir move) ++ show (dist move)
data Coords =
Coords
{ x :: Int
, y :: Int
}
deriving (Eq, Ord)
instance Show Coords where
show coords = "(" ++ show (x coords) ++ ", " ++ show (y coords) ++ ")"
(<+>) :: Coords -> Coords -> Coords
(Coords ax ay) <+> (Coords bx by) = Coords (ax + bx) (ay + by)
data Step =
Step
{ idx :: Int
, coords :: Coords
}
instance Show Step where
show step = show (idx step) ++ ": " ++ show (coords step)
parseMove :: String -> Move
parseMove s =
let direction =
case head s of
'U' -> Up
'R' -> Right
'D' -> Down
'L' -> Left
distance = read (tail s)
in Move { dir = direction, dist = distance }
moveToCoords :: Move -> Coords
moveToCoords move =
case dir move of
Up -> Coords 0 (-dist move)
Right -> Coords (dist move) 0
Down -> Coords 0 (dist move)
Left -> Coords (-dist move) 0
intermediateCoords :: Coords -> [Coords]
intermediateCoords coords =
case coords of
Coords x 0 -> map (flip Coords 0) [0,signum x..x]
Coords 0 y -> map (Coords 0) [0,signum y..y]
_ -> error "x or y must be zero"
coordsToSteps :: [Coords] -> [Step]
coordsToSteps = zipWith Step [0..]
tracePath :: Coords -> [Move] -> [Coords]
tracePath _ [] = []
tracePath origin (move:moves) =
let coords = moveToCoords move
next = coords <+> origin
in map (<+> origin) (intermediateCoords coords) ++ tail' (tracePath next moves)
takeShortcuts :: [Step] -> [Step]
takeShortcuts steps =
let loop (step:steps) counter indexMap =
let (newStep, newIndexMap) =
case Map.lookup (coords step) indexMap of
Just index -> (step { idx = index }, indexMap)
Nothing -> (step { idx = counter }, Map.insert (coords step) (idx step) indexMap)
in newStep : loop steps (idx step + 1) newIndexMap
in loop steps 0 Map.empty
findIntersections :: [[Move]] -> [Coords]
findIntersections = findPathIntersections . map (tracePath (Coords 0 0))
findPathIntersections :: [[Coords]] -> [Coords]
findPathIntersections =
Set.toAscList .
Set.delete (Coords 0 0) . foldr1 Set.intersection . map Set.fromList
manhattanDistance :: Coords -> Int
manhattanDistance coords = abs (x coords) + abs (y coords)
part1 :: [[Move]] -> Int
part1 = minimum . map manhattanDistance . findIntersections
part2 :: [[Move]] -> Int
part2 moves =
let path = map (tracePath (Coords 0 0)) moves
steps = map coordsToSteps path
intersections = findPathIntersections path
intersectionStepsAmount stepsList =
map
(\is -> idx $ fromJust $ find ((== is) . coords) stepsList)
intersections
stepAmounts1 = intersectionStepsAmount (head steps)
stepAmounts2 = intersectionStepsAmount (last steps)
in minimum (zipWith (+) stepAmounts1 stepAmounts2)
parseMoves :: String -> [Move]
parseMoves = map parseMove . wordsWhen (== ',')
main :: IO ()
main = do
contents <- readFile "input/day3.txt"
let wires = map parseMoves (lines contents)
putStrLn ("Part 1: " ++ show (part1 wires))
putStrLn ("Part 2: " ++ show (part2 wires))