2023: d18: ex2: add solution
This commit is contained in:
parent
7e2489f5f9
commit
57f28d6411
97
2023/d18/ex2/ex2.py
Executable file
97
2023/d18/ex2/ex2.py
Executable file
|
@ -0,0 +1,97 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import itertools
|
||||
import sys
|
||||
from enum import StrEnum
|
||||
from typing import NamedTuple
|
||||
|
||||
|
||||
class Point(NamedTuple):
|
||||
x: int
|
||||
y: int
|
||||
|
||||
|
||||
class Direction(StrEnum):
|
||||
UP = "U"
|
||||
DOWN = "D"
|
||||
LEFT = "L"
|
||||
RIGHT = "R"
|
||||
|
||||
def apply(self, pos: Point, n: int = 1) -> Point:
|
||||
DIRECTIONS = {
|
||||
"U": Point(-1, 0),
|
||||
"D": Point(1, 0),
|
||||
"L": Point(0, -1),
|
||||
"R": Point(0, 1),
|
||||
}
|
||||
dx, dy = DIRECTIONS[self.value]
|
||||
return Point(pos.x + dx * n, pos.y + dy * n)
|
||||
|
||||
|
||||
DigPlanStep = tuple[Direction, int]
|
||||
DigPlan = list[DigPlanStep]
|
||||
|
||||
|
||||
def solve(input: list[str]) -> int:
|
||||
def parse_line(line: str) -> DigPlanStep:
|
||||
_, _, color = line.split()
|
||||
color = color[2:-1]
|
||||
n = color[:-1]
|
||||
dir = {
|
||||
"0": "R",
|
||||
"1": "D",
|
||||
"2": "L",
|
||||
"3": "U",
|
||||
}[color[-1]]
|
||||
return Direction(dir), int(n, base=16)
|
||||
|
||||
def parse(input: list[str]) -> DigPlan:
|
||||
return list(map(parse_line, input))
|
||||
|
||||
def dig_trench(plan: DigPlan) -> list[Point]:
|
||||
points = [Point(0, 0)]
|
||||
|
||||
for direction, n in plan:
|
||||
points.append(direction.apply(points[-1], n))
|
||||
# The trench should loop back to the start, make sure we don't count it twice
|
||||
if points[-1] == Point(0, 0):
|
||||
del points[-1]
|
||||
|
||||
return points
|
||||
|
||||
def lagoon_volume(trench: list[Point]) -> int:
|
||||
def shoelace_area(points: list[Point]) -> int:
|
||||
# Must be integer because pipes follow the grid, and can't cut squares in half
|
||||
return abs(
|
||||
sum(
|
||||
(points[i - 1].x * points[i].y) - (points[i].x * points[i - 1].y)
|
||||
for i in range(len(points))
|
||||
)
|
||||
// 2
|
||||
)
|
||||
|
||||
def perimeter(points: list[Point]) -> int:
|
||||
res = 0
|
||||
|
||||
for p, n in itertools.pairwise(itertools.chain(points, [points[0]])):
|
||||
res += abs(n.x - p.x) + abs(n.y - p.y)
|
||||
|
||||
return res
|
||||
|
||||
area = shoelace_area(trench)
|
||||
trench_points = perimeter(trench)
|
||||
interior_points = area - trench_points // 2 + 1
|
||||
return interior_points + trench_points
|
||||
|
||||
plan = parse(input)
|
||||
trench = dig_trench(plan)
|
||||
return lagoon_volume(list(trench))
|
||||
|
||||
|
||||
def main() -> None:
|
||||
input = sys.stdin.read().splitlines()
|
||||
print(solve(input))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in a new issue