[haskell] Solve Day 5

This commit is contained in:
Akshay Mankar 2023-12-08 23:15:30 +01:00
parent 66839b7bfd
commit d6f9b4dae3
Signed by: axeman
GPG key ID: CA08F3AB62369B89
6 changed files with 626 additions and 0 deletions

View file

@ -27,4 +27,11 @@
checkBash = false; checkBash = false;
checkRust = false; checkRust = false;
}; };
day5 = {
part1 = "462648396";
part2 = "2520479";
checkHaskell = true;
checkBash = false;
checkRust = false;
};
} }

View file

@ -24,6 +24,7 @@ library
, Day2 , Day2
, Day3 , Day3
, Day4 , Day4
, Day5
executable aoc2023 executable aoc2023
import: warnings import: warnings

View file

@ -7,6 +7,7 @@ import Day2 qualified
import System.Environment (getArgs) import System.Environment (getArgs)
import qualified Day3 import qualified Day3
import qualified Day4 import qualified Day4
import qualified Day5
main :: IO () main :: IO ()
main = do main = do
@ -15,4 +16,5 @@ main = do
["day2"] -> Day2.run ["day2"] -> Day2.run
["day3"] -> Day3.run ["day3"] -> Day3.run
["day4"] -> Day4.run ["day4"] -> Day4.run
["day5"] -> Day5.run
args -> error $ "Invlaid args: " <> show args args -> error $ "Invlaid args: " <> show args

370
haskell/src/Day5.hs Normal file
View file

