#!/usr/bin/env python # https://adventofcode.com/2025/day/5 f = open("day05input.txt", "r") #f = open("testinput.txt", "r") d = [l.strip() for l in f.readlines()] # prepare data if "" in d: i = d.index("") Ranges = [[int(r.split("-")[0]), int(r.split("-")[1])] for r in d[:i]] IDs = list(map(int, d[i+1:])) else: Ranges = [[int(r.split("-")[0]), int(r.split("-")[1])] for r in d] def part1(ranges: list[list[int, int]], ids: list[int]) -> int: for r in ranges: assert int(r[0]) <= int(r[1]) total = 0 # loop over IDs for _id in ids: id_counter = 0 # loop over ranges for r in ranges: if r[0] <= _id <= r[1]: total += 1 break return total def part2(ranges: list[list[int, int]]) -> int: for r in ranges: assert int(r[0]) <= int(r[1]) distinct_ranges = [] # integrates current range with current list of distinct ranges # returns true if a change was made to distinct_ranges def compare(c_range: list[int, int]) -> bool: r_min, r_max = c_range # checks subcase def subcase(): # subcase a: it's the last distinct range or the range ends before the next distinct range starts if i == len(distinct_ranges) - 1 or r_max < distinct_ranges[i + 1][0]: d_range[1] = r_max # subcase b: range ends inside next distinct range elif distinct_ranges[i + 1][0] <= r_max <= distinct_ranges[i + 1][1]: d_range[1] = distinct_ranges[i + 1][1] distinct_ranges.pop(i + 1) # subcase c: range stretches beyond the end of the next distinct range # I'll ignore this case b/c I think it doesn't occur else: raise Exception("Shit, I missed a subcase!") # loop over distinct ranges for i in range(len(distinct_ranges)): d_range = distinct_ranges[i] # case 1: range sits completely outside current distinct range if r_max < d_range[0] or d_range[1] < r_min: continue # case 2: range sits inside the current distinct range elif d_range[0] <= r_min <= r_max <= d_range[1]: return False # case 3: range spans from outside into current distant range, but not beyond elif r_min < d_range[0] <= r_max <= d_range[1]: d_range[0] = r_min return True # case 4: range spans from inside the current distance range beyond its end elif d_range[0] <= r_min <= d_range[1] < r_max: subcase() return True # case 5: range completely envelops current distant range elif r_min < d_range[0] < d_range[1] < r_max: d_range[0] = r_min subcase() return True # case 6: I missed a case else: raise Exception("Shit, I missed a case!") distinct_ranges.append(c_range) distinct_ranges.sort(key=lambda a: a[0]) return True # compute ranges with no overlaps for r in ranges: # print(distinct_ranges) # no distinct ranges yet? if len(distinct_ranges) == 0: distinct_ranges.append(r) continue # compare new range to all distinct ranges computed so far # repeat until no changes happened while True: has_changed = compare(r) # make sure distinct ranges never overlap assert all([distinct_ranges[j+1][0] - distinct_ranges[j][1] > 0 for j in range (len(distinct_ranges) - 1)]) if not has_changed: break return sum([r[1] - r[0] + 1 for r in distinct_ranges]) #print(part1(Ranges, IDs)) print(part2(Ranges))