コード例 #1
0
ファイル: day23.py プロジェクト: leiver/aoc_2020_python
def day23():
    cup_circle = [4, 8, 7, 9, 1, 2, 3, 6, 5]
    index_current_cup = 0
    current_cup = cup_circle[index_current_cup]
    for _ in range(100):
        (cup_circle, index_current_cup,
         current_cup) = cup_round(cup_circle, index_current_cup, current_cup)

    index_cup_1 = cup_circle.index(1)
    solution_part_1 = ""
    for i in range(1, len(cup_circle)):
        solution_part_1 += str(cup_circle[(index_cup_1 + i) % len(cup_circle)])

    print("Solution part1:", solution_part_1)

    timing.log("Part 1 finished!")

    cup_circle = [4, 8, 7, 9, 1, 2, 3, 6, 5]
    cup_circle.extend(range(len(cup_circle) + 1, 1000001))
    (current_cup, cup_map) = create_linked_list_and_map(cup_circle)
    for _ in range(10000000):
        current_cup = run_cup_round(current_cup, cup_map)

    solution_part_2 = cup_map[1].next_cup.cup * cup_map[1].next_cup.next_cup.cup

    print("Solution part2:", solution_part_2)
コード例 #2
0
def day8():
    instructions = []
    with open(os.path.join(sys.path[0], "inputs/input_day8.txt"), "r") as file:
        for line in file:
            (command, number) = line.split(" ")
            instructions.append((command, number))

    (solution_part1,
     initial_loop_of_instructions) = find_loop_or_end(instructions)

    print("Solution part1: " + str(solution_part1))

    timing.log("Part 1 finished!")

    for (changed_instruction, (command,
                               number)) in initial_loop_of_instructions:
        if command in ["jmp", "nop"]:
            if command == "jmp":
                instructions[changed_instruction] = (
                    "nop", instructions[changed_instruction][1])
            else:
                instructions[changed_instruction] = (
                    "jmp", instructions[changed_instruction][1])

            (acc, loop_if_any) = find_loop_or_end(instructions)

            if not loop_if_any:
                print("Solution part2: " + str(acc))
                break

            instructions[changed_instruction] = (
                command, instructions[changed_instruction][1])
コード例 #3
0
ファイル: day15.py プロジェクト: leiver/aoc_2020_python
def day15():
    numbers = {6: 0, 13: 1, 1: 2, 15: 3, 2: 4, 0: 5}
    last_number = 0
    for index in range(len(numbers), 2019):
        if last_number in numbers:
            last_number_index = numbers.get(last_number)
            numbers[last_number] = index
            last_number = index - last_number_index
        else:
            numbers[last_number] = index
            last_number = 0

    print("Solution part1: ", last_number)

    timing.log("Part 1 finished!")

    numbers = {6: 0, 13: 1, 1: 2, 15: 3, 2: 4, 0: 5}
    last_number = 0
    for index in range(len(numbers), 29999999):
        if last_number in numbers:
            last_number_index = numbers.get(last_number)
            numbers[last_number] = index
            last_number = index - last_number_index
        else:
            numbers[last_number] = index
            last_number = 0

    print("Solution part2: ", last_number)
コード例 #4
0
ファイル: day7.py プロジェクト: leiver/aoc_2020_python
def day7():
    bag_contents = {}
    containers_for_bag = {}
    with open(os.path.join(sys.path[0], "inputs/input_day7.txt"), "r") as file:
        for line in file:
            (bag, contents) = line.rstrip().strip(".").split(" bags contain ")
            contents = [
                (
                    value.split(" ")[0],
                    value[len(value.split(" ")[0]) + 1::].replace(" bags", "").replace(" bag", "")
                ) for value in contents.split(", ")
            ]
            bag_contents[bag] = contents
            for (amount, container) in contents:
                if "no" != amount:
                    if container in containers_for_bag:
                        if bag not in containers_for_bag[container]:
                            containers_for_bag[container].append(bag)
                    else:
                        containers_for_bag[container] = [bag]

    print("Solution part1: " + str(len(bags_that_can_contain_given_bag("shiny gold", containers_for_bag))))

    timing.log("Part 1 finished!")

    print("Solution part2: " + str(bags_inside_given_bag("shiny gold", bag_contents)))
