From 5b40798732bb90c4b219a3f9cc4c1955bbe0c237 Mon Sep 17 00:00:00 2001 From: Olle Kjellqvist <olle.kjellqvist@control.lth.se> Date: Wed, 11 Dec 2024 15:09:45 +0100 Subject: [PATCH] Added some solutions --- day 10/day_10_olle.py | 79 +++++++++++++++++++++++++++++++++ day 11/day_11_olle.py | 83 ++++++++++++++++++++++++++++++++++ day 6/day_6_olle.py | 99 +++++++++++++++++++++++++++++++++++++++++ day 7/day_7_olle.py | 66 +++++++++++++++++++++++++++ day 8/day_8_olle.py | 94 +++++++++++++++++++++++++++++++++++++++ day 9/day_9_olle.py | 101 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 522 insertions(+) create mode 100644 day 10/day_10_olle.py create mode 100644 day 11/day_11_olle.py create mode 100644 day 6/day_6_olle.py create mode 100644 day 7/day_7_olle.py create mode 100644 day 8/day_8_olle.py create mode 100644 day 9/day_9_olle.py diff --git a/day 10/day_10_olle.py b/day 10/day_10_olle.py new file mode 100644 index 0000000..d29efe0 --- /dev/null +++ b/day 10/day_10_olle.py @@ -0,0 +1,79 @@ +from typing import List, Tuple +import numpy as np +def read_file(filename: str) -> List[List[int]]: + with open(filename, 'r') as file: + data = file.read().strip().split('\n') + topographic_map = [[int(c) for c in row] for row in data] + return topographic_map + +def get_all_indices(value: int, topographic_map: List[List[int]]) -> List[Tuple[int, int]]: + return [(i, j) for i in range(len(topographic_map)) for j in range(len(topographic_map[0])) if topographic_map[i][j] == value] + +def create_value_map(topographic_map: List[List[int]]) -> np.ndarray: + height, width = len(topographic_map), len(topographic_map[0]) + # Initialize values to 1 for all indices with value 9 + value_map = {} + for i, j in get_all_indices(9, topographic_map): + value_map[(i, j)] = set([(i, j)]) + # Iterate backwards from 8 to 0 + for value in range(8, -1, -1): + for i, j in get_all_indices(value, topographic_map): + if (i, j) not in value_map: + value_map[(i, j)] = set() + if ((i + 1) <= (height - 1) and + topographic_map[i + 1][j] == value + 1): + value_map[(i, j)] = value_map[(i, j)].union(value_map[(i + 1, j)]) + if ((i - 1) >= 0 and + topographic_map[i - 1][j] == value + 1): + value_map[(i, j)] = value_map[(i, j)].union(value_map[(i - 1, j)]) + if ((j + 1) <= (width - 1) and + topographic_map[i][j + 1] == value + 1): + value_map[(i, j)] = value_map[(i, j)].union(value_map[(i, j + 1)]) + if ((j - 1) >= 0 and + topographic_map[i][j - 1] == value + 1): + value_map[(i, j)] = value_map[(i, j)].union(value_map[(i, j - 1)]) + value_sums = [[len(value_map[(i, j)]) for j in range(width)] for i in range(height)] + return np.array(value_sums) + + +def create_value_map_2(topographic_map: List[List[int]]) -> np.ndarray: + height, width = len(topographic_map), len(topographic_map[0]) + value_map = np.zeros((height, width), dtype=int) + # Initialize values to 1 for all indices with value 9 + for i, j in get_all_indices(9, topographic_map): + value_map[i][j] = 1 + # Iterate backwards from 8 to 0 + for value in range(8, -1, -1): + for i, j in get_all_indices(value, topographic_map): + if ((i + 1) <= (height - 1) and + topographic_map[i + 1][j] == value + 1): + value_map[i][j] += value_map[i + 1][j] + if ((i - 1) >= 0 and + topographic_map[i - 1][j] == value + 1): + value_map[i][j] += value_map[i - 1][j] + if ((j + 1) <= (width - 1) and + topographic_map[i][j + 1] == value + 1): + value_map[i][j] += value_map[i][j + 1] + if ((j - 1) >= 0 and + topographic_map[i][j - 1] == value + 1): + value_map[i][j] += value_map[i][j - 1] + return value_map + +def sum_of_trailhead_scores(topographic_map: List[List[int]]) -> int: + value_map = create_value_map(topographic_map) + sum_scores = 0 + for i, j in get_all_indices(0, topographic_map): + sum_scores += value_map[i][j] + return sum_scores + +def sum_of_trailhead_scores_2(topographic_map: List[List[int]]) -> int: + value_map = create_value_map_2(topographic_map) + sum_scores = 0 + for i, j in get_all_indices(0, topographic_map): + sum_scores += value_map[i][j] + return sum_scores + +if __name__ == '__main__': + topographic_map = read_file('input') + print(sum_of_trailhead_scores(topographic_map)) + print(sum_of_trailhead_scores_2(topographic_map)) diff --git a/day 11/day_11_olle.py b/day 11/day_11_olle.py new file mode 100644 index 0000000..5d9934e --- /dev/null +++ b/day 11/day_11_olle.py @@ -0,0 +1,83 @@ +from typing import List, Dict +import time +def read_file(filename:str) -> List[str]: + with open(filename) as f: + return f.read().strip().split(' ') + +def read_file_optimized(filename:str) -> Dict[str, int]: + arrangement = read_file(filename) + new_arrangement = {} + for stone in arrangement: + try: + new_arrangement[stone] += 1 + except KeyError: + new_arrangement[stone] = 1 + return new_arrangement + + +def blink(arrangement:List[str]) -> List[str]: + new_arrangement = [] + for stone in arrangement: + if int(stone) == 0: + new_arrangement.append('1') + elif len(stone) % 2 == 0: + first_half = stone[:len(stone)//2] + second_half = stone[len(stone)//2:] + # remove leading zeros of second half + second_half = str(int(second_half)) + new_arrangement.append(first_half) + new_arrangement.append(second_half) + else: + new_arrangement.append(str(int(stone) * 2024)) + return new_arrangement + +def blink_optimized(arrangement: Dict[str, int]) -> Dict[str, int]: + new_arrangement = {} + for stone, count in arrangement.items(): + if int(stone) == 0: + try: + new_arrangement['1'] += count + except KeyError: + new_arrangement['1'] = count + elif len(stone) % 2 == 0: + first_half = stone[:len(stone)//2] + second_half = stone[len(stone)//2:] + # remove leading zeros of second half + second_half = str(int(second_half)) + try: + new_arrangement[first_half] += count + except KeyError: + new_arrangement[first_half] = count + try: + new_arrangement[second_half] += count + except KeyError: + new_arrangement[second_half] = count + else: + try: + new_arrangement[str(int(stone) * 2024)] += count + except KeyError: + new_arrangement[str(int(stone) * 2024)] = count + return new_arrangement + + +if __name__ == '__main__': + arrangement = read_file('input') + new_arrangement = read_file_optimized('input') + tic = time.time() + for i in range(25): + arrangement = blink(arrangement) + toc = time.time() + print(f"25 blinks took {toc-tic} seconds with unoptimized version") + print("Number of stones after 25 blinks: ", len(arrangement)) + tic = time.time() + for i in range(25): + new_arrangement = blink_optimized(new_arrangement) + toc = time.time() + print(f"25 blinks took {toc-tic} seconds with optimized version") + print("Number of stones after 25 blinks: ", sum(new_arrangement.values())) + new_arrangement = read_file_optimized('input') + for i in range(75): + new_arrangement = blink_optimized(new_arrangement) + print("Number of stones after 75 blinks: ", sum(new_arrangement.values())) + + diff --git a/day 6/day_6_olle.py b/day 6/day_6_olle.py new file mode 100644 index 0000000..9b1ad31 --- /dev/null +++ b/day 6/day_6_olle.py @@ -0,0 +1,99 @@ +from typing import List, Tuple + +class Turtle: + + def __init__(self, direction, environment, coords=(0, 0)): + self.direction = direction + self.coords = coords + self.environment = environment + self.has_visited = set([(coords[0], coords[1], direction)]) + self.in_loop = False + + def step(self) -> bool: + x, y = self.coords + if self.direction == 'N': + if y == 0: + return False + elif self.environment[y - 1][x] == '#': + self.direction = 'E' + else: + y -= 1 + elif self.direction == 'E': + if x == len(self.environment[0]) - 1: + return False + elif self.environment[y][x + 1] == '#': + self.direction = 'S' + else: + x += 1 + elif self.direction == 'S': + if y == len(self.environment) - 1: + return False + elif self.environment[y + 1][x] == '#': + self.direction = 'W' + else: + y += 1 + elif self.direction == 'W': + if x == 0: + return False + elif self.environment[y][x - 1] == '#': + self.direction = 'N' + else: + x -= 1 + else: + raise ValueError("Invalid direction") + + # Update position and track visited coordinates + self.coords = (x, y) + if (x, y, self.direction) in self.has_visited: + self.in_loop = True + return False + self.has_visited.add((x, y, self.direction)) + return True + + def run(self): + while self.step(): + pass + if self.in_loop: + return False + else: + return True + + def get_visited(self) -> int: + return len(set([(x, y) for x, y, _ in self.has_visited])) + +def get_starting_coords(environment: List[str]) -> Tuple[int, int]: + for row, col in enumerate(environment): + if '^' in col: + return (col.index('^'), row) + raise ValueError("Starting position not found in environment") + +def read_file(filename: str) -> List[str]: + with open(filename, 'r') as f: + data = f.read().strip().split('\n') + return data + +def get_p2_solution(filename: str) -> int: + orig_environment = read_file(filename) + start_coords = get_starting_coords(orig_environment) + num_loops = 0 + for i in range(len(orig_environment)): + print(f"Row {i} of {len(orig_environment)}, num_loops: {num_loops}") + for j in range(len(orig_environment[0])): + if orig_environment[i][j] in ['^', '#']: + continue + environment = [list(row) for row in orig_environment] + environment[i][j] = '#' + turtle = Turtle(direction='N', environment=environment, coords=start_coords) + turtle.run() + if turtle.in_loop: + num_loops += 1 + return num_loops + +if __name__ == "__main__": + environment = read_file('input') + start_coords = get_starting_coords(environment) + turtle = Turtle(direction='N', environment=environment, coords=start_coords) + turtle.run() + print(turtle.get_visited()) + print(get_p2_solution('input')) + diff --git a/day 7/day_7_olle.py b/day 7/day_7_olle.py new file mode 100644 index 0000000..06c4776 --- /dev/null +++ b/day 7/day_7_olle.py @@ -0,0 +1,66 @@ +from typing import List, Tuple +from math import log10 +def read_file(file_name: str) -> Tuple[List[int], List[List[int]]]: + lhs = [] + rhs = [] + with open(file_name, 'r') as file: + for line in file: + if line == '': + break + line = line.strip().split(': ') + lhs.append(int(line[0])) + rhs.append([int(x) for x in line[1].split(' ')]) + return lhs, rhs + +def sum_valid_equations(lhs: List[int], rhs: List[List[int]]) -> int: + max_len = max(len(l) for l in rhs) - 1 # -1 because only operators between numbers + operator_table = [ + [['+'], ['*']] + ] + for i in range(1, max_len): + operator_table.append( + [o + operator for o in operator_table[-1] for operator in [['+'], ['*']]] + ) + valid_lhs = 0 + for l, r in zip(lhs, rhs): + operator_list = operator_table[len(r) - 2] + for operators in operator_list: + if check_equation(l, r, operators): + valid_lhs += l + break + return valid_lhs + +def sum_valid_equations_p2(lhs: List[int], rhs: List[List[int]]) -> int: + max_len = max(len(l) for l in rhs) - 1 # -1 because only operators between numbers + operator_table = [ + [['+'], ['*'], ['||']] + ] + for i in range(1, max_len): + operator_table.append( + [o + operator for o in operator_table[-1] for operator in [['+'], ['*'],['||']]] + ) + valid_lhs = 0 + for l, r in zip(lhs, rhs): + operator_list = operator_table[len(r) - 2] + for operators in operator_list: + if check_equation(l, r, operators): + valid_lhs += l + break + return valid_lhs + +def check_equation(lhs: int, rhs: List[int], operators: List[str]) -> bool: + result = rhs[0] + for i in range(1, len(rhs)): + if operators[i-1] == '+': + result += rhs[i] + elif operators[i-1] == '*': + result *= rhs[i] + elif operators[i-1] == '||': + result = result * 10**int(log10(rhs[i]) + 1) + rhs[i] + return result == lhs + +if __name__ == "__main__": + lhs, rhs = read_file('input') + print(sum_valid_equations(lhs, rhs)) + print(sum_valid_equations_p2(lhs, rhs)) + diff --git a/day 8/day_8_olle.py b/day 8/day_8_olle.py new file mode 100644 index 0000000..ee598c8 --- /dev/null +++ b/day 8/day_8_olle.py @@ -0,0 +1,94 @@ +from math import gcd +from typing import List, Set, Dict +def read_file(filename: str) -> List[str]: + with open(filename, 'r') as file: + data = file.read().strip().split('\n') + return data + +def get_letters(environment: List[str]) -> Set[str]: + letters = set() + for row in environment: + letters = letters.union(set(row)) + letters.discard('.') + return letters + +def get_coordinates(environment: List[str]) -> Dict: + height = len(environment) + width = len(environment[0]) + letter_coordinates = {} + for i in range(height): + line = environment[i] + for j in range(width): + if line[j] == '.': + continue + if line[j] in letter_coordinates.keys(): + letter_coordinates[line[j]].append((i, j)) + else: + letter_coordinates[line[j]] = [(i, j)] + return letter_coordinates + + +def num_of_antinodes(environment: List[str]) -> int: + height = len(environment) + width = len(environment[0]) + antinodes = set() + coords_dict = get_coordinates(environment) + for key in coords_dict.keys(): + if len(coords_dict[key]) < 2: + continue + else: + coords = coords_dict[key] + for j in range(len(coords) - 1): + for k in range(j + 1, len(coords)): + row1, col1 = coords[j] + row2, col2 = coords[k] + antinode1_row = 2 * row2 - row1 + antinode2_row = 2 * row1 - row2 + antinode1_col = 2 * col2 - col1 + antinode2_col = 2 * col1 - col2 + if (0 <= antinode1_row < height + and 0 <= antinode1_col < width): + antinodes.add((antinode1_row, antinode1_col)) + if (0 <= antinode2_row < height + and 0 <= antinode2_col < width): + antinodes.add((antinode2_row, antinode2_col)) + return len(antinodes) + +def num_of_antinodes_2(environment: List[str]) -> int: + height = len(environment) + width = len(environment[0]) + antinodes = set() + coords_dict = get_coordinates(environment) + for key in coords_dict.keys(): + if len(coords_dict[key]) < 2: + continue + else: + coords = coords_dict[key] + for j in range(len(coords) - 1): + for k in range(j + 1, len(coords)): + row1, col1 = coords[j] + row2, col2 = coords[k] + rowdist = row2-row1 + coldist = col2-col1 + rowdist = rowdist // gcd(rowdist, coldist) + coldist = coldist // gcd(rowdist, coldist) + antirow, anticol = row1, col1 + while (0 <= antirow < height and + 0 <= anticol < height): + antinodes.add((antirow, anticol)) + antirow += rowdist + anticol += coldist + antirow, anticol = row1, col1 + antirow -= rowdist + anticol -= coldist + while (0 <= antirow < height and + 0 <= anticol < height): + antinodes.add((antirow, anticol)) + antirow -= rowdist + anticol -= coldist + return len(antinodes) + +if __name__ == "__main__": + env = read_file("input") + print(num_of_antinodes(env)) + print(num_of_antinodes_2(env)) diff --git a/day 9/day_9_olle.py b/day 9/day_9_olle.py new file mode 100644 index 0000000..4707546 --- /dev/null +++ b/day 9/day_9_olle.py @@ -0,0 +1,101 @@ +from typing import List +def read_file(filename: str) -> str: + with open(filename) as f: + return f.readline().strip() + +def get_id_representation(file: str) -> List[str]: + i = 0 + id_rep = [] + while i < len(file): + if i % 2 == 0: + id_rep += [str(i//2)]*int(file[i]) + else: + id_rep += ['.']*int(file[i]) + i += 1 + return id_rep + + + +def rearrange(id_rep: str) -> List[str]: + i = 0 + j = len(id_rep) - 1 + rearranged_rep = [] + while i < j: + while id_rep[i].isnumeric() and i < j: + rearranged_rep += [id_rep[i]] + i += 1 + while (id_rep[j] == '.') and i < j: + j -= 1 + if i == j: + break + else: + rearranged_rep += [id_rep[j]] + i += 1 + j -= 1 + rearranged_rep += [id_rep[j]] + print(len(id_rep)) + print(len(rearranged_rep)) + return rearranged_rep + ['.'] * (len(id_rep) - len(rearranged_rep)) + +def rearrange_whole(file: str) -> List[str]: + file = list(file) + ids = [] + i = 0 + for i in range(len(file)): + if i % 2 == 0: + ids += [str(i // 2)] + else: + ids += [str('.')] + j = 1 + while j <= (len(file) - 1): + if ids[-j] == '.': + j += 1 + continue + i = 0 + while (i < (len(file) - j) and + (ids[i].isnumeric() or int(file[i]) < int(file[-j]))): + i += 1 + if i == (len(file) - j): + j += 1 + continue + elif int(file[i]) == int(file[-j]): + ids[i], ids[-j] = ids[-j], ids[i] + file[i], file[-j] = file[-j], file[i] + j += 1 + else: + numel_to_keep = int(file[i]) - int(file[-j]) + file[i] = file[-j] + file = file[:i+1] + [str(numel_to_keep)] + file[i+1:] + ids[i], ids[-j] = ids[-j], ids[i] + ids = ids[:i+1] + ['.'] + ids[i+1:] + j += 2 + + rearranged_file = [] + for i in range(len(file)): + rearranged_file += [ids[i]]*int(file[i]) + return rearranged_file + + + +def compute_checksum(arranged_file: List[str]) -> int: + checksum = 0 + i = 0 + while arranged_file[i].isnumeric(): + checksum += int(arranged_file[i]) * i + i += 1 + return checksum + +def compute_checksum_whole(arranged_file: List[str]) -> int: + checksum = 0 + i = 0 + for i in range(len(arranged_file)): + if arranged_file[i].isnumeric(): + checksum += int(arranged_file[i]) * i + return checksum + + +if __name__ == "__main__": + file = read_file('input') + id_rep = get_id_representation(file) + print(compute_checksum(rearrange(id_rep))) + print(compute_checksum_whole(rearrange_whole(file))) -- GitLab