Advent of Code 2022 day 14
Relatively straightforward. Decided to keep track of things with classes.
# with open("14_test.txt") as f: with open("14.txt") as f: data = f.read() def sign(x): if x < 0: return -1 return 1 class Node: def __init__(self, data): self.x, self.y = [int(c) for c in data.split(",")] def __str__(self): return f"{self.x}, {self.y}" class Line: def __init__(self, data): self.line = [Node(n) for n in data.split(" -> ")] @property def min_w(self): return min(node.x for node in self.line) @property def max_w(self): return max(node.x for node in self.line) @property def h(self): return max(node.y for node in self.line) def __iter__(self): return iter(self.line) def __getitem__(self, i): return self.line[i] class Grid: def __init__(self, data): self.lines = [Line(l) for l in data.splitlines()] self.min_w = min(line.min_w for line in self.lines) self.max_w = max(line.max_w for line in self.lines) self.h = max(line.h for line in self.lines) self.grid = [ ["." for _ in range(self.max_w-self.min_w+1)] for _ in range(self.h+1) ] for line in self.lines: for node, old_node in zip(line[1:], line): self.fill_line(node, old_node) self.comes_to_rest = True self.sand_start = Node("500, 0") def __str__(self): return "\n".join([ "".join( row ) for row in self.grid ]) def block(self, node, char="#"): self.grid[node.y][node.x-self.min_w] = char def between(self, start, end): if start.x != end.x: return [ Node(f"{i}, {end.y}") for i in range(start.x, end.x, sign(end.x-start.x)) ] return [ Node(f"{end.x}, {j}") for j in range(start.y, end.y, sign(end.y-start.y)) ] def fill_line(self, end, start): self.block(start) self.block(end) for node in self.between(start, end): self.block(node) def at(self, x, y): if x >= self.min_w and y >= 0 and x < self.max_w+1 and y < self.h+1: return self.grid[y][x-self.min_w] return False def speck_movable(self, speck): if ( self.at(x=speck.x, y=speck.y+1) == "." or self.at(x=speck.x-1, y=speck.y+1) == "." or self.at(x=speck.x+1, y=speck.y+1) == "." ): return True return False def move_speck(self, speck): if self.at(x=speck.x, y=speck.y+1) == ".": return Node(f"{speck.x}, {speck.y+1}") elif self.at(x=speck.x-1, y=speck.y+1) == ".": return Node(f"{speck.x-1}, {speck.y+1}") elif self.at(x=speck.x+1, y=speck.y+1) == ".": return Node(f"{speck.x+1}, {speck.y+1}") def sand(self): speck = self.sand_start while self.speck_movable(speck): speck = self.move_speck(speck) if speck.x == self.min_w or speck.x == self.max_w or speck.y == self.h: self.comes_to_rest = False else: self.block(speck, char="o") def answer(self): for i in range(100000): self.sand() print(self) if not self.comes_to_rest: return i grid = Grid(data) print(grid.answer()) # part 2 class Grid2: def __init__(self, data): self.lines = [Line(l) for l in data.splitlines()] self.h = max(line.h for line in self.lines)+2 self.max_w = max(line.max_w for line in self.lines) self.N = 2*self.max_w+1 self.grid = [ ["." for _ in range(self.N)] for _ in range(self.h+1) ] for x in range(self.N): self.grid[-1][x] = "#" for line in self.lines: for node, old_node in zip(line[1:], line): self.fill_line(node, old_node) self.sand_start = Node("500, 0") self.completed = False def __str__(self): return "\n".join([ "".join( row ) for row in self.grid ]) def block(self, node, char="#"): self.grid[node.y][node.x] = char def between(self, start, end): if start.x != end.x: return [ Node(f"{i}, {end.y}") for i in range(start.x, end.x, sign(end.x-start.x)) ] return [ Node(f"{end.x}, {j}") for j in range(start.y, end.y, sign(end.y-start.y)) ] def fill_line(self, end, start): self.block(start) self.block(end) for node in self.between(start, end): self.block(node) def at(self, x, y): if x >= 0 and y >= 0 and x < self.N and y < self.h+1: return self.grid[y][x] return False def speck_movable(self, speck): if ( self.at(x=speck.x, y=speck.y+1) == "." or self.at(x=speck.x-1, y=speck.y+1) == "." or self.at(x=speck.x+1, y=speck.y+1) == "." ): return True return False def move_speck(self, speck): if self.at(x=speck.x, y=speck.y+1) == ".": return Node(f"{speck.x}, {speck.y+1}") elif self.at(x=speck.x-1, y=speck.y+1) == ".": return Node(f"{speck.x-1}, {speck.y+1}") elif self.at(x=speck.x+1, y=speck.y+1) == ".": return Node(f"{speck.x+1}, {speck.y+1}") def sand(self): speck = self.sand_start while self.speck_movable(speck): speck = self.move_speck(speck) if speck.x == self.sand_start.x and speck.y == self.sand_start.y: self.completed = True else: self.block(speck, char="o") def answer(self): for i in range(1, 100000): self.sand() if self.completed: return i grid = Grid2(data) print(grid.answer())