コード例 #5
0
def day22():
    with open(os.path.join(sys.path[0], "inputs/input_day22.txt"), "r") as file:
        (initial_deck_player_1, initial_deck_player_2) = file.read().rstrip().split("\n\n")
        initial_deck_player_1 = deque([int(card) for card in initial_deck_player_1.rstrip().split("\n")[1:]])
        initial_deck_player_2 = deque([int(card) for card in initial_deck_player_2.rstrip().split("\n")[1:]])

    print("Solution part1:", calculate_score(play_regular_game(initial_deck_player_1.copy(), initial_deck_player_2.copy())))

    timing.log("Part 1 finished!")

    print("Solution part2:", calculate_score(play_recursive_game(initial_deck_player_1.copy(), initial_deck_player_2.copy())[1]))
コード例 #6
0
ファイル: day19.py プロジェクト: leiver/aoc_2020_python
def day19():
    rule_map = {}

    with open(os.path.join(sys.path[0], "inputs/input_day19.txt"),
              "r") as file:
        (rules, messages) = file.read().split("\n\n")
        for rule in rules.rstrip().split("\n"):
            (rule_id, sub_rules) = rule.rstrip().split(": ")
            rule_map[int(rule_id)] = [[
                sub_rule_element.strip("\"")
                for sub_rule_element in sub_rule.split(" ")
            ] for sub_rule in sub_rules.split(" | ")]

        valid_messages = 0
        for message in messages.rstrip().split("\n"):
            message = message.rstrip()
            valid_to_index = is_input_valid(message, rule_map, 0, 0)
            if valid_to_index == (len(message)):
                valid_messages += 1
        print("Solution part1: ", valid_messages)

        timing.log("Part 1 finished!")

        valid_messages = 0

        for message in messages.rstrip().split("\n"):
            message = message.rstrip()
            forty_two_looped = 0
            forty_two_success = True
            valid_to_index = 0
            while forty_two_success:
                new_index = is_input_valid(message, rule_map, valid_to_index,
                                           42)
                if new_index:
                    valid_to_index = new_index
                    forty_two_looped += 1
                else:
                    forty_two_success = False
            thirty_one_looped = 0
            thirty_one_success = True
            while thirty_one_success:
                new_index = is_input_valid(message, rule_map, valid_to_index,
                                           31)
                if new_index:
                    valid_to_index = new_index
                    thirty_one_looped += 1
                else:
                    thirty_one_success = False

            if forty_two_looped > thirty_one_looped > 0 and valid_to_index == len(
                    message):
                valid_messages += 1

        print("Solution part2: ", valid_messages)
コード例 #7
0
ファイル: day9.py プロジェクト: leiver/aoc_2020_python
def day9():
    numbers_parsed = []

    with open(os.path.join(sys.path[0], "inputs/input_day9.txt"), "r") as file:
        for line in file:
            number_to_parse = int(line.rstrip())

            if len(numbers_parsed) >= 25:
                found_match = False
                for first_number in range(1, 25):
                    for second_number in range(first_number + 1, 26):
                        if numbers_parsed[
                                len(numbers_parsed) -
                                first_number] + numbers_parsed[
                                    len(numbers_parsed) -
                                    second_number] == number_to_parse:
                            found_match = True
                            break
                    if found_match:
                        break
                if not found_match:
                    invalid_number = number_to_parse
                    break

            numbers_parsed.append(number_to_parse)

    print("Solution part1: " + str(invalid_number))

    timing.log("Part 1 finished!")

    start_range_index = 0
    current_sum = numbers_parsed[0]
    for end_range_index in range(1, len(numbers_parsed)):
        new_number = numbers_parsed[end_range_index]
        new_sum = current_sum + new_number
        while new_sum > invalid_number:
            old_number = numbers_parsed[start_range_index]
            new_sum -= old_number
            start_range_index += 1
        if new_sum == invalid_number:
            list_of_numbers = numbers_parsed[
                start_range_index:end_range_index + 1]
            smallest_number = min(list_of_numbers)
            largest_number = max(list_of_numbers)
            result = smallest_number + largest_number
            break
        current_sum = new_sum

    print("Solution part2: " + str(result))
