This commit is contained in:
Christian
2023-10-18 10:47:20 +02:00
parent 402aa9fb80
commit dcf6f48993
6 changed files with 273 additions and 2 deletions

View File

@@ -49,15 +49,17 @@ library
Days.D13 Days.D13
Days.D14 Days.D14
Days.D15 Days.D15
Days.D16
build-depends: build-depends:
bytestring bytestring
, either , either
, hashable
, hashtables ^>=1.3.1
, heap ^>=1.0.4
, lens , lens
, megaparsec ^>=9.4.0 , megaparsec ^>=9.4.0
, mtl , mtl
, parser-combinators , parser-combinators
, hashable
, hashtables ^>=1.3.1
, text , text
, vector ^>=0.13.0.0 , vector ^>=0.13.0.0

View File

@@ -23,6 +23,7 @@ paths =
, "./data/13.in" , "./data/13.in"
, "./data/14.in" , "./data/14.in"
, "./data/15.in" , "./data/15.in"
, "./data/16.in"
] ]
solutions :: [(Int, Day, FilePath)] solutions :: [(Int, Day, FilePath)]

View File

@@ -22,6 +22,7 @@ paths =
, "./data/13.in" , "./data/13.in"
, "./data/14.in" , "./data/14.in"
, "./data/15.in" , "./data/15.in"
, "./data/16.in"
] ]
solutions :: [(Integer, [Day], FilePath)] solutions :: [(Integer, [Day], FilePath)]

51
data/16.in Normal file
View File

@@ -0,0 +1,51 @@
Valve NV has flow rate=5; tunnels lead to valves ZV, CG, YB, HX, OY
Valve NU has flow rate=6; tunnels lead to valves DA, MA, OA, DK
Valve VU has flow rate=0; tunnels lead to valves PS, FX
Valve JW has flow rate=0; tunnels lead to valves AA, MD
Valve RI has flow rate=0; tunnels lead to valves OY, DG
Valve DG has flow rate=9; tunnels lead to valves TG, RI, DF, EV, KW
Valve PH has flow rate=7; tunnels lead to valves KW, OW, LT, LZ
Valve KZ has flow rate=12; tunnels lead to valves ET, QV, CK, MS
Valve IX has flow rate=0; tunnels lead to valves TS, DO
Valve MS has flow rate=0; tunnels lead to valves LZ, KZ
Valve IL has flow rate=0; tunnels lead to valves DO, ET
Valve EJ has flow rate=20; tunnels lead to valves AV, JY
Valve DK has flow rate=0; tunnels lead to valves NU, CG
Valve YB has flow rate=0; tunnels lead to valves NV, PS
Valve OA has flow rate=0; tunnels lead to valves YA, NU
Valve DA has flow rate=0; tunnels lead to valves NU, RG
Valve KO has flow rate=0; tunnels lead to valves AA, TG
Valve RG has flow rate=4; tunnels lead to valves DF, DA, ZV, MD, LB
Valve MA has flow rate=0; tunnels lead to valves AA, NU
Valve OW has flow rate=0; tunnels lead to valves DO, PH
Valve KW has flow rate=0; tunnels lead to valves DG, PH
Valve DO has flow rate=14; tunnels lead to valves IX, IL, CZ, OW
Valve DF has flow rate=0; tunnels lead to valves RG, DG
Valve TG has flow rate=0; tunnels lead to valves DG, KO
Valve LB has flow rate=0; tunnels lead to valves RG, FX
Valve HX has flow rate=0; tunnels lead to valves AA, NV
Valve GB has flow rate=0; tunnels lead to valves AV, XK
Valve CG has flow rate=0; tunnels lead to valves DK, NV
Valve LT has flow rate=0; tunnels lead to valves AO, PH
Valve FX has flow rate=23; tunnels lead to valves LB, HY, VU
Valve ET has flow rate=0; tunnels lead to valves IL, KZ
Valve CK has flow rate=0; tunnels lead to valves UX, KZ
Valve LZ has flow rate=0; tunnels lead to valves PH, MS
Valve YA has flow rate=17; tunnels lead to valves JY, OA
Valve TS has flow rate=0; tunnels lead to valves NO, IX
Valve NO has flow rate=8; tunnel leads to valve TS
Valve XK has flow rate=24; tunnel leads to valve GB
Valve PS has flow rate=18; tunnels lead to valves EV, VU, YB
Valve AA has flow rate=0; tunnels lead to valves JW, HX, MA, KO
Valve MD has flow rate=0; tunnels lead to valves JW, RG
Valve JM has flow rate=19; tunnels lead to valves QV, HY, AO
Valve AV has flow rate=0; tunnels lead to valves EJ, GB
Valve AO has flow rate=0; tunnels lead to valves JM, LT
Valve JY has flow rate=0; tunnels lead to valves YA, EJ
Valve OY has flow rate=0; tunnels lead to valves NV, RI
Valve UX has flow rate=13; tunnels lead to valves CZ, CK
Valve HY has flow rate=0; tunnels lead to valves JM, FX
Valve EV has flow rate=0; tunnels lead to valves PS, DG
Valve CZ has flow rate=0; tunnels lead to valves UX, DO
Valve ZV has flow rate=0; tunnels lead to valves NV, RG
Valve QV has flow rate=0; tunnels lead to valves JM, KZ

