From 7c399251d6650b94d02da5b9956d5fc4b55e62b6 Mon Sep 17 00:00:00 2001 From: Niccolo Borgioli Date: Thu, 9 Dec 2021 15:35:29 +0100 Subject: [PATCH 1/2] 09 --- 2021/09/README.md | 11 +++ 2021/09/python/main.py | 153 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 2021/09/README.md create mode 100644 2021/09/python/main.py diff --git a/2021/09/README.md b/2021/09/README.md new file mode 100644 index 0000000..64e1bed --- /dev/null +++ b/2021/09/README.md @@ -0,0 +1,11 @@ +# 09 + +The first was really straight forward. The second was cool as we could do a breadth first search, taking account the edges of our growing basins. + +
+ Solutions +
    +
  1. 439
  2. +
  3. 900900
  4. +
+
diff --git a/2021/09/python/main.py b/2021/09/python/main.py new file mode 100644 index 0000000..00b4893 --- /dev/null +++ b/2021/09/python/main.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python + +from os.path import join, dirname +from typing import List + +# Day 09 + +# Common + + +def read_input(filename): + data = join(dirname(__file__), '..', filename) + with open(data) as f: + return f.read().strip() + + +test = read_input('test.txt') +data = read_input('input.txt') + + +def make_string_blue(string): + return f'\033[94m{string}\033[0m' + + +def make_string_red(string): + return f'\033[91m{string}\033[0m' + + +class Point: + def __init__(self, x, y): + self.x = x + self.y = y + + def __repr__(self): + return f'({self.x}, {self.y})' + + def __hash__(self) -> int: + return hash((self.x, self.y)) + + def __eq__(self, o: object) -> bool: + if not isinstance(o, Point): + return False + return self.x == o.x and self.y == o.y + + def value(self, data: List[List[int]]): + return data[self.y][self.x] + + +class Floor: + def __init__(self, data: List[List[int]]): + self.data = data + self.max_x = len(data[0]) + self.max_y = len(data) + + def paint(self, blue: List[Point] = [], red: List[Point] = []) -> str: + out = '' + for y in range(self.max_y): + for x in range(self.max_x): + v = str(self.data[y][x]) + if Point(x, y) in red: + v = f'\033[31m{v}\033[0m' + elif Point(x, y) in blue: + v = f'\033[34m{v}\033[0m' + out += v + out += '\n' + return out + + def get_neighbors(self, point: Point) -> List[Point]: + neighbors = [] + if point.x - 1 >= 0: + neighbors.append(Point(point.x - 1, point.y)) + if point.x + 1 < self.max_x: + neighbors.append(Point(point.x + 1, point.y)) + if point.y - 1 >= 0: + neighbors.append(Point(point.x, point.y - 1)) + if point.y + 1 < self.max_y: + neighbors.append(Point(point.x, point.y + 1)) + return neighbors + + def find_lowest_points(self) -> List[Point]: + lowest_points = [] + for y in range(self.max_y): + for x in range(self.max_x): + p = Point(x, y) + neighbors = self.get_neighbors(p) + values = [p.value(self.data) for p in neighbors] + if p.value(self.data) < min(values): + lowest_points.append(p) + return lowest_points + + def find_basins(self): + lowest = self.find_lowest_points() + basins = [] + for point in lowest: + basin = set([point]) + edges = set([point]) + while len(edges) > 0: + new_edges = set() + for edge in edges: + neighbors = self.get_neighbors(edge) + for neighbor in neighbors: + if neighbor not in basin and neighbor.value(self.data) < 9: + basin.add(neighbor) + new_edges.add(neighbor) + edges = new_edges + basins.append(basin) + print(self.paint( + [point for basin in basins for point in basin], lowest)) + return basins + + def flag2(self) -> int: + basins = self.find_basins() + sizes = [len(b) for b in basins] + sizes = sorted(sizes, reverse=True) + total = 1 + for size in sizes[0:3]: + total *= size + return total + + def flag(self) -> int: + points = self.find_lowest_points() + return sum([ + p.value(self.data) + 1 + for p in points + ]) + + @staticmethod + def parse(data: str): + return Floor([ + list(map(int, list(row))) + for row in data.split('\n') + ]) + + +# 1 +print('1.') + +floor = Floor.parse(test) +print(floor.paint(floor.find_lowest_points())) +print(f'Test: {floor.flag()}') + +floor = Floor.parse(data) +print(floor.paint(floor.find_lowest_points())) +print(f'Real: {floor.flag()}') + +# 2 +print('\n2.') + +floor = Floor.parse(test) +print(f'Test: {floor.flag2()}') + +floor = Floor.parse(data) +print(f'Real: {floor.flag2()}') From ce3792cf6f680dafb5525095c5cce5c98ae3da78 Mon Sep 17 00:00:00 2001 From: Niccolo Borgioli Date: Thu, 16 Dec 2021 14:49:03 +0100 Subject: [PATCH 2/2] 19 --- 2021/10/README.md | 11 ++++ 2021/10/python/main.py | 122 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 2021/10/README.md create mode 100644 2021/10/python/main.py diff --git a/2021/10/README.md b/2021/10/README.md new file mode 100644 index 0000000..85ff953 --- /dev/null +++ b/2021/10/README.md @@ -0,0 +1,11 @@ +# 10 + +Description + +
+ Solutions +
    +
  1. 392097
  2. +
  3. 2
  4. +
