From 8a2747d45d81bb194682fdddf113a668170674ac Mon Sep 17 00:00:00 2001 From: pareronia <49491686+pareronia@users.noreply.github.com> Date: Mon, 18 Dec 2023 10:04:40 +0100 Subject: [PATCH] AoC 2023 Day 18 Part 2 --- README.md | 2 +- src/main/python/AoC2023_18.py | 117 +++++++++++++++++++++++----------- 2 files changed, 80 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 41177482..c9089ca5 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | | ---| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| python3 | [✓](src/main/python/AoC2023_01.py) | [✓](src/main/python/AoC2023_02.py) | [✓](src/main/python/AoC2023_03.py) | [✓](src/main/python/AoC2023_04.py) | [✓](src/main/python/AoC2023_05.py) | [✓](src/main/python/AoC2023_06.py) | [✓](src/main/python/AoC2023_07.py) | [✓](src/main/python/AoC2023_08.py) | [✓](src/main/python/AoC2023_09.py) | [✓](src/main/python/AoC2023_10.py) | [✓](src/main/python/AoC2023_11.py) | [✓](src/main/python/AoC2023_12.py) | [✓](src/main/python/AoC2023_13.py) | [✓](src/main/python/AoC2023_14.py) | [✓](src/main/python/AoC2023_15.py) | [✓](src/main/python/AoC2023_16.py) | [✓](src/main/python/AoC2023_17.py) | | | | | | | | | +| python3 | [✓](src/main/python/AoC2023_01.py) | [✓](src/main/python/AoC2023_02.py) | [✓](src/main/python/AoC2023_03.py) | [✓](src/main/python/AoC2023_04.py) | [✓](src/main/python/AoC2023_05.py) | [✓](src/main/python/AoC2023_06.py) | [✓](src/main/python/AoC2023_07.py) | [✓](src/main/python/AoC2023_08.py) | [✓](src/main/python/AoC2023_09.py) | [✓](src/main/python/AoC2023_10.py) | [✓](src/main/python/AoC2023_11.py) | [✓](src/main/python/AoC2023_12.py) | [✓](src/main/python/AoC2023_13.py) | [✓](src/main/python/AoC2023_14.py) | [✓](src/main/python/AoC2023_15.py) | [✓](src/main/python/AoC2023_16.py) | [✓](src/main/python/AoC2023_17.py) | [✓](src/main/python/AoC2023_18.py) | | | | | | | | | java | [✓](src/main/java/AoC2023_01.java) | [✓](src/main/java/AoC2023_02.java) | [✓](src/main/java/AoC2023_03.java) | [✓](src/main/java/AoC2023_04.java) | [✓](src/main/java/AoC2023_05.java) | [✓](src/main/java/AoC2023_06.java) | [✓](src/main/java/AoC2023_07.java) | [✓](src/main/java/AoC2023_08.java) | [✓](src/main/java/AoC2023_09.java) | [✓](src/main/java/AoC2023_10.java) | [✓](src/main/java/AoC2023_11.java) | [✓](src/main/java/AoC2023_12.java) | [✓](src/main/java/AoC2023_13.java) | [✓](src/main/java/AoC2023_14.java) | [✓](src/main/java/AoC2023_15.java) | [✓](src/main/java/AoC2023_16.java) | [✓](src/main/java/AoC2023_17.java) | | | | | | | | | | rust | [✓](src/main/rust/AoC2023_01/src/main.rs) | [✓](src/main/rust/AoC2023_02/src/main.rs) | [✓](src/main/rust/AoC2023_03/src/main.rs) | [✓](src/main/rust/AoC2023_04/src/main.rs) | | [✓](src/main/rust/AoC2023_06/src/main.rs) | [✓](src/main/rust/AoC2023_07/src/main.rs) | [✓](src/main/rust/AoC2023_08/src/main.rs) | [✓](src/main/rust/AoC2023_09/src/main.rs) | | | | | | [✓](src/main/rust/AoC2023_15/src/main.rs) | [✓](src/main/rust/AoC2023_16/src/main.rs) | [✓](src/main/rust/AoC2023_17/src/main.rs) | | | | | | | | | diff --git a/src/main/python/AoC2023_18.py b/src/main/python/AoC2023_18.py index 407bf218..cf9e5efc 100644 --- a/src/main/python/AoC2023_18.py +++ b/src/main/python/AoC2023_18.py @@ -4,23 +4,20 @@ # import sys -from typing import NamedTuple, Iterator +from typing import NamedTuple from aoc.common import InputData from aoc.common import SolutionBase from aoc.common import aoc_samples -from aoc.common import log from aoc.geometry import Direction -from aoc.geometry import Draw from aoc.geometry import Position -from aoc.graph import flood_fill -from aoc.navigation import Heading -from aoc.navigation import NavigationWithHeading class DigInstruction(NamedTuple): direction: Direction amount: int + big_direction: Direction + big_amount: int Input = list[DigInstruction] @@ -46,49 +43,93 @@ class DigInstruction(NamedTuple): """ +class Polygon(NamedTuple): + vertices: list[Position] + + def shoelace(self) -> int: + ans = 0 + size = len(self.vertices) + for i in range(size): + ans += self.vertices[i].x * ( + self.vertices[(i + 1) % size].y - self.vertices[i - 1].y + ) + return abs(ans) // 2 + + def circumference(self) -> int: + ans = 0 + for i in range(1, len(self.vertices)): + ans += self.vertices[i].manhattan_distance(self.vertices[i - 1]) + return ans + + def picks(self) -> int: + a = self.shoelace() + b = self.circumference() + return a - b // 2 + 1 + + def area(self) -> int: + return self.picks() + self.circumference() + + class Solution(SolutionBase[Input, Output1, Output2]): def parse_input(self, input_data: InputData) -> Input: ans = [] for line in input_data: - d, a, _ = line.split() - ans.append(DigInstruction(Direction.from_str(d), int(a))) + d, a, hx = line.split() + bd = [ + Direction.RIGHT, + Direction.DOWN, + Direction.LEFT, + Direction.UP, + ][int(hx[7])] + ba = int(hx[2:7], 16) + ans.append( + DigInstruction( + Direction.from_str(d), + int(a), + bd, + ba, + ) + ) + return ans + + def to_positions( + self, instructions: list[DigInstruction] + ) -> list[Position]: + pos = Position(0, 0) + ans = [pos] + for instruction in instructions: + pos = pos.translate( + instruction.direction.vector, instruction.amount + ) + ans.append(pos) + return ans + + def to_positions_big( + self, instructions: list[DigInstruction] + ) -> list[Position]: + pos = Position(0, 0) + ans = [pos] + for instruction in instructions: + pos = pos.translate( + instruction.big_direction.vector, instruction.big_amount + ) + ans.append(pos) return ans def part_1(self, instructions: Input) -> Output1: - log(instructions) - nav = NavigationWithHeading(Position(0, 0), Heading.NORTH) - for ins in instructions: - for i in range(ins.amount): - nav.drift(Heading.from_direction(ins.direction), 1) - positions = {_ for _ in nav.get_visited_positions(True)} - for line in Draw.draw(positions, "#", "."): - log(line) - min_x = min(p.x for p in positions) - max_y = max(p.y for p in positions) - pos = Position(min_x - 1, max_y + 1) - while True: - pos = Position(pos.x + 1, pos.y - 1) - if pos in positions: - break - start = Position(pos.x + 1, pos.y - 1) - log(start) - - def adjacent(pos: Position) -> Iterator[Position]: - for d in Direction.octants(): - n = pos.translate(d.vector) - if n not in positions: - yield n - - inside = flood_fill(start, adjacent) - return len(inside) + len(positions) - - def part_2(self, input: Input) -> Output2: - return 0 + positions = self.to_positions(instructions) + polygon = Polygon(positions) + return polygon.area() + + def part_2(self, instructions: Input) -> Output2: + positions = self.to_positions_big(instructions) + polygon = Polygon(positions) + return polygon.area() @aoc_samples( ( ("part_1", TEST, 62), - # ("part_2", TEST, "TODO"), + ("part_2", TEST, 952408144115), ) ) def samples(self) -> None: