Do day 20.
This commit is contained in:
121
d22.py
Normal file
121
d22.py
Normal file
@@ -0,0 +1,121 @@
|
||||
from lib import *
|
||||
|
||||
EXAMPLE = """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
|
||||
"""
|
||||
|
||||
def overlap(a, b):
|
||||
# Could be made generic with for loop.
|
||||
[(ax1, ax2), (ay1, ay2), (az1, az2)] = a
|
||||
[(bx1, bx2), (by1, by2), (bz1, bz2)] = b
|
||||
xo = (bx1 <= ax2 and bx2 >= ax1)
|
||||
yo = (by1 <= ay2 and by2 >= ay1)
|
||||
zo = (bz1 <= az2 and bz2 >= az1)
|
||||
return xo and yo and zo
|
||||
|
||||
def can_overlap(a, b):
|
||||
a, b = list(a), list(b)
|
||||
za1, za2 = a[2]
|
||||
zb1, zb2 = b[2]
|
||||
|
||||
zad = za1 - 1
|
||||
a[2] = (za1 - zad, za2 - zad)
|
||||
|
||||
zbd = zb1 - 1
|
||||
b[2] = (zb1 - zbd, zb2 - zbd)
|
||||
return overlap(a, b)
|
||||
|
||||
def solve(input: Input, second=False):
|
||||
bricks = []
|
||||
for line in input.lines():
|
||||
s = tuple(str_to_ints(line))
|
||||
s = tuple([tuple(sorted([s[i], s[i + 3]])) for i in range(3)])
|
||||
bricks.append(s)
|
||||
|
||||
# Check which bricks can overlap in general
|
||||
d = {i: [] for i in range(len(bricks))}
|
||||
for a in range(len(bricks)):
|
||||
for b in range(a + 1, len(bricks)):
|
||||
if can_overlap(bricks[a], bricks[b]):
|
||||
# print(f"{bricks[a]} can overlap with {bricks[b]}")
|
||||
d[a].append(b)
|
||||
d[b].append(a)
|
||||
|
||||
# Lower bricks as much as possible
|
||||
idxs = sorted(range(len(bricks)), key=lambda i: bricks[i][2][1])
|
||||
for idx_active_idx, idx_active in enumerate(idxs):
|
||||
b = list(bricks[idx_active])
|
||||
lowest_z = 1
|
||||
for idx_to_check in idxs[:idx_active_idx]:
|
||||
if idx_to_check in d[idx_active]:
|
||||
lowest_z = max(lowest_z, bricks[idx_to_check][2][1] + 1)
|
||||
zp = list(b[2])
|
||||
zp[0], zp[1] = lowest_z, zp[1] - (zp[0] - lowest_z)
|
||||
b[2] = tuple(zp)
|
||||
# print(f"{bricks[idx_active]} -> {b}")
|
||||
bricks[idx_active] = b
|
||||
|
||||
# for l, b in zip(LETTERS_UPPER, bricks): print(l, b)
|
||||
|
||||
if second:
|
||||
# Create a map that for each objects, shows what objects it is
|
||||
# supported by.
|
||||
supported_by = {i: set() for i in range(len(bricks))}
|
||||
for i in range(len(bricks)):
|
||||
b = bricks[i]
|
||||
zl = b[2][0]
|
||||
if zl == 1:
|
||||
supported_by[i].add(-1)
|
||||
for ni in d[i]:
|
||||
if bricks[ni][2][1] + 1 == zl:
|
||||
supported_by[i].add(ni)
|
||||
|
||||
res = 0
|
||||
for i in range(len(bricks)):
|
||||
removed = set([i])
|
||||
to_process = [i]
|
||||
while to_process:
|
||||
ri = to_process.pop()
|
||||
for ni in d[ri]:
|
||||
if supported_by[ni].issubset(removed) and not ni in removed:
|
||||
removed.add(ni)
|
||||
to_process.append(ni)
|
||||
res += len(removed) - 1
|
||||
return res
|
||||
|
||||
else:
|
||||
support_map = {i: [] for i in range(len(bricks))}
|
||||
support_bricks = set()
|
||||
# For all bricks, check if it would fall if a particular brick was removed.
|
||||
for i in range(len(bricks)):
|
||||
b = bricks[i]
|
||||
zl = b[2][0]
|
||||
if zl == 1:
|
||||
continue # supported by floor
|
||||
|
||||
for ni in d[i]:
|
||||
if bricks[ni][2][1] + 1 == zl:
|
||||
support_map[i].append(ni)
|
||||
if len(support_map[i]) == 1:
|
||||
support_bricks.add(support_map[i][0])
|
||||
# print(f"{bricks[i]} supported by {support_map[i]}")
|
||||
return len(bricks) - len(support_bricks)
|
||||
|
||||
def main():
|
||||
DAY_INPUT = "i22.txt"
|
||||
|
||||
print("Example 1:", solve(Input(EXAMPLE)))
|
||||
print("Solution 1:", solve(Input(DAY_INPUT)))
|
||||
assert solve(Input(DAY_INPUT)) == 428
|
||||
|
||||
print("Example 2:", solve(Input(EXAMPLE), True))
|
||||
print("Solution 2:", solve(Input(DAY_INPUT), True))
|
||||
assert solve(Input(DAY_INPUT), True) == 35654
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user