advent-of-code/2022/python/day18.py

203 lines
5.4 KiB
Python

import matrix
import shared
from pprint import pprint
from dataclasses import dataclass
from typing import Set, Tuple
from functools import cached_property
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
@dataclass
class Cube:
x: int
y: int
z: int
neighbor_coords: Set[Tuple[int,int,int]] = None
@cached_property
def xyz(self):
return (self.x,self.y,self.z)
def set_neighbor_coords(self):
self.neighbor_coords = get_neighbors(self.x,self.y,self.z)
def get_neighbors(x,y,z):
# Generate the six neighbor_coords
# Look at a die 6 to the left, 2 on top, one on right
offsets = (
(1,0,0), # 1
(0,0,1), # 2
(0,-1,0),# 3
(0,1,0), # 4
(0,0,-1),# 5
(-1,0,0) # 6
)
return set([(x+o[0], y+o[1], z+o[2]) for o in offsets])
def get_flat_neighbors(x,y,z):
# Generate the six neighbor_coords
# Look at a die 6 to the left, 2 on top, one on right
# ignore top and bottom
offsets = (
(1,0,0), # 1
#(0,0,1), # 2
(0,-1,0),# 3
(0,1,0), # 4
#(0,0,-1),# 5
(-1,0,0) # 6
)
return set([(x+o[0], y+o[1], z+o[2]) for o in offsets])
def get_flat_neighbors_from(x,y,z, f):
return get_flat_neighbors(x,y,z).intersection(f)
# @shared.profile
def part1(rows):
cubes = {}
for idx, (x,y,z) in enumerate(rows):
cubes[(x,y,z)] = {'n':get_neighbors(x,y,z), 'not_me': tuple(map(tuple,rows[:idx] + rows[idx+1:]))}
potential = len(cubes) * 6
for coords, cube in cubes.items():
for other in cube['not_me']:
if other in cube['n']:
potential -= 1
print(potential)
def surface_area(rows):
cubes = {}
maxX,maxY,maxZ = 0,0,0
for idx, (x,y,z) in enumerate(rows):
cubes[(x,y,z)] = {'n':get_neighbors(x,y,z), 'not_me': set(map(tuple,rows[:idx] + rows[idx+1:]))}
maxX = max(maxX,x)
maxY = max(maxY,y)
maxZ = max(maxZ,z)
_cubes = frozenset(cubes.keys())
potential = len(cubes) * 6
for coords, cube in cubes.items():
for other in cube['not_me']:
if other in cube['n']:
potential -= 1
return potential, maxX,maxY,maxZ, cubes, _cubes
def part2(rows):
potential, maxX, maxY,maxZ,cubes,_cubes = surface_area(rows)
air = set()
for x in range(0,maxX+1):
for y in range(0,maxY+1):
for z in range(0,maxZ+1):
if (x,y,z) not in _cubes:
air.add((x,y,z))
air -= _cubes # Remove all lava from air
all_air_count = len(air)
all_lava_count = len(_cubes)
possible_count = (maxX+1)*(maxY+1)*(maxZ+1)
print("all_air: ", all_air_count)
print("all_lava:", all_lava_count)
print("possible:", possible_count)
print(maxZ,maxY,maxX)
print()
#shared.render_cubes(maxX+2,maxY+2,maxZ+1, [x for x in _cubes])
air_nx = {}
for a in air:
neighbors = get_neighbors(*a)
air_nx[a] = {
'lava':neighbors & _cubes or None,
'air': neighbors & air or None
}
# loop row by row
inside = []
for z in range(0,maxZ+2):
seen = set()
seen.add((0,0,z))
mx = matrix.matrix_of_size(maxX+2,maxY+2)
for x,y in matrix.spiral_generator(maxX+2, maxY+2):
xyz = x,y,z
mx[y][x] = "#"
if xyz in _cubes:
continue
ns = get_flat_neighbors(x,y,z)
for neigh in ns:
if neigh in seen:
seen.add(xyz)
this_level = set(x for x in air_in_row(air, z))
for (x,y,z) in seen:
mx[y][x] = 0
for x,y,z in this_level:
if (x,y,z) not in seen:
mx[y][x] = "o"
print(matrix.ppmx(mx, pad=False,space=False))
print()
#print(len(this_level), len(seen))
#inside.extend([x for x in this_level if x not in seen])
#print(seen)
#shared.render_cubes(maxX+2,maxY+2,maxZ+1, [x for x in seen])
#shared.render_cubes(maxX+2,maxY+2,maxZ+1, [x for x in this_level-seen])
##print()
##print(inside)
##print()
#actually_inside = []
#for i in inside:
# ns = get_neighbors(*i)
# # check for surrounded 100% by rock
# rock_count = 0
# for n in ns:
# if n in _cubes:
# rock_count +=1
# if rock_count == 6:
# print("in rock")
# actually_inside.append(i)
# continue
# #check for surrounded 100% by air
# air_count = 0
# for n in ns:
# if n in air:
# air_count +=1
# print("in air")
# break
# actually_inside.append(i)
##print(actually_inside)
#tot, _, _, _, _, _ = surface_area(actually_inside)
#print(tot)
#print(potential - tot)
def air_in_row(air, z):
x = []
for a in air:
if a[2] == z:
x.append(a)
return x
def main():
rows = [list(map(int,row.split(","))) for row in shared.load_rows(18)]
with shared.elapsed_timer() as elapsed:
part1(rows)
print("🕒", elapsed())
#rows = [map(int,row.split(",")) for row in shared.load_rows(18)]
with shared.elapsed_timer() as elapsed:
part2(rows)
print("🕒", elapsed())
if __name__ == "__main__":
main()