2023: d18: ex2: add solution
This commit is contained in:
parent
b806f8dc39
commit
d4aa8d246f
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