-
Notifications
You must be signed in to change notification settings - Fork 0
/
Day13.hs
225 lines (190 loc) · 7.31 KB
/
Day13.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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
-- Day 13: Mine Cart Madness --
--
-- Usage:
--
-- ghc -O3 Day13.hs
-- ./Day13 < ../inputs/13.txt
import Data.List ( find
, groupBy
, intercalate
, sortOn
, maximumBy
, intersect
)
import Data.Map ( Map )
import qualified Data.Map as Map
import Data.Maybe ( fromJust )
import Data.Ord ( comparing )
import Data.Tuple ( swap )
-- TYPES
data Track = Track TrackType TrackState
deriving (Show, Eq)
data TrackType = TVertical -- |
| THorizontal -- -
| TDiagonalUp -- /
| TDiagonalDown -- \
| TIntersection -- +
deriving (Show, Eq)
data TrackState = SCart CartDirection IntersectionRule
| SCrash
| SClear
deriving (Show, Eq)
data CartDirection = DUp
| DDown
| DLeft
| DRight
deriving (Show, Eq)
data IntersectionRule = RLeft
| RStraight
| RRight
deriving (Show, Eq)
type Coord = (Integer, Integer)
type TrackNetwork = Map Coord Track
-- PARSING
parseTrack :: Char -> Track
parseTrack '|' = Track TVertical SClear
parseTrack '-' = Track THorizontal SClear
parseTrack '/' = Track TDiagonalUp SClear
parseTrack '\\' = Track TDiagonalDown SClear
parseTrack '+' = Track TIntersection SClear
parseTrack '^' = Track TVertical $ SCart DUp RLeft
parseTrack 'v' = Track TVertical $ SCart DDown RLeft
parseTrack '<' = Track THorizontal $ SCart DLeft RLeft
parseTrack '>' = Track THorizontal $ SCart DRight RLeft
parseTrack x = error $ "parse error at: " ++ show x
parseLine :: Coord -> String -> [(Coord, Track)]
parseLine (x, y) (' ' : xs) = parseLine (x + 1, y) xs
parseLine (x, y) (t : xs) = ((x, y), parseTrack t) : parseLine (x + 1, y) xs
parseLine _ [] = []
-- CART TRANSITIONS
nextTrack :: CartDirection -> Coord -> TrackNetwork -> (Coord, Track)
nextTrack dir (x, y) network = (coord, fromJust $ Map.lookup coord network)
where
coord = case dir of
DUp -> (x, y - 1)
DDown -> (x, y + 1)
DLeft -> (x - 1, y)
DRight -> (x + 1, y)
applyWithCrashState :: TrackState -> Track -> Track -> Track
applyWithCrashState crashState (Track _ (SCart dir rule)) track = case track of
crashSite@(Track _ SCrash) -> crashSite
Track t (SCart _ _) -> Track t crashState
Track TDiagonalUp SClear -> case dir of
DLeft -> Track TDiagonalUp (SCart DDown rule)
DRight -> Track TDiagonalUp (SCart DUp rule)
DUp -> Track TDiagonalUp (SCart DRight rule)
DDown -> Track TDiagonalUp (SCart DLeft rule)
Track TDiagonalDown SClear -> case dir of
DLeft -> Track TDiagonalDown (SCart DUp rule)
DRight -> Track TDiagonalDown (SCart DDown rule)
DUp -> Track TDiagonalDown (SCart DLeft rule)
DDown -> Track TDiagonalDown (SCart DRight rule)
Track t SClear -> Track t (SCart dir rule)
moveCart :: (Coord, Track) -> (Coord, Track) -> TrackNetwork -> TrackNetwork
moveCart (coord, Track prevType _) to = Map.union updatedEntries
where
updatedEntries = Map.fromList [prevWithoutCart, to]
prevWithoutCart = (coord, Track prevType SClear)
nextIntersectionRule :: IntersectionRule -> IntersectionRule
nextIntersectionRule rule = case rule of
RLeft -> RStraight
RStraight -> RRight
RRight -> RLeft
move
:: (Track -> Track -> Track) -> (Coord, Track) -> TrackNetwork -> TrackNetwork
move apply from@(coord, Track TIntersection (SCart dir rule)) network =
moveCart from to network
where
to = (coord', apply fromTrack toTrack)
(coord', toTrack) = nextTrack newDir coord network
fromTrack = Track TIntersection (SCart newDir newRule)
newRule = nextIntersectionRule rule
newDir = case (dir, rule) of
(DDown , RLeft ) -> DRight
(DDown , RRight ) -> DLeft
(DUp , RLeft ) -> DLeft
(DUp , RRight ) -> DRight
(DLeft , RLeft ) -> DDown
(DLeft , RRight ) -> DUp
(DRight , RLeft ) -> DUp
(DRight , RRight ) -> DDown
(currentDir, RStraight) -> currentDir
move apply from@(coord, fromTrack@(Track _ (SCart dir _))) network = moveCart
from
to
network
where
to = (coord', apply fromTrack toTrack)
(coord', toTrack) = nextTrack dir coord network
move _ (_, Track _ _) network = network
-- DISPLAY
displayTrackNetwork :: TrackNetwork -> String
displayTrackNetwork network =
intercalate
"\n"
[ displayRow row
| row <- groupBy sameRow $ sortOn (snd . fst) $ Map.toList network
]
++ "\n"
++ show carts
where
sameRow ((_, y1), _) ((_, y2), _) = y1 == y2
carts = occupiedTracks network
displayRow :: [(Coord, Track)] -> String
displayRow row = [ displayTrack (trackAt (x, y)) | x <- [0 .. xMax] ]
where
trackAt (x', y') = snd <$> find ((== (x', y')) . fst) row
((xMax, _), _) = maximumBy (comparing (fst . fst)) row
(_ , y) = fst $ head row
displayTrack :: Maybe Track -> Char
displayTrack (Just (Track _ SCrash)) = 'X'
displayTrack (Just (Track _ (SCart DUp _))) = '^'
displayTrack (Just (Track _ (SCart DDown _))) = 'v'
displayTrack (Just (Track _ (SCart DLeft _))) = '<'
displayTrack (Just (Track _ (SCart DRight _))) = '>'
displayTrack (Just (Track THorizontal _)) = '-'
displayTrack (Just (Track TVertical _)) = '|'
displayTrack (Just (Track TDiagonalDown _)) = '\\'
displayTrack (Just (Track TDiagonalUp _)) = '/'
displayTrack (Just (Track TIntersection _)) = '+'
displayTrack Nothing = ' '
-- PARTS
occupiedTracks :: TrackNetwork -> [(Coord, Track)]
occupiedTracks network =
sortOn (swap . fst) [ t | t@(_, Track _ (SCart _ _)) <- Map.toList network ]
tick
:: (Track -> Track -> Track)
-> TrackNetwork
-> [(Coord, Track)]
-> TrackNetwork
tick _ network [] = network
tick apply network (cart : carts) = tick
apply
network'
(carts `intersect` occupiedTracks network')
where network' = move apply cart network
firstCrash :: TrackNetwork -> (Coord, Track)
firstCrash network
| null crashes = firstCrash $ tick apply network (occupiedTracks network)
| otherwise = head crashes
where
crashes =
sortOn (swap . fst) [ t | t@(_, Track _ SCrash) <- Map.toList network ]
apply = applyWithCrashState SCrash
lastCart :: TrackNetwork -> Maybe (Coord, Track)
lastCart network = case carts of
[] -> Nothing
[cart] -> Just cart
_ -> lastCart $ tick apply network carts
where
carts = occupiedTracks network
apply = applyWithCrashState SClear
part1 :: TrackNetwork -> String
part1 = show . firstCrash
part2 :: TrackNetwork -> String
part2 = show . lastCart
main :: IO ()
main = interact (\input -> unlines [part1 $ parse input, part2 $ parse input])
where
parse = Map.fromList . concatMap parseRow . zip [0 ..] . lines
parseRow (y, line) = parseLine (0, y) line