214
src/Days/D16.hs Normal file
View File

@@ -0,0 +1,214 @@
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Days.D16 where
import Common
import Data.Char (isAsciiUpper)
import Data.List (foldl', sortOn)
import Data.Map (Map)
import Data.Map qualified as Map
import Data.Maybe (fromJust, fromMaybe, isJust)
import Data.Ord (Down (..))
import Data.Set (Set)
import Data.Set qualified as Set
import Data.Text (Text)
import Data.Tuple (swap)
import Data.Vector (Vector)
import Data.Vector qualified as Vector
import Parse
import Text.Megaparsec (sepBy, takeWhile1P, (<|>))
import Text.Megaparsec.Char (space)
import Text.Megaparsec.Char.Lexer qualified as Lexer
type ValveName = Text
parser :: Parser (Map ValveName Int, Map ValveName [ValveName])
parser = makeMaps <$> someLines nodeDescription
where
symbol = Lexer.symbol space
lexeme = Lexer.lexeme space
valve :: Parser Text
valve = takeWhile1P Nothing isAsciiUpper
nodeDescription =
(,,)
<$ symbol "Valve"
<*> lexeme valve
<* symbol "has flow rate="
<*> number
<* symbol ";"
<* ( symbol "tunnels lead to valves"
<|> symbol "tunnel leads to valve"
)
<*> sepBy valve (symbol ",")
makeMaps :: [(Text, Int, [Text])] -> (Map Text Int, Map Text [Text])
makeMaps intermediate =
let (valve, rate, connections) = unzip3 intermediate
in ( Map.fromList (zip valve rate),
Map.fromList (zip valve connections)
)
floydWarshall :: (Ord a) => Map a [a] -> Map (a, a) Int
floydWarshall m =
let keys = Map.keys m
in foldl'
( \acc (k, i, j) ->
Map.alter
( let newPath =
do
dist_i_k <- Map.lookup (i, k) acc
dist_k_j <- Map.lookup (k, j) acc
return $ dist_i_k + dist_k_j
in \case
Nothing -> newPath
Just v -> Just $ case newPath of
Nothing -> v
Just newPath_ -> min v newPath_
)
(i, j)
acc
)
(Map.fromList [((a, b), 1) | a <- keys, b <- fromMaybe [] $ Map.lookup a m])
[(k, i, j) | k <- keys, i <- keys, j <- keys]
data Network = Network
{ rates :: Map ValveName Int,
nextBest :: Map ValveName (Vector (ValveName, Int))
}
preProcess ::
Map ValveName Int ->
Map ValveName [ValveName] ->
Network
preProcess rates network =
let distances = floydWarshall network
possibleStops = Map.filterWithKey (\k v -> v > 0 || k == "AA") rates
possibleStopsV = Vector.fromList . Map.toList $ possibleStops
nextBest =
Map.fromList
[ ( from,
Vector.fromList
. map snd
. sortOn (Down . fst)
$ [ (rate, (to, dist + 1))
| (to, rate) <- Vector.toList possibleStopsV,
rate > 0,
let maybeDist = Map.lookup (from, to) distances,
isJust maybeDist,
let dist = fromJust maybeDist
]
)
| (from, _) <- Vector.toList possibleStopsV
]
in Network
{ rates = possibleStops,
nextBest = nextBest
}
data SearchState = SearchState
{ pressure :: Int,
opened :: Set ValveName,
pos :: (ValveName, ValveName),
time :: (Int, Int)
}
maximizePressure ::
Int ->
Bool ->
ValveName ->
Network ->
Int
maximizePressure deadline withElephant start (Network {..}) =
go
0
SearchState
{ pressure = 0,
opened = Set.empty,
pos = (start, start),
time = (deadline, if withElephant then deadline else 0)
}
where
go :: Int -> SearchState -> Int
go best searcher =
let nextStates = branch searcher
in if null nextStates
then pressure searcher
else
foldl'
( \best_ nextSearcher ->
if bound nextSearcher > best_
then max (go best_ nextSearcher) best_
else best_
)
best
nextStates
branch :: SearchState -> [SearchState]
branch SearchState {..} =
[ let f = if nextTime < snd time then swap else id
in SearchState
{ pressure = pressure + rate * nextTime,
opened = Set.insert nextPos opened,
pos = f (nextPos, snd pos),
time = f (nextTime, snd time)
}
| (nextPos, dist) <-
maybe [] Vector.toList (Map.lookup (fst pos) nextBest),
fst time > dist,
not $ Set.member nextPos opened,
let nextTime = fst time - dist,
let rate = fromJust (Map.lookup nextPos rates)
]
heuristic :: Vector (Vector (ValveName, Int, Int))
heuristic =
Vector.fromList
[ Vector.reverse
. Vector.fromList
. sortOn (\(_, min_dist, rate) -> (time - min_dist) * rate)
$ [ (valve, min_dist, rate)
| (valve, rate) <- Map.toList rates,
let min_dist =
minimum
. Vector.map snd
. fromJust
$ Map.lookup valve nextBest,
time > min_dist
]
| time <- [0 .. deadline]
]
bound :: SearchState -> Int
bound SearchState {..} = uncurry (go opened) time pressure
where
go opened max_t min_t bound =
case Vector.toList
. Vector.filter (\(valve, _, _) -> not $ Set.member valve opened)
$ (heuristic Vector.! max_t) of
[] -> bound
((valveId, min_dist, flow) : _) ->
let max_t' = max_t - min_dist
bound' = bound + flow * max_t'
min_t' = min min_t max_t'
max_t'' = max min_t max_t'
opened' = Set.insert valveId opened
in go opened' max_t'' min_t' bound'
part1 :: Network -> Int
part1 = maximizePressure 30 False "AA"
part2 :: Network -> Int
part2 = maximizePressure 26 True "AA"
day :: Day
day =
parsecDay
(uncurry preProcess <$> parser)
( definitive . part1,
definitive . part2
)

View File

@@ -27,6 +27,7 @@ import Days.D12 qualified as D12
import Days.D13 qualified as D13 import Days.D13 qualified as D13
import Days.D14 qualified as D14 import Days.D14 qualified as D14
import Days.D15 qualified as D15 import Days.D15 qualified as D15
import Days.D16 qualified as D16
import Data.ByteString.Char8 qualified as BS import Data.ByteString.Char8 qualified as BS
import Data.Text.IO qualified as T import Data.Text.IO qualified as T
@@ -54,4 +55,5 @@ days =
, [D13.day] , [D13.day]
, [D14.day] , [D14.day]
, [D15.day] , [D15.day]
, [D16.day]
] ]