コード例 #8
0
ファイル: day24.py プロジェクト: leiver/aoc_2020_python
def day24():
    floor_map = set()
    paths = []
    with open(os.path.join(sys.path[0], "inputs/input_day24.txt"),
              "r") as file:
        for path in file:
            instructions = []
            x = y = 0
            path = path.rstrip()
            path_iter = iter(path)
            instruction = next(path_iter, None)
            while instruction:
                if instruction in ["n", "s"]:
                    instruction += next(path_iter)

                instructions.append(instruction)

                (x, y) = coords_from_instruction(x, y, instruction)

                instruction = next(path_iter, None)

            if (x, y) in floor_map:
                floor_map.remove((x, y))
            else:
                floor_map.add((x, y))

            paths.append(instructions)

    print("Solution part1:", len(floor_map))

    timing.log("Part 1 finished!")

    for _ in range(100):
        new_floor = set()
        white_tiles_parsed = set()
        for (x, y) in floor_map:
            (black_tile_count, neighbours) = get_neighbours(x, y, floor_map)
            if 0 < black_tile_count <= 2:
                new_floor.add((x, y))
            for neighbour in neighbours:
                if neighbour not in floor_map and neighbour not in white_tiles_parsed:
                    (black_tile_count,
                     _) = get_neighbours(neighbour[0], neighbour[1], floor_map)
                    if black_tile_count == 2:
                        new_floor.add(neighbour)
                    white_tiles_parsed.add(neighbour)
        floor_map = new_floor

    print("Solution part2:", len(floor_map))
コード例 #9
0
ファイル: day13.py プロジェクト: leiver/aoc_2020_python
def day13():
    bus_schedules = []
    bus_ids = []
    bus_indexes = {}
    with open(os.path.join(sys.path[0], "inputs/input_day13.txt"),
              "r") as file:
        lines = file.readlines()
        earliest_timestamp = int(lines[0].rstrip())
        closest_schedule = 0
        smallest_waiting_time = 0
        index = 0
        for bus_schedule in lines[1].rstrip().split(","):
            if bus_schedule != "x":
                bus_schedule = int(bus_schedule)
                bus_schedules.append((index, bus_schedule))
                bus_ids.append(bus_schedule)
                bus_indexes[bus_schedule] = bus_schedule - index

                closest_timestamp = find_closest_timestamp_above_given_timestamp(
                    bus_schedule, earliest_timestamp)
                waiting_time = closest_timestamp - earliest_timestamp
                if closest_schedule == 0 or waiting_time < smallest_waiting_time:
                    smallest_waiting_time = waiting_time
                    closest_schedule = bus_schedule
            index += 1

    print("Solution part1: " + str(closest_schedule * smallest_waiting_time))

    timing.log("Part 1 finished!")

    bus_ids.sort(reverse=True)

    current_value = bus_indexes[bus_ids[0]]
    increment = bus_ids[0]
    for index in range(1, len(bus_ids)):
        while current_value % bus_ids[index] != bus_indexes[
                bus_ids[index]] % bus_ids[index]:
            current_value += increment
        increment *= bus_ids[index]
    print("Solution part2: ", current_value)
コード例 #10
0
def day14():
    memory = {}
    memory_part_2 = {}
    mask = ""
    with open(os.path.join(sys.path[0], "inputs/input_day14.txt"),
              "r") as file:
        for line in file:
            (command, value) = line.rstrip().split(" = ")
            if command == "mask":
                mask = value
            elif "mem" in command:
                mem_index = int(command[command.index("[") +
                                        1:command.index("]")])
                mem_index_binary = '{0:036b}'.format(mem_index)
                mem_value = '{0:036b}'.format(int(value))

                fixed_mem_value = ""
                for mem_value_index in range(len(mem_value)):
                    if mask[mem_value_index] != "X":
                        fixed_mem_value += mask[mem_value_index]
                    else:
                        fixed_mem_value += mem_value[mem_value_index]
                memory[mem_index] = fixed_mem_value

                write_to_address(mem_index_binary, mask, 0, memory_part_2,
                                 int(value))

    solution_part_1 = 0
    for value in memory.values():
        solution_part_1 += int(value, base=2)

    print("Solution part1: ", solution_part_1)

    timing.log("Part 1 finished!")

    solution_part_2 = get_all_values_from_memory(memory_part_2)

    print("Solution part2: ", solution_part_2)
