def start(self): self.screen.fill((0, 0, 0)) self.grid = Grid(self.width, self.height) self.rows_range = range(self.grid.height) self.cols_range = range(self.grid.width) # self._random_populated_cells(self.width, self.height) self._create_r_pentomino(63, 30) self._create_r_pentomino(63, 60)
def test_correctly_snapshot_ticks_a_3x3_grid_with_centered_cell_and_2_adjacent_cells(): grid = Grid(width=3, height=3) grid.populate_cells([(0, 0), (0, 1), (1, 0)]) grid.advance_generation() assert len(grid.matrix) == 3*3 # space at position (1,1) spawned a cell as it has 3 adjancent assert grid.matrix == [1, 1, 0, 1, 1, 0, 0, 0, 0]
def test_on_a_2x2_grid_populates_some_positions(): grid = Grid(width=2, height=2) grid.populate_cells([(0, 1), (1, 1)]) assert grid.get_cell(0, 0) == 0 assert grid.get_cell(0, 1) == 1 assert grid.get_cell(1, 0) == 0 assert grid.get_cell(1, 1) == 1
def test_indexes_swap_when_updating_the_linebuffer(): irrelevant_y_coordinate = 1 grid = Grid(width=4, height=3) grid._update_linebuffer(irrelevant_y_coordinate) assert grid.linebuffer_index == 0 assert grid.linebuffer_prev_index == 1 grid._update_linebuffer(irrelevant_y_coordinate) assert grid.linebuffer_index == 1 assert grid.linebuffer_prev_index == 0 grid._update_linebuffer(irrelevant_y_coordinate) assert grid.linebuffer_index == 0 assert grid.linebuffer_prev_index == 1
def test_fills_current_index_with_y_coordinate_line(): grid = Grid(width=3, height=3) setup_grid_cells(grid) # this also works as edge-detection case grid._update_linebuffer(0) assert grid.linebuffer[grid.linebuffer_index] == [1, 0, 0] # the other line should be empty grid._update_linebuffer(1) assert grid.linebuffer[grid.linebuffer_index] == [0, 1, 0] assert grid.linebuffer[grid.linebuffer_prev_index] == [1, 0, 0] grid._update_linebuffer(2) assert grid.linebuffer[grid.linebuffer_index] == [0, 0, 1] assert grid.linebuffer[grid.linebuffer_prev_index] == [0, 1, 0]
def test_its_data_becomes_invalid_if_updates_are_not_y_coordinate_consecutive( ): grid = Grid(width=3, height=3) setup_grid_cells(grid) # all fine so far grid._update_linebuffer(0) assert grid.linebuffer[grid.linebuffer_index] == [1, 0, 0] grid._update_linebuffer(2) assert grid.linebuffer[grid.linebuffer_index] == [0, 0, 1] # this'd be the correct value assert grid.linebuffer[grid.linebuffer_prev_index] != [0, 1, 0] # but instead has just the swapped old value, returning wrong data assert grid.linebuffer[grid.linebuffer_prev_index] == [1, 0, 0]
def test_creates_a_new_grid_filled_with_empty_cells(): grid = Grid(width=3, height=3) for cell in grid.matrix: assert cell == 0
def test_linebuffer_indexes_start_with_current_1_and_previous_0(): grid = Grid(width=4, height=3) assert grid.linebuffer_index == 1 assert grid.linebuffer_prev_index == 0
def test_setups_an_NxM_grid_with_width_N_4_and_height_M_3(): grid = Grid(width=4, height=3) assert len(grid.matrix) == 3*4
def test_on_a_3x3_grid_a_centered_cell_survives_if_has_2_adjacent_cells(): grid = Grid(width=3, height=3) grid.populate_cells([(1, 1), (0, 1), (1, 2)]) grid.advance_generation() assert grid.get_cell(1, 1) == 1
def test_on_a_1x1_grid_evolution_calculations_dont_error(): grid = Grid(width=1, height=1) grid.advance_generation() assert grid.get_cell(0, 0) == 0
class GameOfLife(): def __init__(self, width, height, fullscreen=False): self.width = width self.height = height self.screen = pygame.display.set_mode((width, height)) self.paused = True if fullscreen: pygame.display.toggle_fullscreen() def start(self): self.screen.fill((0, 0, 0)) self.grid = Grid(self.width, self.height) self.rows_range = range(self.grid.height) self.cols_range = range(self.grid.width) # self._random_populated_cells(self.width, self.height) self._create_r_pentomino(63, 30) self._create_r_pentomino(63, 60) def run(self): clock = pygame.time.Clock() self.screen.fill((0, 0, 0)) self._draw_grid() pygame.display.flip() while True: for event in pygame.event.get(): if event.type == QUIT: sys.exit() self._check_keyboard() if not self.paused: self._update_grid() clock.tick(30) def _draw_grid(self): for y in self.rows_range: for x in self.cols_range: if self.grid.get_cell(x, y) == 1: self.screen.set_at((x, y), (0, 255, 0)) def _update_grid(self): self.grid.advance_generation() self.screen.fill((0, 0, 0)) self._draw_grid() pygame.display.flip() def _check_keyboard(self): key = pygame.key.get_pressed() if key[K_ESCAPE]: sys.exit() elif key[K_SPACE]: self.paused = False def _random_populated_cells(self, grid_width, grid_height, num_cells=None): cells = [] if num_cells is None: min_cells = grid_width + grid_height - 1 max_cells = ((grid_width - 1) * (grid_height - 1)) // 4 num_cells = random.randint(min_cells, max_cells) for index in range(num_cells): cells.append((random.randint(0, grid_width - 1), random.randint(0, grid_height - 1))) self.grid.populate_cells(cells) # More patterns here: https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life#Examples_of_patterns def _create_r_pentomino(self, start_x, start_y): cells = [] cells.append((start_x + 1, start_y)) cells.append((start_x + 2, start_y)) cells.append((start_x, start_y + 1)) cells.append((start_x + 1, start_y + 1)) cells.append((start_x + 1, start_y + 2)) self.grid.populate_cells(cells)
Populates top-left corner of grid (X means "previous value"): 1 X X X 1 X X X 1 """ cells = [] cells.append((0, 0)) cells.append((1, 1)) cells.append((2, 2)) grid.populate_cells(cells) with description("Tests related to line buffer optimized grid"): with context("grid setup"): with it("linebuffer is created with appropiate width and always 2 lines"): grid = Grid(width=4, height=3) expect(len(grid.linebuffer)).to(equal(2)) expect(len(grid.linebuffer[0])).to(equal(grid.width)) with it("linebuffer indexes start with current=1, previous=0"): grid = Grid(width=4, height=3) expect(grid.linebuffer_index).to(equal(1)) expect(grid.linebuffer_prev_index).to(equal(0)) with it("when updating the linebuffer, indexes swap"): irrelevant_y_coordinate = 1 grid = Grid(width=4, height=3) grid._update_linebuffer(irrelevant_y_coordinate) expect(grid.linebuffer_index).to(equal(0)) expect(grid.linebuffer_prev_index).to(equal(1))
def test_on_a_1x1_grid_populates_the_position(): grid = Grid(width=1, height=1) assert grid.get_cell(0, 0) == 0 grid.populate_cells([(0, 0)]) assert grid.get_cell(0, 0) == 1
def test_on_a_3x3_grid_a_topleft_cell_survives_if_has_exactly_2_adjacent_cells(): grid = Grid(width=3, height=3) grid.populate_cells([(0, 0), (0, 1), (1, 0)]) grid.advance_generation() assert grid.get_cell(0, 0) == 1
def test_on_a_3x3_grid_a_topleft_cell_dies_if_has_no_adjacent_cells(): grid = Grid(width=3, height=3) grid.populate_cells([(0, 0)]) grid.advance_generation() assert grid.get_cell(0, 0) == 0
def test_on_a_2x2_grid_evolution_calculations_dont_error(): grid = Grid(width=2, height=2) grid.populate_cells([(0, 1)]) grid.advance_generation() assert grid.get_cell(0, 1) == 0
def test_setups_an_square_3x3_grid(): grid = Grid(width=3, height=3) assert len(grid.matrix) == 3*3
def test_on_a_3x3_grid_a_centered_cell_dies_if_has_more_than_3_adjacent_cells(): grid = Grid(width=3, height=3) grid.populate_cells([(1, 0), (1, 1), (2, 0), (2, 1), (2, 2)]) grid.advance_generation() assert grid.get_cell(1, 1) == 0
def test_on_a_3x3_grid_a_topleft_cell_space_doesnt_reproduces_if_has_less_than_3_adjacent_cells(): grid = Grid(width=3, height=3) grid.populate_cells([(0, 1), (1, 0)]) grid.advance_generation() assert grid.get_cell(0, 0) == 0
def test_on_a_3x3_grid_a_centered_cell_space_reproduces_if_has_exactly_3_adjacent_cells(): grid = Grid(width=3, height=3) grid.populate_cells([(0, 1), (1, 2), (2, 2)]) grid.advance_generation() assert grid.get_cell(1, 1) == 1
def test_given_a_1x1_grid_a_full_snapshot_tick_returns_another_1x1_grid(): grid = Grid(width=1, height=1) grid.populate_cells([(0, 0)]) grid.advance_generation() assert len(grid.matrix) == 1 assert grid.matrix[0] == 0
class GameOfLife(): def __init__(self, width, height, fullscreen=False): self.width = width self.height = height self.screen = pygame.display.set_mode((width, height)) self.paused = True if fullscreen: pygame.display.toggle_fullscreen() def start(self): self.screen.fill((0, 0, 0)) self.grid = Grid(self.width, self.height) self.rows_range = range(self.grid.height) self.cols_range = range(self.grid.width) # self._random_populated_cells(self.width, self.height) self._create_r_pentomino(63, 30) self._create_r_pentomino(63, 60) def run(self): clock = pygame.time.Clock() self.screen.fill((0, 0, 0)) self._draw_grid() pygame.display.flip() while self.paused: for event in pygame.event.get(): if event.type == QUIT: sys.exit() self._check_keyboard() while True: for event in pygame.event.get(): if event.type == QUIT: sys.exit() self._check_keyboard() self._update_grid() clock.tick(30) def _draw_grid(self): for y in self.rows_range: for x in self.cols_range: if self.grid.get_cell(x, y) == 1: self.screen.set_at((x, y), (0, 255, 0)) def _update_grid(self): self.grid.advance_generation() self.screen.fill((0, 0, 0)) self._draw_grid() pygame.display.flip() def _check_keyboard(self): key = pygame.key.get_pressed() if key[K_ESCAPE]: sys.exit() elif key[K_SPACE]: self.paused = False def _random_populated_cells(self, grid_width, grid_height, num_cells=None): cells = [] if num_cells is None: min_cells = grid_width + grid_height - 1 max_cells = ((grid_width - 1) * (grid_height - 1)) // 4 num_cells = random.randint(min_cells, max_cells) for index in range(num_cells): cells.append((random.randint(0, grid_width - 1), random.randint(0, grid_height - 1))) self.grid.populate_cells(cells) # More patterns here: https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life#Examples_of_patterns def _create_r_pentomino(self, start_x, start_y): cells = [] cells.append((start_x + 1, start_y)) cells.append((start_x + 2, start_y)) cells.append((start_x, start_y + 1)) cells.append((start_x + 1, start_y + 1)) cells.append((start_x + 1, start_y + 2)) self.grid.populate_cells(cells)
def test_linebuffer_is_created_with_appropiate_width_and_always_2_lines(): grid = Grid(width=4, height=3) assert len(grid.linebuffer) == 2 assert len(grid.linebuffer[0]) == grid.width