From 54bbc228f42009b4a1cc3adc8c5e86ea617eb464 Mon Sep 17 00:00:00 2001 From: felixm Date: Sat, 30 Nov 2024 12:22:44 -0500 Subject: [PATCH] Solve 2021 day 22 --- 2021/d22.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++++---- README.md | 2 +- 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/2021/d22.py b/2021/d22.py index 677f883..d6e670f 100644 --- a/2021/d22.py +++ b/2021/d22.py @@ -1,5 +1,7 @@ from lib import get_data from lib import ints +from dataclasses import dataclass +from typing import Optional data = get_data(__file__) @@ -17,11 +19,74 @@ for i, line in enumerate(data.splitlines()): ps.remove((x, y, z)) else: assert False + print(len(ps)) -# For part 1, we were limited to an area of -50..50 for all three directions. -# For part 2, this limitation is no longer in place. That means we cannot keep -# track of all the points individually. Instead, it will be necessary. To keep -# track of regions. Ultimately, we then have to perform arithmetic on the -# regions. This is not trivial enough for me right now. +@dataclass +class Line: + a: int + b: int + + def intersects(self, other: "Line") -> bool: + return self.b > other.a and self.a < other.b + + def intersection(self, other: "Line") -> Optional["Line"]: + if not self.intersects(other): + return None + return Line(max(self.a, other.a), min(self.b, other.b)) + + def length(self) -> int: + assert self.b > self.a + return self.b - self.a + 1 + + +@dataclass(frozen=True) +class Cube: + on: bool + x: Line + y: Line + z: Line + + @classmethod + def from_line(cls, line: str) -> "Cube": + xs = ints(line) + assert len(xs) == 6 + lines = [Line(*xs[i:i+2]) for i in range(0, len(xs), 2)] + on = True if line.startswith("on") else False + return Cube(on, *lines) + + def volume(self) -> int: + return (self.x.length() * self.y.length() * self.z.length()) * (1 if self.on else -1) + + def intersects(self, other: "Cube") -> bool: + return ( + self.x.intersects(other.x) + and self.y.intersects(other.y) + and self.z.intersects(other.z) + ) + + def intersection(self, other: "Cube") -> Optional["Cube"]: + x = self.x.intersection(other.x) + y = self.y.intersection(other.y) + z = self.z.intersection(other.z) + if x is None or y is None or z is None: + return None + return Cube(not self.on, x, y, z) + + +# Inclusion/exclusion principle from set theory +cubes = [] +for line in data.splitlines(): + cc = Cube.from_line(line) + ncubes = [] + for i in range(len(cubes)): + c = cubes[i] + ic = c.intersection(cc) + if ic is not None: + cubes.append(ic) + + if cc.on: + cubes.append(cc) + +print(sum(c.volume() for c in cubes)) diff --git a/README.md b/README.md index da35e5e..49df1fd 100644 --- a/README.md +++ b/README.md @@ -193,7 +193,7 @@ Solutions and utility script for Advent of Code challenges in Python. - Day 19: - Day 20: 105:00 (That wasn't easy but was able to solve in one go.) - Day 21: 37:45 (Wasn't hard but I was just too slow.) -- Day 22: +- Day 22: 142:00 (Wonderful problem and hard for me but learned something new for sure.) - Day 23: 81:38 (Fun but obviously not competitive.) - Day 24: 232:00 (Super hard for me but I am proud of it.) - Day 25: 15:43 (That could have been much much faster.)