コード例 #11
0
def day11():
    seats = []
    with open(os.path.join(sys.path[0], "inputs/input_day11.txt"),
              "r") as file:
        for line in file:
            row_values = []
            row = line.rstrip()
            row_length = len(row)
            for seat in row:
                row_values.append(seat)
            seats.append(row_values)

    old_seat_map = copy.deepcopy(seats)
    new_seat_map = copy.deepcopy(seats)
    seat_changed_status = True
    while seat_changed_status:
        occupied_seats = 0
        seat_changed_status = False
        for row in range(len(old_seat_map)):
            for seat in range(row_length):
                if old_seat_map[row][seat] != '.':
                    adjacent_occupied = count_adjacent_occupied((seat, row),
                                                                old_seat_map)
                    if old_seat_map[row][seat] == '#' and adjacent_occupied > 3:
                        new_seat_map[row][seat] = "L"
                        seat_changed_status = True
                    elif old_seat_map[row][
                            seat] == 'L' and adjacent_occupied == 0:
                        new_seat_map[row][seat] = '#'
                        seat_changed_status = True
                    else:
                        new_seat_map[row][seat] = old_seat_map[row][seat]
                    if new_seat_map[row][seat] == '#':
                        occupied_seats += 1
        old_seat_map = copy.deepcopy(new_seat_map)

    print("Solution part1: " + str(occupied_seats))

    timing.log("Part 1 finished!")

    old_seat_map = copy.deepcopy(seats)
    new_seat_map = copy.deepcopy(seats)
    seat_changed_status = True
    while seat_changed_status:
        occupied_seats = 0
        seat_changed_status = False
        for row in range(len(old_seat_map)):
            for seat in range(row_length):
                if old_seat_map[row][seat] != '.':
                    adjacent_occupied = count_nearest_occupied((seat, row),
                                                               old_seat_map)
                    if old_seat_map[row][seat] == '#' and adjacent_occupied > 4:
                        new_seat_map[row][seat] = "L"
                        seat_changed_status = True
                    elif old_seat_map[row][
                            seat] == 'L' and adjacent_occupied == 0:
                        new_seat_map[row][seat] = '#'
                        seat_changed_status = True
                    else:
                        new_seat_map[row][seat] = old_seat_map[row][seat]
                    if new_seat_map[row][seat] == '#':
                        occupied_seats += 1
        old_seat_map = copy.deepcopy(new_seat_map)

    print("Solution part2: " + str(occupied_seats))
コード例 #12
0
from assignments import day14
from assignments import day15
from assignments import day16
from assignments import day17
from assignments import day18
from assignments import day19
from assignments import day20
from assignments import day21
from assignments import day22
from assignments import day23
from assignments import day24
from assignments import day25

day1.day1()

timing.log("Day 1 finished!")
timing.log("Starting day 2!")

day2.day2()

timing.log("Day 2 finished!")
timing.log("Starting day 3!")

day3.day3()

timing.log("Day 3 finished!")
timing.log("Starting day 4!")

day4.day4()

timing.log("Day 4 finished!")
コード例 #13
0
def day17():
    dimension_map = {}
    four_dimension_map = {}

    with open(os.path.join(sys.path[0], "inputs/input_day17.txt"),
              "r") as file:
        y = 0
        for line in file:
            x = 0
            for box in line.rstrip():
                if box == "#":
                    dimension_map[(x, y, 0)] = "#"
                    four_dimension_map[(x, y, 0, 0)] = "#"
                x += 1
            y += 1

    for _ in range(6):
        next_iteration = {}
        memo = set()
        for (coordinates, box) in dimension_map.items():
            (boxes, neighbours) = get_neighbours(dimension_map, coordinates)
            new_box = parse_box_value(box, boxes)
            if new_box == "#":
                next_iteration[coordinates] = new_box
            memo.add(coordinates)

            for neighbour in neighbours:
                if neighbour not in memo:
                    (boxes, _) = get_neighbours(dimension_map, neighbour)
                    neighbour_box = "."
                    if neighbour in dimension_map:
                        neighbour_box = dimension_map[neighbour]
                    new_neighbour_box = parse_box_value(neighbour_box, boxes)
                    if new_neighbour_box == "#":
                        next_iteration[neighbour] = new_neighbour_box
                    memo.add(neighbour)
        dimension_map = copy.deepcopy(next_iteration)

    print("Solution part1: ", len(dimension_map))

    timing.log("Part 1 finished!")

    for _ in range(6):
        next_iteration = {}
        memo = set()
        for (coordinates, box) in four_dimension_map.items():
            (boxes, neighbours) = get_4d_neighbours(four_dimension_map,
                                                    coordinates)
            new_box = parse_box_value(box, boxes)
            if new_box == "#":
                next_iteration[coordinates] = new_box
            memo.add(coordinates)

            for neighbour in neighbours:
                if neighbour not in memo:
                    (boxes, _) = get_4d_neighbours(four_dimension_map,
                                                   neighbour)
                    neighbour_box = "."
                    if neighbour in four_dimension_map:
                        neighbour_box = four_dimension_map[neighbour]
                    new_neighbour_box = parse_box_value(neighbour_box, boxes)
                    if new_neighbour_box == "#":
                        next_iteration[neighbour] = new_neighbour_box
                    memo.add(neighbour)
        four_dimension_map = copy.deepcopy(next_iteration)

    print("Solution part2: ", len(four_dimension_map))
