diff --git a/2022/09/README.md b/2022/09/README.md new file mode 100644 index 0000000..bd6468e --- /dev/null +++ b/2022/09/README.md @@ -0,0 +1,11 @@ +# 09 + +This took me waaay longer than it should be. The first part was quick and easy, but in the second part my diagonal logic was flawed. Then the "this is to late I should go to sleep" mode kicked in. + +
+ Solutions +
    +
  1. 5695
  2. +
  3. 2434
  4. +
+
diff --git a/2022/09/python/main.py b/2022/09/python/main.py new file mode 100644 index 0000000..2ea7c1f --- /dev/null +++ b/2022/09/python/main.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python + +from dataclasses import dataclass +from os.path import dirname, join +from typing import Literal + +# 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') + + +@dataclass(unsafe_hash=True) +class Point: + x: int + y: int + + def __add__(self, other: 'Point'): + return Point(self.x + other.x, self.y + other.y) + + def __sub__(self, other: 'Point'): + return Point(self.x - other.x, self.y - other.y) + + def distance(self, other: 'Point') -> int: + return max(abs(self.x - other.x), abs(self.y-other.y)) + + def normalise(self): + self.x = max(-1, min(1, self.x)) + self.y = max(-1, min(1, self.y)) + return self + + +Directions = Literal['U', 'D', 'L', 'R'] +Instruction = tuple[Directions, int] + +MAPPING: dict[Directions, Point] = {'U': Point(0, 1), 'D': Point(0, -1), 'L': Point(-1, 0), 'R': Point(1, 0)} + + +class Rope: + def __init__(self, instructions: list[Instruction], links: int) -> None: + self.instructions = instructions + start = Point(0, 0) + self.touched: set[Point] = set([start]) + self.links = [start for _ in range(links)] + + def move(self, instruction: Instruction): + direction, travel = instruction + last = len(self.links) - 1 + for _ in range(travel): + moved: list[Point] = [] + for i, link in enumerate(self.links): + if i == 0: + link = link + MAPPING[direction] + else: + head = moved[i-1] + distance = head.distance(link) + if distance > 1: + link = link + (head - link).normalise() + moved.append(link) + if i == last: + self.touched.add(link) + self.links = moved + + def run(self): + for instruction in self.instructions: + self.move(instruction) + return len(self.touched) + + @staticmethod + def visualise(points: list[Point], numbered=True) -> str: + """ + Only used to visualise, not actual logic + """ + max_x = max(p.x for p in points) + min_x = min(p.x for p in points) + max_y = max(p.y for p in points) + min_y = min(p.y for p in points) + range_x = max_x - min_x + range_y = max_y - min_y + grid = [ + ['.' for _ in range(range_x+1)] + for _ in range(range_y+1) + ] + for i, link in enumerate(points): + row = grid[range_y-(link.y - min_y)] + if row[link.x-min_x] == '.': + row[link.x-min_x] = str(i) if numbered else "#" + return '\n'.join(map(lambda line: ''.join(line), grid)) + + @staticmethod + def parse(data: str, links: int): + instructions = [ + (i[0], int(i[1])) + for i in map(lambda line: line.split(), data.splitlines()) + ] + return Rope(instructions, links) + + +# Running +grid = Rope.parse(data, 2) +print(grid.run()) +grid = Rope.parse(data, 10) +print(grid.run())