From 896031f9fe84e847e4ce993af88c2ea2c0766cf0 Mon Sep 17 00:00:00 2001 From: Jacob Jonsson Date: Fri, 5 Dec 2025 21:51:04 +0100 Subject: [PATCH] feat: solve day 5, part 2 Keeping the old solution for the ranges was too ineffective when it came to enumerating all contained integers, the amount of elements was too large to either expand the ranges into Sets or similar. However, it turned out (after some thinking) to be fairly straight-forward to check if consecutive ranges overlapped, and in that case merge them. The solution therefore now parses the ranges, sorts them on the first component and then merges them as far as possible. changes: JJ: M app/Day5.hs --- app/Day5.hs | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/app/Day5.hs b/app/Day5.hs index 3368606..5defbd9 100644 --- a/app/Day5.hs +++ b/app/Day5.hs @@ -1,5 +1,8 @@ module Main where +import Control.Arrow ((***)) +import Data.Ix (inRange) +import Data.List (foldl', sort) import System.Environment (getArgs) main :: IO () @@ -14,19 +17,37 @@ part1 = length . uncurry (filter . isFresh) . parse isFresh :: [(Int, Int)] -> Int -> Bool isFresh fresh ing = any (contained ing) fresh - contained :: Int -> (Int, Int) -> Bool - contained a (x, y) = x <= a && a <= y +contained :: Int -> (Int, Int) -> Bool +contained = flip inRange +-- Splits input on empty line and parses the first half with genRanges +-- and the second half into ints with read. parse :: String -> ([(Int, Int)], [Int]) -parse = (\(ranges, ingredients) -> ((map parseRange ranges), map read (drop 1 ingredients))) . span (/= "") . lines +parse = (genRanges *** (map read . drop 1)) . span (/= "") . lines where + genRanges :: [String] -> [(Int, Int)] + genRanges = foldl' (mergeRanges) mempty . sort . map parseRange + + -- Parse "X-Y" -> (X, Y) parseRange :: String -> (Int, Int) parseRange s = case span (/= '-') s of (start, '-' : end) -> (read start, read end) _ -> error $ "parseRange: expected a string on the form \"NUM-NUM\", got \"" <> s <> "\"" + -- Generate ranges by combining adjacent ranges if they overlap. + -- + -- Ignores ranges that are fully enclosed by previous range. + -- N.B. Assumes ranges are sorted on first component. + mergeRanges :: [(Int, Int)] -> (Int, Int) -> [(Int, Int)] + mergeRanges [] i = [i] + mergeRanges fresh@((a, b) : rest) (x, y) + | x `contained` (a, b) && y > b = (a, y) : rest + | x < a && y `contained` (a, b) = (x, b) : rest + | x `contained` (a, b) && y `contained` (a, b) = fresh + | otherwise = (x, y) : fresh + part2 :: String -> Int -part2 = error "Not implemented" +part2 = sum . map ((+ 1) . uncurry (flip (-))) . fst . parse testInput :: String testInput =