コード例 #14
0
def day16():

    rule_map = {}
    range_list = []

    invalid_fields = []
    valid_tickets = []
    with open(os.path.join(sys.path[0], "inputs/input_day16.txt"),
              "r") as file:
        (rules, my_ticket, tickets) = file.read().split("\n\n")
        for rule in rules.rstrip().split("\n"):
            (command, ranges) = rule.rstrip().split(": ")
            rule_map[command] = [
                tuple([int(bound) for bound in range_rule.split("-")])
                for range_rule in ranges.split(" or ")
            ]
            range_list.extend(rule_map[command])

        my_ticket = [
            int(value)
            for value in my_ticket.rstrip().split("\n")[1].rstrip().split(",")
        ]
        valid_tickets.append(my_ticket)

        for ticket in tickets.rstrip().split("\n")[1:]:
            valid_ticket = True
            for value in ticket.rstrip().split(","):
                value = int(value)
                invalid = True
                for (lower_end, upper_end) in range_list:
                    if lower_end <= value <= upper_end:
                        invalid = False
                        break
                if invalid:
                    invalid_fields.append(value)
                    valid_ticket = False
            if valid_ticket:
                valid_tickets.append(
                    [int(value) for value in ticket.rstrip().split(",")])

    print("Solution part1: ", sum(invalid_fields))

    timing.log("Part 1 finished!")

    valid_index_for_rule = {}
    not_valid_index_for_rule = {}
    for ticket in valid_tickets:
        for ticket_index in range(len(ticket)):
            ticket_value = ticket[ticket_index]
            for field_name in rule_map.keys():
                valid_for_field = False
                for (lower_end, upper_end) in rule_map[field_name]:
                    if lower_end <= ticket_value <= upper_end:
                        valid_for_field = True
                if valid_for_field and (
                        field_name not in not_valid_index_for_rule
                        or ticket_index
                        not in not_valid_index_for_rule[field_name]):
                    if field_name in valid_index_for_rule:
                        valid_index_for_rule[field_name].add(ticket_index)
                    else:
                        valid_index_for_rule[field_name] = {ticket_index}
                elif not valid_for_field:
                    if field_name in valid_index_for_rule and ticket_index in valid_index_for_rule[
                            field_name]:
                        valid_index_for_rule[field_name].remove(ticket_index)
                    if field_name in not_valid_index_for_rule:
                        not_valid_index_for_rule[field_name].add(ticket_index)
                    else:
                        not_valid_index_for_rule[field_name] = {ticket_index}

    valid_index_for_rule_sorted_list = list(valid_index_for_rule.items())
    valid_index_for_rule_sorted_list.sort(key=sorting)

    departure_fields = []
    solution_part_2 = 1
    known_indexes = []
    while len(departure_fields) < 6:
        for (field_name, indexes) in valid_index_for_rule_sorted_list:
            filtered_indexes = [
                index for index in indexes if index not in known_indexes
            ]
            if len(filtered_indexes) == 1:
                known_indexes.append(filtered_indexes[0])
                if field_name.startswith("departure"):
                    departure_fields.append(filtered_indexes[0])
                    solution_part_2 *= my_ticket[filtered_indexes[0]]

    print("Solution part2: ", solution_part_2)
コード例 #15
0
def day1():
    part1()

    timing.log("Part 1 finished!")

    part2()
