Part 1: Simulate Conway's Game of Life. Count how many lights are on after 100 iterations.
class Grid(object): def __init__(self, str): self.data = [ [(char == '#') for char in line] for line in str.split() ] self.width = len(self.data) self.height = len(self.data) def __getitem__(self, pt): row, col = pt if 0 <= row < self.height and 0 <= col < self.width: return self.data[row][col] else: return False def neighbors(self, row, col): return sum( (1 if self[row + row_delta, col + col_delta] else 0) for row_delta in range(-1, 2) for col_delta in range(-1, 2) ) - (1 if self[row, col] else 0) def step(self): new_data = copy.deepcopy(self.data) for row in range(self.height): for col in range(self.width): if self[row, col]: new_data[row][col] = (2 <= self.neighbors(row, col) <= 3) else: new_data[row][col] = (self.neighbors(row, col) == 3) self.data = new_data def __repr__(self): return '\n'.join( ''.join( '#' if self[row, col] else '.' for col in range(self.width) ) for row in range(self.height) ) def __len__(self): return sum( 1 if self[row, col] else 0 for row in range(self.height) for col in range(self.width) ) if __name__ == '__main__': grid = Grid(sys.stdin.read()) for i in range(int(sys.argv)): grid.step() print(grid) print(len(grid))
I don’t use actually use object oriented programming that much, but this problem just called for it. It let me abstract away the counting of accessing of elements while dealing with edge cases. Also, it will be even more helpful in part 21.
The three interesting bits are the
step functions. The first is interesting, since it actually takes a tuple as as argument, but looks like it takes two parameters:
grid[row, col] is actually equivalent to
grid[(row, col)], since it’s actually the
, that makes a tuple, not the parenthesis.
neighbors will count up the positive neighboring elements using
__getitem__. We subtract the one at the end if the node is enabled to account for the fact that we counted the node as its own neighbor.
With all of that,
step becomes much easier. We just have to loop and using the
neighbors function. Shiny.
Part 2: Repeat, but this time the four lights in the corners will be stuck on.
This is another reason why I wanted to write this in an object oriented style (in highsight):
part1 = imp.load_source('part1', 'part-1.py') class FixedCornerGrid(part1.Grid): def __getitem__(self, pt): row, col = pt if (row in (0, self.width - 1) and col in (0, self.height - 1)): return True else: return part1.Grid.__getitem__(self, pt) if __name__ == '__main__': grid = FixedCornerGrid(sys.stdin.read()) for i in range(int(sys.argv)): grid.step() print(grid) print(len(grid))
The weirdness comes from the fact that I for some reason decided to use dashes in my filenames. Otherwise, I could just have done
import part1. But other than that, we only have to slightly tweak the
__getitem__ behavior for the four corners. Other than that, we just directly reuse all of the code from the original
I actually really like how this turned out. It brings a slightly new twist to a problem I’ve solved a half dozen or more times before.