previous: list[range] = [] current: list[range] = [] def finishmap(): global previous global current print(current, previous) current += previous previous = current current = [] previous = mergeRanges(previous) def mergeRanges(ranges: list[range]) -> list[range]: ranges.sort(key=lambda x: x.start) newRanges: list[range] = [] for i in ranges: if len(newRanges) == 0: newRanges.append(i) continue if not overlap(newRanges[-1], i): newRanges.append(i) continue newRanges[-1] = range(min(newRanges[-1].start, i.start), max(newRanges[-1].stop, i.stop)) newRanges.append(i) return newRanges def overlap(range1: range, range2: range): if range1.start >= range2.stop or range1.stop <= range2.start: return False return True def getOverlapRange(range1: range, range2: range) -> tuple[range|None,range,range|None]: if not overlap(range1, range2): raise Exception("Ranges do not overlap") return ( range(range1.start, range2.start) if range1.start < range2.start else None, range(max(range1.start, range2.start), min(range1.stop, range2.stop)), range(range2.stop, range1.stop) if range1.stop > range2.stop else None ) def splitRange(range1: range, at: int) -> tuple[range,range]: if not at in range1: raise Exception("at not in range") return (range(range1.start, at), range(at, range1.stop)) for line in open("input2"): if line.startswith("seeds:"): split = line.split()[1:] previous = [range(int(x), int(x) + int(y)) for x, y in zip(split[::2], split[1::2])] continue if line.endswith("map:\n"): print(line[:-5]) finishmap() continue if len(line.split()) < 3: continue dest, source, length = line.split() dest = int(dest) source = int(source) offset = dest - source length = int(length) sourceRange = range(source, source + length) maxloops = 10 toRemove: list[range] = [] newPrevious: list[range] = [] for i in previous: if not overlap(i, sourceRange): continue toRemove.append(i) if i.start == sourceRange.start and i.stop == sourceRange.stop: current.append(range(dest, dest + length)) continue nonoverlap1, overlapping, nonoverlap2 = getOverlapRange(i, sourceRange) if nonoverlap1 is not None and nonoverlap1.start < nonoverlap1.stop: newPrevious.append(nonoverlap1) current.append(range(overlapping.start + offset, overlapping.stop + offset)) if nonoverlap2 is not None and nonoverlap2.start < nonoverlap2.stop: newPrevious.append(nonoverlap2) for i in toRemove: previous.remove(i) previous += newPrevious finishmap() print(min(prev.start for prev in previous))