コード例 #16
0
ファイル: day20.py プロジェクト: leiver/aoc_2020_python
def day20():

    images = {}
    image_borders = {}
    images_from_border = {}
    images_from_reverse_border = {}
    with open(os.path.join(sys.path[0], "inputs/input_day20.txt"),
              "r") as file:
        for image in file.read().rstrip().split("\n\n"):
            image_lines = image.rstrip().split("\n")
            image_id = int(image_lines[0][5:].strip(":"))
            image_lines = image_lines[1:]

            north_border_pixels = image_lines[0]
            north_border = parse_binary_from_border(north_border_pixels)
            north_border_reverse = parse_binary_from_border(
                north_border_pixels[::-1])
            add_to_images_from_border_map(images_from_border, north_border,
                                          image_id)
            add_to_images_from_border_map(images_from_reverse_border,
                                          north_border_reverse, image_id)

            east_border_pixels = "".join(
                [pixels[-1] for pixels in image_lines])
            east_border = parse_binary_from_border(east_border_pixels)
            east_border_reverse = parse_binary_from_border(
                east_border_pixels[::-1])
            add_to_images_from_border_map(images_from_border, east_border,
                                          image_id)
            add_to_images_from_border_map(images_from_reverse_border,
                                          east_border_reverse, image_id)

            south_border_pixels = image_lines[-1]
            south_border = parse_binary_from_border(south_border_pixels[::-1])
            south_border_reverse = parse_binary_from_border(
                south_border_pixels)
            add_to_images_from_border_map(images_from_border, south_border,
                                          image_id)
            add_to_images_from_border_map(images_from_reverse_border,
                                          south_border_reverse, image_id)

            west_border_pixels = "".join(
                [pixels[0] for pixels in image_lines[::-1]])
            west_border = parse_binary_from_border(west_border_pixels)
            west_border_reverse = parse_binary_from_border(
                west_border_pixels[::-1])
            add_to_images_from_border_map(images_from_border, west_border,
                                          image_id)
            add_to_images_from_border_map(images_from_reverse_border,
                                          west_border_reverse, image_id)

            pixels = [[pixel for pixel in pixel_row]
                      for pixel_row in image_lines]

            images[image_id] = Picture(image_id,
                                       (north_border, north_border_reverse),
                                       (east_border, east_border_reverse),
                                       (south_border, south_border_reverse),
                                       (west_border, west_border_reverse),
                                       pixels)

            image_borders[image_id] = {
                "N": (north_border, north_border_reverse),
                "E": (east_border, east_border_reverse),
                "S": (south_border, south_border_reverse),
                "W": (west_border, west_border_reverse)
            }

    total_amount_of_images = len(image_borders)

    directions = {"N": 0, "E": 1, "S": 2, "W": 3}
    directions_other_way = {0: "N", 1: "E", 2: "S", 3: "W"}
    direction_x = {"N": 0, "E": 1, "S": 0, "W": -1}
    direction_y = {"N": -1, "E": 0, "S": 1, "W": 0}
    opposite_direction = {"N": "S", "E": "W", "S": "N", "W": "E"}

    open_borders = {}
    full_image = {}
    full_picture_length = int(math.sqrt(len(images)))
    for (image_id, picture) in images.items():
        borders_matched = []
        for i in range(4):
            (border,
             _) = picture.border_from_direction(directions_other_way[i])
            if len(images_from_border[border]
                   ) == 2 or border in images_from_reverse_border:
                borders_matched.append(i)
        if len(borders_matched) == 2:
            rotation = (borders_matched[0] - borders_matched[1] + 4) % 4
            if rotation == 1:
                left_most_border = borders_matched[1]
            elif rotation == 3:
                left_most_border = borders_matched[0]
            picture.rotate((1 - left_most_border + 4) % 4)
            open_borders = {
                (0, 0, "N"): picture.border_from_direction("N"),
                (0, 0, "E"): picture.border_from_direction("E"),
                (0, 0, "S"): picture.border_from_direction("S"),
                (0, 0, "W"): picture.border_from_direction("W")
            }
            full_image = {(0, 0): picture}
            break

    while len(full_image) != total_amount_of_images:
        for (border_id, border) in open_borders.items():
            (x, y, direction) = border_id
            image_to_match_border_x = x + direction_x[direction]
            image_to_match_border_y = y + direction_y[direction]
            matching_images = 0
            if 0 <= image_to_match_border_x < full_picture_length and 0 <= image_to_match_border_y < full_picture_length:
                matching_border_found = False
                for image_to_match in images.values():
                    if image_to_match not in full_image.values():
                        if image_to_match.direction_that_matches(border[0]):
                            all_directions_match = True
                            for direction_matcher in directions.keys():
                                if (
                                        image_to_match_border_x +
                                        direction_x[direction_matcher],
                                        image_to_match_border_y +
                                        direction_y[direction_matcher],
                                        opposite_direction[direction_matcher]
                                ) in open_borders and not image_to_match.direction_that_matches(
                                        open_borders[
                                            (image_to_match_border_x +
                                             direction_x[direction_matcher],
                                             image_to_match_border_y +
                                             direction_y[direction_matcher],
                                             opposite_direction[
                                                 direction_matcher])][0]):
                                    all_directions_match = False
                                    break

                            if all_directions_match:
                                matching_image = image_to_match
                                matching_images += 1
                if matching_images == 1:
                    matching_image.transmute_to_match(direction, border[0])
                    full_image[(image_to_match_border_x,
                                image_to_match_border_y)] = matching_image
                    matching_border_found = True
                    break
        if matching_border_found:
            for direction in directions.keys():
                opposing_border_x = image_to_match_border_x + direction_x[
                    direction]
                opposing_border_y = image_to_match_border_y + direction_y[
                    direction]
                opposing_border = (opposing_border_x, opposing_border_y,
                                   opposite_direction[direction])
                if opposing_border in open_borders:
                    del open_borders[opposing_border]
                elif 0 <= opposing_border_x < full_picture_length and 0 <= opposing_border_y < full_picture_length:
                    open_borders[(
                        image_to_match_border_x, image_to_match_border_y,
                        direction
                    )] = matching_image.border_from_direction(direction)

    solution_part1 = full_image[(0, 0)].picture_id * full_image[
        (0, full_picture_length - 1)].picture_id * full_image[
            (full_picture_length - 1, 0)].picture_id * full_image[
                (full_picture_length - 1, full_picture_length - 1)].picture_id

    print("Solution part1: ", solution_part1)

    timing.log("Part 1 finished!")

    pixels = []
    for picture_y in range(full_picture_length):
        for picture_x in range(full_picture_length):
            if picture_x == 0:
                pixels.extend(full_image[(picture_x, picture_y)].pixels)
            else:
                pixels_from_picture = full_image[(picture_x, picture_y)].pixels
                for pixel_y in range(len(pixels_from_picture)):
                    pixels[(picture_y * len(pixels_from_picture)) +
                           pixel_y].extend(pixels_from_picture[pixel_y])

    full_picture = Picture(1, (0, 0), (0, 0), (0, 0), (0, 0), pixels)

    nessy = [(0, 1), (1, 2), (4, 2), (5, 1), (6, 1), (7, 2), (10, 2), (11, 1),
             (12, 1), (13, 2), (16, 2), (17, 1), (18, 0), (18, 1), (19, 1)]

    full_picture_length = len(full_picture.pixels_with_borders)

    nessy_orientation = False
    rotation_count = 0
    flipped_x = False
    flipped_y = False
    nessys_found = 0
    while not nessy_orientation:
        modified_picture = copy.deepcopy(full_picture.pixels_with_borders)
        for y in range(full_picture_length - 2):
            for x in range(full_picture_length - 19):
                nessy_found = True
                for nessy_coord in nessy:
                    if full_picture.pixels_with_borders[y + nessy_coord[1]][
                            x + nessy_coord[0]] != "#":
                        nessy_found = False
                        break
                if nessy_found:
                    nessys_found += 1
                    nessy_orientation = True
                    for nessy_coord in nessy:
                        modified_picture[y +
                                         nessy_coord[1]][x +
                                                         nessy_coord[0]] = "O"
        if not nessy_orientation:
            if rotation_count == 3 and not flipped_x:
                full_picture.flip_x()
                rotation_count = 0
                flipped_x = True
            elif rotation_count == 3 and not flipped_y:
                full_picture.flip_y()
                rotation_count = 0
                flipped_y = True
            elif rotation_count == 3:
                break
            else:
                full_picture.rotate(1)
                rotation_count += 1

    rough_water_count = 0
    for y in range(full_picture_length):
        for x in range(full_picture_length):
            if modified_picture[y][x] == "#":
                rough_water_count += 1

    print("Solution part2: ", rough_water_count)