2023 day 22.
This commit is contained in:
parent
c496ea25c9
commit
af2fbf2da1
@ -1,13 +1,111 @@
|
|||||||
|
import itertools
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import string
|
||||||
import sys
|
import sys
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from dataclasses import dataclass
|
|
||||||
|
VERBOSE = os.getenv("AOC_VERBOSE") == "True"
|
||||||
|
logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING)
|
||||||
|
|
||||||
|
|
||||||
lines = sys.stdin.read().splitlines()
|
lines = sys.stdin.read().splitlines()
|
||||||
|
|
||||||
|
|
||||||
|
def _name(i: int) -> str:
|
||||||
|
if len(lines) < 26:
|
||||||
|
return string.ascii_uppercase[i]
|
||||||
|
return f"B{i:04d}"
|
||||||
|
|
||||||
|
|
||||||
|
def build_supports(
|
||||||
|
bricks: list[tuple[tuple[int, int, int], tuple[int, int, int]]]
|
||||||
|
) -> tuple[dict[int, set[int]], dict[int, set[int]]]:
|
||||||
|
# 1. compute locations where a brick of sand will land after falling by processing
|
||||||
|
# them in sorted order of bottom z location
|
||||||
|
levels: dict[tuple[int, int, int], int] = defaultdict(lambda: -1)
|
||||||
|
for i_brick, ((sx, sy, sz), (ex, ey, ez)) in enumerate(bricks):
|
||||||
|
assert sx <= ex and sy <= ey and sz <= ez
|
||||||
|
|
||||||
|
xs, ys = range(sx, ex + 1), range(sy, ey + 1)
|
||||||
|
|
||||||
|
for z in range(sz - 1, 0, -1):
|
||||||
|
if any(levels[x, y, z] >= 0 for x, y in itertools.product(xs, ys)):
|
||||||
|
break
|
||||||
|
sz, ez = sz - 1, ez - 1
|
||||||
|
|
||||||
|
bricks[i_brick] = ((sx, sy, sz), (ex, ey, ez))
|
||||||
|
zs = range(sz, ez + 1)
|
||||||
|
|
||||||
|
for x, y, z in itertools.product(xs, ys, zs):
|
||||||
|
levels[x, y, z] = i_brick
|
||||||
|
|
||||||
|
# 2. compute the bricks that supports any brick
|
||||||
|
supported_by: dict[int, set[int]] = {}
|
||||||
|
supports: dict[int, set[int]] = {i_brick: set() for i_brick in range(len(bricks))}
|
||||||
|
for i_brick, ((sx, sy, sz), (ex, ey, ez)) in enumerate(bricks):
|
||||||
|
name = _name(i_brick)
|
||||||
|
|
||||||
|
supported_by[i_brick] = {
|
||||||
|
v
|
||||||
|
for x, y in itertools.product(range(sx, ex + 1), range(sy, ey + 1))
|
||||||
|
if (v := levels[x, y, sz - 1]) != -1
|
||||||
|
}
|
||||||
|
logging.info(
|
||||||
|
f"{name} supported by {', '.join(map(_name, supported_by[i_brick]))}"
|
||||||
|
)
|
||||||
|
|
||||||
|
for support in supported_by[i_brick]:
|
||||||
|
supports[support].add(i_brick)
|
||||||
|
|
||||||
|
return supported_by, supports
|
||||||
|
|
||||||
|
|
||||||
|
bricks: list[tuple[tuple[int, int, int], tuple[int, int, int]]] = []
|
||||||
|
for line in lines:
|
||||||
|
bricks.append(
|
||||||
|
(
|
||||||
|
tuple(int(c) for c in line.split("~")[0].split(",")), # type: ignore
|
||||||
|
tuple(int(c) for c in line.split("~")[1].split(",")), # type: ignore
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# sort bricks by bottom z position to compute supports
|
||||||
|
bricks = sorted(bricks, key=lambda b: b[0][-1])
|
||||||
|
supported_by, supports = build_supports(bricks)
|
||||||
|
|
||||||
# part 1
|
# part 1
|
||||||
answer_1 = ...
|
answer_1 = len(bricks) - sum(
|
||||||
|
any(len(supported_by[supported]) == 1 for supported in supports_to)
|
||||||
|
for supports_to in supports.values()
|
||||||
|
)
|
||||||
print(f"answer 1 is {answer_1}")
|
print(f"answer 1 is {answer_1}")
|
||||||
|
|
||||||
# part 2
|
# part 2
|
||||||
answer_2 = ...
|
falling_in_chain: dict[int, set[int]] = {}
|
||||||
|
for i_brick in range(len(bricks)):
|
||||||
|
to_disintegrate: set[int] = {
|
||||||
|
supported
|
||||||
|
for supported in supports[i_brick]
|
||||||
|
if len(supported_by[supported]) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
supported_by_copy = dict(supported_by)
|
||||||
|
|
||||||
|
falling_in_chain[i_brick] = set()
|
||||||
|
while to_disintegrate:
|
||||||
|
falling_in_chain[i_brick].update(to_disintegrate)
|
||||||
|
|
||||||
|
to_disintegrate_v: set[int] = set()
|
||||||
|
|
||||||
|
for d_brick in to_disintegrate:
|
||||||
|
for supported in supports[d_brick]:
|
||||||
|
supported_by_copy[supported] = supported_by_copy[supported] - {d_brick}
|
||||||
|
|
||||||
|
if not supported_by_copy[supported]:
|
||||||
|
to_disintegrate_v.add(supported)
|
||||||
|
|
||||||
|
to_disintegrate = to_disintegrate_v
|
||||||
|
|
||||||
|
answer_2 = sum(len(falling) for falling in falling_in_chain.values())
|
||||||
print(f"answer 2 is {answer_2}")
|
print(f"answer 2 is {answer_2}")
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,7 @@
|
|||||||
|
1,0,1~1,2,1
|
||||||
|
0,0,2~2,0,2
|
||||||
|
0,2,3~2,2,3
|
||||||
|
0,0,4~0,2,4
|
||||||
|
2,0,5~2,2,5
|
||||||
|
0,1,6~2,1,6
|
||||||
|
1,1,8~1,1,9
|
Loading…
Reference in New Issue
Block a user