+
diff --git a/2021/10/python/main.py b/2021/10/python/main.py new file mode 100644 index 0000000..ea85413 --- /dev/null +++ b/2021/10/python/main.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python + +from os.path import join, dirname +from typing import List + +# Day 10 + +# Common + + +def read_input(filename): + data = join(dirname(__file__), '..', filename) + with open(data) as f: + return f.read().strip() + + +test = read_input('test.txt') +data = read_input('input.txt') + + +class Node: + def __init__(self, brackets, children): + self.brackets = brackets + self.children = children + + def add_child(self, child): + self.children.append(child) + + def __repr__(self) -> str: + return f'{self.brackets} -> {len(self.children)} {self.children}' + + +class Parser: + openers = ['(', '[', '{', '<'] + closers = [')', ']', '}', '>'] + + def __init__(self, code: str): + self.tollerant = False + self.code = code + self.pos = 0 + self.valid = True + self.nodes: List[Node] = [] + + def parse(self, tollerant=False): + self.tollerant = tollerant + root = Node(None, []) + try: + self.traverse(root) + except Exception as e: + # print(e.args[0]) + pass + self.nodes.append(root) + + def traverse(self, node): + c = self.code[self.pos] + typ = self.openers.index(c) if c in self.openers else -1 + if typ == -1: + self.valid = False + raise Exception( + f'Invalid character at position {self.pos}. Found {c}') + + node = Node(typ, []) + while True: + self.pos += 1 + closing = self.closers[typ] + if self.pos >= len(self.code): + if self.tollerant: + self.code += closing + break + else: + raise Exception("EOF") + c = self.code[self.pos] + if c == closing: + break + else: + node.add_child(self.traverse(node)) + return node + + +class Checker: + def __init__(self, lines: List[str]): + self.lines = [Parser(line) for line in lines] + + def flag1(self): + for line in self.lines: + line.parse(tollerant=False) + points = {')': 3, ']': 57, '}': 1197, '>': 25137} + flag = [ + points[line.code[line.pos]] + for line in self.lines + if not line.valid + ] + return sum(flag) + + def flag2(self): + added = '' + for line in self.lines: + print(f'\nChecking:') + print(line.code) + before = len(line.code) + line.parse(tollerant=True) + if line.valid: + print(line.code) + added += line.code[before:] + + @ staticmethod + def parse(lines: str): + return Checker(lines.split('\n')) + + +# 1 +print('1.') +checker = Checker.parse(test) +print(f'Test: {checker.flag1()}') +checker = Checker.parse(data) +print(f'Real: {checker.flag1()}') + +# 2 +print('\n2.') + +checker = Checker.parse(test) +print(f'Test: {checker.flag2()}')