From 16dbc2cd1a910b5c5d34b7994436c20dedbfc259 Mon Sep 17 00:00:00 2001 From: cupcakearmy Date: Sat, 10 Dec 2022 02:11:53 +0100 Subject: [PATCH] day 9 --- 2022/09/README.md | 11 ++++ 2022/09/python/main.py | 112 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 2022/09/README.md create mode 100644 2022/09/python/main.py 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())