130 lines
3.4 KiB
Python
130 lines
3.4 KiB
Python
#!/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))
|