import matrix import shared from pprint import pprint from dataclasses import dataclass from typing import Set, Tuple from functools import cached_property @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 ) neighbor_coords = set([(x+o[0], y+o[1], z+o[2]) for o in offsets]) return neighbor_coords def count_neighbor_coords(x,y,z): pass # @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 part2(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 air = set() for x in range(0,maxX+1): for y in range(0,maxY+1): for z in range(0,maxZ+1): air.add((x,y,z)) air -= _cubes # Remove all lava from air for a in air: neighbors = get_neighbors(*a) if len(neighbors & _cubes)==6: #print(a, "====", neighbors & _cubes) potential -= 6 # _ns = get_neighbors(x,y,z) # lava_neighbors = _ns & _cubes # if len(lava_neighbors) == 6: # potential -= 6 print(potential) # @shared.profile def _part2(rows): cubes = [] _lava = set() for row in rows: x,y,z = row maxX = max(maxX,x) maxY = max(maxY,y) maxZ = max(maxZ,z) cube = Cube(x=x,y=y,z=z) cube.set_neighbor_coords() cubes.append(cube) _lava.add((x,y,z)) print(maxX,maxY,maxZ) potential = len(cubes) * 6 for idx in range(len(cubes)): cube = cubes[idx] others = cubes[:idx] + cubes[idx+1:] for other in others: if other.xyz in cube.neighbor_coords: potential -= 1 air = [] for x in range(0,maxX+1): for y in range(0,maxY+1): for z in range(0,maxZ+1): lava_count = 0 if (x,y,z) in _lava: continue # is air air_touching_lava = False for _n in _ns: if _n in _lava: lava_count += 1 air_touching_lava = True if air_touching_lava: air.append((x,y,z)) if lava_count == 6: potential -= 6 print(potential, air) 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()