2022-12-18 05:28:24 +00:00
|
|
|
import matrix
|
|
|
|
import shared
|
2022-12-18 06:36:15 +00:00
|
|
|
from pprint import pprint
|
2022-12-18 05:28:24 +00:00
|
|
|
from dataclasses import dataclass
|
|
|
|
from typing import Set, Tuple
|
|
|
|
from functools import cached_property
|
2022-12-18 16:02:25 +00:00
|
|
|
from mpl_toolkits.mplot3d import Axes3D
|
|
|
|
import numpy as np
|
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
|
|
|
|
|
2022-12-18 05:28:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class Cube:
|
|
|
|
x: int
|
|
|
|
y: int
|
|
|
|
z: int
|
2022-12-18 05:57:30 +00:00
|
|
|
neighbor_coords: Set[Tuple[int,int,int]] = None
|
2022-12-18 05:28:24 +00:00
|
|
|
|
|
|
|
@cached_property
|
|
|
|
def xyz(self):
|
|
|
|
return (self.x,self.y,self.z)
|
|
|
|
|
2022-12-18 05:57:30 +00:00
|
|
|
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
|
2022-12-18 05:28:24 +00:00
|
|
|
# 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
|
|
|
|
)
|
2022-12-18 16:02:25 +00:00
|
|
|
return set([(x+o[0], y+o[1], z+o[2]) for o in offsets])
|
2022-12-18 05:28:24 +00:00
|
|
|
|
2022-12-18 16:02:25 +00:00
|
|
|
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])
|
2022-12-18 05:57:30 +00:00
|
|
|
|
2022-12-18 16:02:25 +00:00
|
|
|
def get_flat_neighbors_from(x,y,z, f):
|
|
|
|
return get_flat_neighbors(x,y,z).intersection(f)
|
2022-12-18 05:28:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
# @shared.profile
|
|
|
|
def part1(rows):
|
2022-12-18 06:36:15 +00:00
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-12-18 16:02:25 +00:00
|
|
|
def surface_area(rows):
|
2022-12-18 06:36:15 +00:00
|
|
|
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())
|
2022-12-18 05:28:24 +00:00
|
|
|
|
2022-12-18 05:57:30 +00:00
|
|
|
potential = len(cubes) * 6
|
2022-12-18 06:36:15 +00:00
|
|
|
for coords, cube in cubes.items():
|
|
|
|
for other in cube['not_me']:
|
|
|
|
if other in cube['n']:
|
2022-12-18 05:57:30 +00:00
|
|
|
potential -= 1
|
2022-12-18 16:02:25 +00:00
|
|
|
return potential, maxX,maxY,maxZ, cubes, _cubes
|
|
|
|
|
|
|
|
|
|
|
|
def part2(rows):
|
|
|
|
potential, maxX, maxY,maxZ,cubes,_cubes = surface_area(rows)
|
|
|
|
|
2022-12-18 06:36:15 +00:00
|
|
|
|
|
|
|
air = set()
|
|
|
|
for x in range(0,maxX+1):
|
|
|
|
for y in range(0,maxY+1):
|
|
|
|
for z in range(0,maxZ+1):
|
2022-12-18 16:02:25 +00:00
|
|
|
if (x,y,z) not in _cubes:
|
|
|
|
air.add((x,y,z))
|
2022-12-18 06:36:15 +00:00
|
|
|
air -= _cubes # Remove all lava from air
|
2022-12-18 16:02:25 +00:00
|
|
|
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()
|
2022-12-18 17:06:24 +00:00
|
|
|
#shared.render_cubes(maxX+2,maxY+2,maxZ+1, [x for x in _cubes])
|
2022-12-18 06:36:15 +00:00
|
|
|
|
2022-12-18 16:02:25 +00:00
|
|
|
air_nx = {}
|
2022-12-18 06:36:15 +00:00
|
|
|
for a in air:
|
|
|
|
neighbors = get_neighbors(*a)
|
2022-12-18 16:02:25 +00:00
|
|
|
air_nx[a] = {
|
|
|
|
'lava':neighbors & _cubes or None,
|
|
|
|
'air': neighbors & air or None
|
|
|
|
}
|
|
|
|
|
|
|
|
# loop row by row
|
|
|
|
inside = []
|
2022-12-18 17:06:24 +00:00
|
|
|
for z in range(0,maxZ+2):
|
2022-12-18 16:02:25 +00:00
|
|
|
seen = set()
|
|
|
|
seen.add((0,0,z))
|
2022-12-18 17:06:24 +00:00
|
|
|
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)
|
2022-12-18 16:02:25 +00:00
|
|
|
|
|
|
|
def air_in_row(air, z):
|
|
|
|
x = []
|
|
|
|
for a in air:
|
|
|
|
if a[2] == z:
|
|
|
|
x.append(a)
|
|
|
|
return x
|
2022-12-18 05:28:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
2022-12-18 06:36:15 +00:00
|
|
|
rows = [list(map(int,row.split(","))) for row in shared.load_rows(18)]
|
2022-12-18 05:28:24 +00:00
|
|
|
with shared.elapsed_timer() as elapsed:
|
|
|
|
part1(rows)
|
|
|
|
print("🕒", elapsed())
|
2022-12-18 06:36:15 +00:00
|
|
|
#rows = [map(int,row.split(",")) for row in shared.load_rows(18)]
|
2022-12-18 05:28:24 +00:00
|
|
|
with shared.elapsed_timer() as elapsed:
|
|
|
|
part2(rows)
|
|
|
|
print("🕒", elapsed())
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|