Day 5

Today we continue the pattern of easy then hard question. It took me a while to track down a bug in part 1 of my code where I created 7 aliases for a dictionary instead of creating 7 separate dictionaries.

For part two, I considered implementing an algorithm faster than brute force. However, it took a while to get the range mapRangesToValues working as intended. The tricky part was considering all the ways the algorithm should start, terminate, and increment.

Part 1

def addLineToMap(line: list[int], map: dict) -> None:
    offset = line[0] - line[1]
    domainSize = line[2]
    # values in [line[1], line[1] + domainSize) get moved by offset
    map[(line[1], line[1] + domainSize)] = offset


def mapToNewValues(values: list[int], map: dict) -> list[int]:
    for idx, value in enumerate(values):
        for domain, offSet in map.items():
            end = domain[1]
            start = domain[0]
            if start <= value < end:
                values[idx] = value + offSet
                break
    return values


if __name__ == "__main__":
    filename = "input.txt"

    with open(filename, encoding="utf-8") as f:
        # note [{}] * 7 creates 7 reference to the same object
        allMaps = [{} for _ in range(7)]
        mapNum = 0
        flag = False
        domain = []
        for lineNum, line in enumerate(f):
            if lineNum == 0:
                domain = [int(seed) for seed in line[7:].split()]
            elif flag:
                if not line[0].isdigit():
                    mapNum += 1
                    flag = False
                else:
                    map = allMaps[mapNum]
                    line = [int(num) for num in line.split()]
                    addLineToMap(line, map)
                continue
            else:
                if not line[0].isdigit():
                    continue
                else:
                    flag = True
                    map = allMaps[mapNum]
                    line = [int(num) for num in line.split()]
                    addLineToMap(line, map)
        for map in allMaps:
            domain = mapToNewValues(domain, map)
        print(min(domain))

Part 2

def addLineToMap(line: list[int], map: list) -> None:
    offset = line[0] - line[1]
    domainSize = line[2]
    # values in [line[1], line[1] + domainSize) get moved by offset
    map.append((line[1], line[1] + domainSize, offset))


def mapRangesToValues(ranges: list[(int, int)], map: dict) -> list[int]:
    newRanges = []
    i = 0
    j = 0
    start1, end1 = ranges[0]
    start2, end2, offset = map[0]
    if start2 > start1:
        newRanges.append((start1, start2))

    while i < len(ranges) and j < len(map):
        # Check for none empty intersection
        start = max(start1, start2)
        end = min(end1, end2)
        if start < end:
            newRanges.append((start + offset, end + offset))
        # Move to the next interval in the list that has a smaller end value
        if end1 < end2:
            i += 1
            if i >= len(ranges):
                break
            start1, end1 = ranges[i]
        elif end1 >= end2:
            j += 1
            if j >= len(map):
                if end2 != end1 and end2 >= start1:
                    newRanges.append((end2, end1))
                    i += 1
                elif end2 == end1:
                    i += 1
                break
            start2, end2, offset = map[j]
            if start2 >= end1:
                newRanges.append((start1, end1))
    while i < len(ranges):
        newRanges.append(ranges[i])
        i += 1

    newRanges.sort(key=lambda interval: interval[0])

    return newRanges


def seedsToRange(values: list[int]) -> list[(int, int)]:
    i = 0
    returned = []
    while (i * 2) + 1 < len(values):
        start = values[i * 2]
        ammount = values[(i * 2) + 1]
        returned.append((start, start + ammount))
        i += 1
    return returned


if __name__ == "__main__":
    filename = "input.txt"

    with open(filename, encoding="utf-8") as f:
        # note [{}] * 7 creates 7 reference to the same object
        allMaps = [[] for _ in range(7)]
        mapNum = 0
        flag = False
        domain = []
        for lineNum, line in enumerate(f):
            if lineNum == 0:
                domain = seedsToRange([int(seed) for seed in line[7:].split()])
                domain = sorted(domain, key=lambda interval: interval[0])
            elif flag:
                if not line[0].isdigit():
                    mapNum += 1
                    flag = False
                else:
                    map = allMaps[mapNum]
                    line = [int(num) for num in line.split()]
                    addLineToMap(line, map)
                continue
            else:
                if not line[0].isdigit():
                    continue
                else:
                    flag = True
                    map = allMaps[mapNum]
                    line = [int(num) for num in line.split()]
                    addLineToMap(line, map)
        for map in allMaps:
            map.sort(key=lambda entry: entry[0])
        for map in allMaps:
            domain = mapRangesToValues(domain, map)
        print(domain[0][0])