# -*- coding: utf-8 -*- from abc import ABC from copy import copy from dataclasses import dataclass from typing import Tuple, Iterator, List, Set from aoc import BaseAssignment @dataclass class Coordinate: x: int y: int def __hash__(self): return tuple([self.x, self.y]).__hash__() def __sub__(self, other: "Coordinate"): return Coordinate(self.x - other.x, self.y - other.y) @property def polarity_x(self): try: return abs(self.x) / self.x except ZeroDivisionError: return 0 @property def polarity_y(self): try: return abs(self.y) / self.y except ZeroDivisionError: return 0 class Assignment(BaseAssignment, ABC): def parse_line(self, item: str) -> Tuple[str, int]: direction, amount = item.split(" ") return direction, int(amount) def move(self, direction: str, amount: int): pass @staticmethod def next_head(head: Coordinate, direction: str): match direction: case "R": head.x += 1 case "L": head.x -= 1 case "U": head.y += 1 case "D": head.y -= 1 @staticmethod def next_tail(head: Coordinate, tail: Coordinate): delta = head - tail if abs(delta.x) > 1 and delta.y == 0: tail.x += delta.x - delta.polarity_x if abs(delta.x) > 1 and abs(delta.y) == 1: tail.x += delta.x - delta.polarity_x tail.y += delta.y if abs(delta.y > 1) and delta.x == 0: tail.y += delta.y - delta.polarity_y if abs(delta.y) > 1 and abs(delta.x) == 1: tail.y += delta.y - delta.polarity_y tail.x += delta.x def tail_positions(self, input: Iterator[str]) -> Iterator[Coordinate]: head = Coordinate(0, 0) tail = Coordinate(0, 0) for line in input: direction, amount = self.parse_line(line) for _ in range(amount): self.next_head(head, direction) self.next_tail(head, tail) yield tail def unique_tail_positions(self, input: Iterator[str]) -> Set[Coordinate]: unique_tail_positions = set() for position in self.tail_positions(input): unique_tail_positions.add(copy(position)) return unique_tail_positions def visualize(self, width: int, height: int, positions: Set[Coordinate]): rows = [] for y in range(height): items = [] for x in range(width): if x == 0 and y == 0: items.append("s") elif Coordinate(x, y) in positions: items.append("#") else: items.append(".") rows.append(items) return "\n".join(["".join(row) for row in reversed(rows)]) class AssignmentOne(Assignment): example_result = 13 def run(self, input: Iterator) -> int: unique_positions = self.unique_tail_positions(input) print() print(self.visualize(6, 5, unique_positions)) return len(unique_positions) class AssignmentTwo(Assignment): pass