@ -0,0 +1,370 @@
{-# LANGUAGE RecordWildCards #-}
{-# OPTIONS_GHC -Wwarn -Wall -Wunused-top-binds #-}
module Day5 (run) where
import Data.Char
import Data.IntervalMap.FingerTree (Interval (Interval), IntervalMap, high, low)
import Data.IntervalMap.FingerTree qualified as IntervalMap
import Data.Maybe (maybeToList)
import Text.ParserCombinators.ReadP
run :: IO ()
run = do
almanac <- readInput
putStrLn $ "Part 1 Answer: " <> show (part1 almanac)
putStrLn $ "Part 2 Answer: " <> show (part2 almanac)
part1 :: Almanac -> Int
part1 almanac =
minimum $ map (seedToLocation almanac) (seeds almanac)
part2 :: Almanac -> Int
part2 almanac =
minimum $ seedToLocationMap almanac
type Seeds = [Int]
type RangeMap = IntervalMap Int Int
data Almanac = Almanac
{ seeds :: Seeds,
seedToSoilMap :: RangeMap,
soilToFertilizerMap :: RangeMap,
fertilizerToWaterMap :: RangeMap,
waterToLightMap :: RangeMap,
lightToTemperatureMap :: RangeMap,
temperatureToHumidityMap :: RangeMap,
humidityToLocationMap :: RangeMap
}
deriving (Show)
rangeMapLookup :: (Ord a, Enum a, Enum b) => (a -> b) -> a -> IntervalMap a b -> b
rangeMapLookup f src m =
case IntervalMap.search src m of
[] -> f src
-- Ignores the fact that there can be many matches as it is not defined in
-- the problem statement
((srcRange, destStart) : _) -> rangeLookup srcRange destStart src
rangeLookup :: (Enum a, Enum b) => Interval a -> b -> a -> b
rangeLookup ia bStart a =
toEnum $ fromEnum bStart + (fromEnum a - fromEnum (low ia))
seedToLocation :: Almanac -> Int -> Int
seedToLocation Almanac {..} seed =
foldl
(rangeMapLookup id)
seed
[ seedToSoilMap,
soilToFertilizerMap,
fertilizerToWaterMap,
waterToLightMap,
lightToTemperatureMap,
temperatureToHumidityMap,
humidityToLocationMap
]
seedToLocationMap :: Almanac -> RangeMap
seedToLocationMap Almanac {..} =
foldl
(composeRangeMaps id id)
(seedToSeedMap seeds)
[ seedToSoilMap,
soilToFertilizerMap,
fertilizerToWaterMap,
waterToLightMap,
lightToTemperatureMap,
temperatureToHumidityMap,
humidityToLocationMap
]
where
seedToSeedMap :: Seeds -> RangeMap
seedToSeedMap [] = mempty
seedToSeedMap [_] = mempty
seedToSeedMap (x : y : rest) = IntervalMap.insert (Interval x (x + y - 1)) x $ seedToSeedMap rest
-- Example:
--
-- ----------------------- :: -------------------- :: -------------------- :: ------------------- :: -------------------------------------------
-- Name :: source interval :: overlapping intervals:: resolution :: Remarks
-- ----------------------- :: -------------------- :: -------------------- :: ------------------- :: -------------------------------------------
-- 1. Perfect overlap :: [0, 10] -> [10, 20] :: [10, 20] -> [30, 40] :: [0, 10] -> [30, 40] ::
-- ----------------------- :: -------------------- :: ---------------------:: ------------------- :: -------------------------------------------
-- 2. Right skewed overlap :: [0, 10] -> [15, 25] :: [10, 20] -> [30, 40] :: [0, 5] -> [35, 40] ::
-- :: :: :: [6, 10] -> [21, 25] ::
-- ----------------------- :: -------------------- :: ---------------------:: ------------------- :: -------------------------------------------
-- 3. Left skewed overlap :: [0, 10] -> [ 5, 15] :: [10, 20] -> [30, 40] :: [0, 4] -> [ 5, 9] ::
-- :: :: :: [5, 10] -> [30, 35] ::
-- ----------------------- :: -------------------- :: ---------------------:: ------------------- :: -------------------------------------------
-- 4. Containing overlap :: [0, 10] -> [10, 20] :: [12, 17] -> [32, 37] :: [0, 1] -> [10, 11] :: left skew
-- :: :: :: [2, 7] -> [12, 17] :: matches
-- :: :: :: [8, 10] -> [ 8, 10] :: right skew
-- ----------------------- :: -------------------- :: ---------------------:: ------------------- :: -------------------------------------------
-- 5. Contained overlap :: [0, 10] -> [10, 20] :: [ 9, 22] -> [29, 42] :: [0, 10] -> [30, 40] ::
-- ----------------------- :: -------------------- :: ---------------------:: ------------------- :: -------------------------------------------
-- 6. Multiple overlaps :: [0, 10] -> [10, 20] :: [ 9, 13] -> [29, 33] :: [0, 3] -> [30, 33] ::
-- :: :: [15, 17] -> [35, 37] :: [4, 4] -> [14, 14] ::
-- :: :: [19, 25] -> [39, 45] :: [5, 7] -> [35, 37] ::
-- :: :: :: [8, 8] -> [18, 18] ::
-- :: :: :: [9, 10] -> [39, 40] ::
-- ----------------------- :: -------------------- :: -------------------- :: ------------------- :: -------------------------------------------
-- >>> i = Interval
-- >>> assertEqual a b = if a == b then True else error $ "Expected: " <> show a <> "\n Actual: " <> show b
--
-- Perfect overlap
-- >>> assertEqual Nothing $ preMatchInterval (i 0 10) 10 (i 10 20)
-- True
--
-- Right skewed overlap
-- >>> assertEqual Nothing $ preMatchInterval (i 0 10) 15 (i 10 20)
-- True
--
-- Left skewed overlap
-- >>> assertEqual (Just $ i 0 4) $ preMatchInterval (i 0 10) 5 (i 10 20)
-- True
--
-- Containing overlap
-- >>> assertEqual (Just $ i 0 1) $ preMatchInterval (i 0 10) 10 (i 12 17)
-- True
--
-- Multiple overlaps 1:
-- >>> assertEqual Nothing $ preMatchInterval (i 0 10) 10 (i 9 13)
-- True
--
-- Extra case, probably not going to happen:
-- >>> assertEqual (Just $ i 0 10) $ preMatchInterval (i 0 10) 10 (i 30 40)
-- True
preMatchInterval :: (Ord b, Enum b, Enum a, Ord a) => Interval a -> b -> Interval b -> Maybe (Interval a)
preMatchInterval ia bStart ib
| bStart < low ib =
Just $ Interval (low ia) (min (high ia) (succByDiff (low ia) (pred $ low ib) bStart))
| otherwise = Nothing
-- >>> i = Interval
-- >>> assertEqual a b = if a == b then True else error $ "Expected: " <> show a <> "\n Actual: " <> show b
--
-- Perfect overlap
-- >>> assertEqual (Just $ i 0 10) $ matchInterval (i 0 10) 10 (i 10 20)
-- True
--
-- Right skewed overlap
-- >>> assertEqual (Just $ i 0 5) $ matchInterval (i 0 10) 15 (i 10 20)
-- True
--
-- Left skewed overlap
-- >>> assertEqual (Just $ i 5 10) $ matchInterval (i 0 10) 5 (i 10 20)
-- True
--
-- Containing overlap
-- >>> assertEqual (Just $ i 2 7) $ matchInterval (i 0 10) 10 (i 12 17)
-- True
--
-- Multiple overlaps 1:
-- >>> assertEqual (Just $ i 0 3) $ matchInterval (i 0 10) 10 (i 9 13)
-- True
--
-- Extra case, probably not going to happen:
-- >>> assertEqual Nothing $ matchInterval (i 0 10) 10 (i 30 40)
-- True
matchInterval :: (Ord a, Ord b, Enum a, Enum b) => Interval a -> b -> Interval b -> Maybe (Interval a)
matchInterval ia bStart ib
| bStart `elemInterval` ib
|| bEnd `elemInterval` ib
|| (bStart <= low ib && bEnd >= high ib) =
Just $ Interval aStart aEnd
| otherwise = Nothing
where
bEnd = succByDiff bStart (high ia) (low ia)
aStart =
if bStart `elemInterval` ib
then low ia
else succByDiff (low ia) (low ib) bStart
aEnd =
if bEnd `elemInterval` ib
then high ia
else succByDiff (low ia) (high ib) bStart
-- >>> i = Interval
-- >>> assertEqual a b = if a == b then True else error $ "Expected: " <> show a <> "\n Actual: " <> show b
--
-- Perfect overlap
-- >>> assertEqual Nothing $ postMatchInterval (i 0 10) 10 (i 10 20)
-- True
--
-- Right skewed overlap
-- >>> assertEqual (Just $ i 6 10) $ postMatchInterval (i 0 10) 15 (i 10 20)
-- True
--
-- Left skewed overlap
-- >>> assertEqual Nothing $ postMatchInterval (i 0 10) 5 (i 10 20)
-- True
--
-- Containing overlap
-- >>> assertEqual (Just $ i 8 10) $ postMatchInterval (i 0 10) 10 (i 12 17)
-- True
--
-- Multiple overlaps 1:
-- >>> assertEqual (Just $ i 4 10) $ postMatchInterval (i 0 10) 10 (i 9 13)
-- True
--
-- Extra case, probably not going to happen:
-- >>> assertEqual Nothing $ postMatchInterval (i 0 10) 10 (i 30 40)
-- True
postMatchInterval :: (Ord a, Ord b, Enum a, Enum b) => Interval a -> b -> Interval b -> Maybe (Interval a)
postMatchInterval ia bStart ib
| bEnd > high ib =
Just $
Interval
(succByDiff (low ia) (succ $ high ib) bStart)
(high ia)
| otherwise = Nothing
where
bEnd = succByDiff bStart (high ia) (low ia)
-- >>> i = Interval
-- >>> assertEqual a b = if a == b then True else error $ "Expected: " <> show a <> "\n Actual: " <> show b
--
-- Perfect overlap
-- >>> assertEqual [i 0 10] $ newIntervals (i 0 10) 10 [(i 10 20)]
-- True
--
-- Right skewed overlap
-- >>> assertEqual [i 0 5, i 6 10] $ newIntervals (i 0 10) 15 [(i 10 20)]
-- True
--
-- Left skewed overlap
-- >>> assertEqual [i 0 4, i 5 10] $ newIntervals (i 0 10) 5 [(i 10 20)]
-- True
--
-- Containing overlap
-- >>> assertEqual [i 0 1, i 2 7, i 8 10] $ newIntervals (i 0 10) 10 [(i 12 17)]
-- True
--
-- Multiple overlaps 1:
-- >>> assertEqual [i 0 3, i 4 4, i 5 7, i 8 8, i 9 10] $ newIntervals (i 0 10) 10 [i 9 13, i 15 17, i 19 25]
-- True
--
-- >>> assertEqual [i 0 10] $ newIntervals (i 0 10) 10 []
-- True
--
-- Extra case, probably not going to happen:
-- >>> assertEqual [i 0 10] $ newIntervals (i 0 10) 10 [(i 30 40)]
-- True
newIntervals :: (Ord a, Ord b, Enum a, Enum b) => Interval a -> b -> [Interval b] -> [Interval a]
newIntervals ia _ [] = [ia]
newIntervals ia bStart (ib : rest) =
let pres = maybeToList $ preMatchInterval ia bStart ib
matches = maybeToList $ matchInterval ia bStart ib
in case postMatchInterval ia bStart ib of
Nothing -> pres <> matches
Just post -> pres <> matches <> newIntervals post (succByDiff bStart (low post) (low ia)) rest
composeRangeMaps :: (Ord a, Ord b, Ord c, Enum a, Enum b, Enum c, Bounded a) => (a -> b) -> (b -> c) -> IntervalMap a b -> IntervalMap b c -> IntervalMap a c
composeRangeMaps a2b b2c r1 r2 =
let allIntervalsWithStarts = IntervalMap.intersections (Interval minBound maxBound) r1
relevantIntervals =
foldMap
( \(ia, bStart) ->
let bEnd = succByDiff bStart (high ia) (low ia)
in newIntervals ia bStart $
map fst $
IntervalMap.intersections (Interval bStart bEnd) r2
)
allIntervalsWithStarts
in foldMap (\ia -> IntervalMap.singleton ia (rangeMapLookup b2c (rangeMapLookup a2b (low ia) r1) r2)) relevantIntervals
-- >>> enumDiff 15 10
-- 5
enumDiff :: (Enum a) => a -> a -> Int
enumDiff x y = fromEnum x - fromEnum y
-- >>> succByDiff 0 15 10
-- 5
succByDiff :: (Enum a, Enum b) => a -> b -> b -> a
succByDiff x y1 y2 = toEnum (fromEnum x + enumDiff y1 y2)
elemInterval :: (Ord a) => a -> Interval a -> Bool
a `elemInterval` i = low i <= a && a <= high i
-- * Parsing
emptyAlmanac :: Almanac
emptyAlmanac =
Almanac
{ seeds = mempty,
seedToSoilMap = mempty,
soilToFertilizerMap = mempty,
fertilizerToWaterMap = mempty,
waterToLightMap = mempty,
lightToTemperatureMap = mempty,
temperatureToHumidityMap = mempty,
humidityToLocationMap = mempty
}
readInput :: IO Almanac
readInput = do
input <- getContents
case readP_to_S (almanacParser <* eof) input of
[] -> error "Parser failed"
[(almanac, "")] -> pure almanac
xs -> error $ "Parser failed: " <> show xs
-- >>> readP_to_S numberParser "123"
-- [(123,"")]
numberParser :: ReadP Int
numberParser =
read <$> munch1 isDigit
-- >>> readP_to_S (seedsParser <* eof) "seeds: 79 14 55 13"
-- [([79,14,55,13],"")]
seedsParser :: ReadP Seeds
seedsParser = do
_ <- string "seeds: "
sepBy1 numberParser (char ' ')
-- >>> readP_to_S (rangeParser <* eof) "52 50 48"
-- [((52,50,48),"")]
rangeParser :: ReadP (Int, Int, Int)
rangeParser = do
destRangeStart <- numberParser
_ <- char ' '
srcRangeStart <- numberParser
_ <- char ' '
rangeLen <- numberParser
pure (destRangeStart, srcRangeStart, rangeLen)
-- >>> readP_to_S (rangeMapParser <* eof) "seed-to-soil map:\n50 98 2\n52 50 48"
-- [(("seed-to-soil",insert (Interval 50 97) 52 $ insert (Interval 98 99) 50 $ empty),"")]
rangeMapParser :: ReadP (String, RangeMap)
rangeMapParser = do
mapName <- munch1 (/= ' ')
_ <- string " map:\n"
rangeMap <-
foldr
( \(destStart, srcStart, len) ->
IntervalMap.insert (Interval srcStart (srcStart + len - 1)) destStart
)
mempty
<$> sepBy1 rangeParser (char '\n')
pure (mapName, rangeMap)
almanacParser :: ReadP Almanac
almanacParser = do
parsedSeeds <- seedsParser
_ <- string "\n\n"
rangeMaps <- sepBy1 rangeMapParser (string "\n\n")
pure $ foldr (uncurry updateAlmanacByName) (emptyAlmanac {seeds = parsedSeeds}) rangeMaps
updateAlmanacByName :: String -> RangeMap -> Almanac -> Almanac
updateAlmanacByName name rangeMap almanac =
case name of
"seed-to-soil" -> almanac {seedToSoilMap = rangeMap}
"soil-to-fertilizer" -> almanac {soilToFertilizerMap = rangeMap}
"fertilizer-to-water" -> almanac {fertilizerToWaterMap = rangeMap}
"water-to-light" -> almanac {waterToLightMap = rangeMap}
"light-to-temperature" -> almanac {lightToTemperatureMap = rangeMap}
"temperature-to-humidity" -> almanac {temperatureToHumidityMap = rangeMap}
"humidity-to-location" -> almanac {humidityToLocationMap = rangeMap}
_ -> almanac -- Ignore the wrong input ¯\_(ツ)_/¯

213
input/day5 Normal file
View file

@ -0,0 +1,213 @@
seeds: 3139431799 50198205 3647185634 110151761 2478641666 139825503 498892555 8913570 961540761 489996751 568452082 100080382 907727477 42158689 1617552130 312026427 342640189 97088268 2049289560 336766062
seed-to-soil map:
1615836342 1401909974 23067952
785532007 269485885 88937774
3019002892 2773729385 10470414
4202163101 2747292152 26437233
3183210415 4217634159 77333137
2847460091 3211730218 136699600
2455891790 3791729773 70553041
3260543552 2581343101 165949051
3840286095 2849853212 361877006
4228600334 2361239030 66366962
1594559581 1077839137 21276761
380069408 165017790 44262617
3598718222 1894384162 241567873
0 1424977926 190757551
1894384162 2810496375 39356837
424332025 606264721 196539291
3521487829 2221977524 77230393
742681934 69797365 36566707
1638904294 1615735477 139190145
1335949488 0 69797365
779248641 802804012 6283366
2638766896 4008940964 208693195
250963029 1142644585 70452661
1933740999 3470280789 321448984
190757551 209280407 60205478
1778094439 1099115898 43528687
2255189983 3348429818 121850971
1000500225 809087378 268751759
1269251984 1754925622 66697504
874469781 358423659 126030444
2526444831 2135952035 86025489
2439072067 3992121241 16819723
3426492603 3897126015 94995226
1405746853 1213097246 188812728
321415690 106364072 58653718
2984159691 3862282814 34843201
2377040954 2299207917 62031113
3029473306 2427605992 153737109
2612470320 2784199799 26296576
620871316 484454103 121810618
soil-to-fertilizer map:
4245401761 2352458099 28057201
2099789767 3998256334 14950546
3446056574 2749719529 135349925
890092371 1379309857 42097049
953714890 896502554 10335567
3115342240 2380515300 218129381
3333471621 3885671381 112584953
663999152 0 226093219
1873325002 727305635 169196919
2042521921 1328150912 51158945
3581406499 4034715214 260252082
500989478 564295961 163009674
4273458962 4013206880 21508334
3992733429 2099789767 164092891
4156826320 2263882658 88575441
3841658581 2598644681 151074848
1094703999 226093219 21270249
1708571521 399542480 164753481
964050457 247363468 130653542
1236290130 1421406906 171284482
0 1592691388 500989478
932189420 378017010 21525470
2114740313 2885069454 1000601927
1115974248 1207835030 120315882
1407574612 906838121 300996909
fertilizer-to-water map:
3217858280 3761663130 355893932
2319366035 2401839275 72374872
1962726423 909927230 105011330
2115307878 441322644 204058157
2095064202 1824085445 20243676
110580631 329763915 34129824
2573484127 2701101998 225220022
1780224111 2342656863 59182412
1717605398 1571533532 62618713
3589165621 3062909078 75538793
842280446 1871096488 471560375
409726333 0 243114563
397401582 760576019 12324751
0 667815878 92760141
3990305711 2926322020 5981819
251664023 1741105813 32320840
2072629125 645380801 22435077
92760141 363893739 17820490
2798704149 3138447871 156185660
3664704414 2573484127 127617871
1839406523 381714229 59608415
283984863 1844329121 26767367
1313840821 1176023584 27579684
2391740907 1790738543 33346902
3929650615 4234312200 60655096
3996287530 3557602002 204061128
1402088224 772900770 137026460
1911435456 2474214147 51290967
1539114684 1393042818 178490714
3573752212 4117557062 15413409
1341420505 1014938560 60667719
3792322285 4132970471 6723091
4200348658 4139693562 80082634
652840896 1203603268 189439550
310752230 243114563 86649352
2425087809 1075606279 100417305
1899014938 1773426653 12420518
2067737753 1785847171 4891372
3799045376 2932303839 130605239
2954889809 3294633531 262968471
4280431292 4219776196 14536004
144710455 1634152245 106953568
water-to-light map:
1071107509 759231097 26724064
1293599454 642189614 64567949
3147690498 1633749175 71376364
3487999223 4080968704 155844998
1700873635 2097781236 292450760
1146121952 1950303734 147477502
2864027062 1470573702 75076585
2507471274 3021419800 175807670
2939103647 922829268 123372939
2826742681 2551285626 37284381
719212681 1821763210 100196810
4236813702 4292227071 2740225
3643844221 3837685117 243283587
209103905 785955161 52302440
100332146 752700417 6530680
1478192138 3229160565 10342236
1358167403 3239502801 94242072
3219066862 706757563 45942854
3062476586 1358475140 29661777
819409491 2390231996 26561329
4195136152 3511184798 41677550
1488534374 1046202207 35444248
3887127808 3552862348 284822769
2188804057 1705125539 116637671
0 309410320 100332146
1117778238 1921960020 28343714
261406345 3333744873 74978759
2100705169 1545650287 88098888
3450098653 3473284228 37900570
2742067948 1273800407 84674733
2305441728 107380774 202029546
3265009716 1130086491 143713916
1993324395 0 107380774
1523978622 465294601 176895013
106862826 2588570007 102241079
2683278944 864040264 58789004
4239553927 4236813702 55413369
1097831573 1081646455 19946665
524293914 1101593120 28493371
336385104 2833510990 187908810
552787285 3197227470 31933095
845970820 2690811086 142699904
988670724 1388136917 82436785
1452409475 838257601 25782663
3092138363 409742466 55552135
584720380 2416793325 134492301
4171950577 3450098653 23185575
light-to-temperature map:
2906633798 3843376160 451591136
1332454428 1190958320 69004583
1837712164 0 353313230
494809338 353313230 376619264
871428602 729932494 461025826
1401459011 1754772241 373416033
3976747173 3375456648 91164221
3495346659 3466620869 376755291
0 1259962903 494809338
2608238358 2459541635 298395440
3907558614 2984992977 69188559
3872101950 2286963246 35456664
4067911394 2757937075 227055902
3358224934 2322419910 137121725
2286963246 3054181536 321275112
1774875044 2128188274 62837120
temperature-to-humidity map:
3966168141 3406025946 214996780
4181164921 3292223571 113802375
1493139015 1471031672 367564898
1423475871 1838596570 69663144
0 479293006 226560784
2500785470 2859072453 433151118
3197453551 2500785470 96923792
758446483 1237739489 233292183
991738666 0 278789291
3555740534 3884539689 410427607
3294377343 2597709262 261363191
226560784 705853790 531885699
1860703913 305742584 20602508
2933936588 3621022726 263516963
1881306421 278789291 26953293
1270527957 326345092 152947914
humidity-to-location map:
848612454 2250862530 61410922
910023376 3689675651 35197452
3724873103 3865027106 240221283
483883727 3324946924 364728727
0 1766978803 483883727
1957894300 561533 922927950
945220828 2590144784 734802140
2880822250 0 561533
3447014853 1489120553 277858250
2881383783 923489483 565631070
3965094386 3724873103 140154003
1680022968 2312273452 277871332

33
input/day5-eg Normal file
View file

@ -0,0 +1,33 @@
seeds: 79 14 55 13
seed-to-soil map:
50 98 2
52 50 48
soil-to-fertilizer map:
0 15 37
37 52 2
39 0 15
fertilizer-to-water map:
49 53 8
0 11 42
42 0 7
57 7 4
water-to-light map:
88 18 7
18 25 70
light-to-temperature map:
45 77 23
81 45 19
68 64 13
temperature-to-humidity map:
0 69 1
1 0 69
humidity-to-location map:
60 56 37
56 93 4