From 1042c2ae34dc3a318ed02e813d924e248bbd16b8 Mon Sep 17 00:00:00 2001 From: Richard Feistenauer Date: Sun, 9 Dec 2018 11:20:16 +0100 Subject: [PATCH] added cell state and display class --- scripts/main_ui.py | 32 +++++--- src/cellular_automaton/ca_cell.py | 19 +---- src/cellular_automaton/ca_cell_state.py | 27 +++++++ src/cellular_automaton/ca_display.py | 80 ++++++++++++++++++++ src/cellular_automaton/cellular_automaton.py | 2 +- 5 files changed, 131 insertions(+), 29 deletions(-) create mode 100644 src/cellular_automaton/ca_cell_state.py create mode 100644 src/cellular_automaton/ca_display.py diff --git a/scripts/main_ui.py b/scripts/main_ui.py index 3024df3..24d5386 100644 --- a/scripts/main_ui.py +++ b/scripts/main_ui.py @@ -7,6 +7,8 @@ import time from cellular_automaton.cellular_automaton import CellularAutomaton from cellular_automaton.ca_rule import Rule from cellular_automaton.ca_neighborhood import MooreNeighborhood, EdgeRule +from cellular_automaton.ca_display import PyGameFor2D +from cellular_automaton.ca_cell_state import CellState class WorldGeneratorWindow: @@ -17,7 +19,7 @@ class WorldGeneratorWindow: pygame.init() pygame.display.set_caption("World Generator") - self.screen = pygame.display.set_mode(self.grid_size) + self.screen = pygame.display.set_mode(self.window_size) self._cellular_automaton = cellular_automaton self.font = pygame.font.SysFont("monospace", 15) @@ -78,25 +80,35 @@ class TestRule(Rule): def evolve_cell(self, cell, neighbors, iteration_index): active = False last_iteration = iteration_index - 1 - if cell.get_status_for_iteration(last_iteration) is None: + if cell.state is None: rand = random.randrange(0, 101, 1) if rand <= 99: - rand = 0 - cell.set_status_for_iteration([rand], iteration_index) - cell.set_status_for_iteration([rand], iteration_index + 1) - if rand != 0: - active = True + cell.state = MyStatus(0) + else: + cell.state = MyStatus(1) cell.set_for_redraw() + active = True elif len(neighbors) == 8: - left_neighbour_status = neighbors[3].get_status_for_iteration(last_iteration) - active = cell.set_status_for_iteration(left_neighbour_status, iteration_index) + left_neighbour_status = neighbors[3].state.get_status_of_iteration(last_iteration) + active = cell.state.set_status_of_iteration(left_neighbour_status, iteration_index) if active: cell.set_for_redraw() return active +class MyStatus(CellState): + def __init__(self, initial_state): + super().__init__(initial_state) + + def get_state_draw_color(self, iteration): + red = 0 + if self._state[iteration % 2][0]: + red = 255 + return [red, 0, 0] + + if __name__ == "__main__": rule = TestRule() ca = CellularAutomaton([400, 400], MooreNeighborhood(EdgeRule.IGNORE_EDGE_CELLS), rule) - ca_window = WorldGeneratorWindow([1000, 730], ca) + ca_window = PyGameFor2D([1000, 730], ca) ca_window.main_loop() diff --git a/src/cellular_automaton/ca_cell.py b/src/cellular_automaton/ca_cell.py index 81a87a7..f1899e6 100644 --- a/src/cellular_automaton/ca_cell.py +++ b/src/cellular_automaton/ca_cell.py @@ -3,7 +3,7 @@ class Cell: self.name = name self.coordinate = coordinate self.neighbours = [] - self._status = [None, None] + self.state = None self._dirty = False def set_neighbours(self, neighbours: list): @@ -20,20 +20,3 @@ class Cell: def release_from_redraw(self): self._dirty = False - - def set_status_of_iteration(self, new_status, iteration): - """ Will set the new status for the iteration modulo two. - :param new_status: The new status to set. - :param iteration: Uses the iteration index, to differ between current and next state. - :return True if status has changed. - """ - self._status[iteration % 2] = new_status - - return self._status[0] != self._status[1] - - def get_status_of_iteration(self, iteration): - """ Will return the status for the iteration modulo two. - :param iteration: Uses the iteration index, to differ between current and next state. - :return The status for this iteration. - """ - return self._status[iteration % 2] diff --git a/src/cellular_automaton/ca_cell_state.py b/src/cellular_automaton/ca_cell_state.py new file mode 100644 index 0000000..0707f5c --- /dev/null +++ b/src/cellular_automaton/ca_cell_state.py @@ -0,0 +1,27 @@ +class CellState: + """ + This is the base class for all cell states. + When using the cellular automaton display, inherit this class and implement get_state_draw_color. + """ + def __init__(self, initial_state): + self._state = [[initial_state], [initial_state]] + + def set_status_of_iteration(self, new_status, iteration): + """ Will set the new status for the iteration modulo two. + :param new_status: The new status to set. + :param iteration: Uses the iteration index, to differ between current and next state. + :return True if status has changed. + """ + self._state[iteration % 2] = new_status + + return self._state[0] != self._state[1] + + def get_status_of_iteration(self, iteration): + """ Will return the status for the iteration modulo two. + :param iteration: Uses the iteration index, to differ between current and next state. + :return The status for this iteration. + """ + return self._state[iteration % 2] + + def get_state_draw_color(self, iteration): + raise NotImplementedError diff --git a/src/cellular_automaton/ca_display.py b/src/cellular_automaton/ca_display.py new file mode 100644 index 0000000..a681658 --- /dev/null +++ b/src/cellular_automaton/ca_display.py @@ -0,0 +1,80 @@ +import pygame +import time +import operator + +from cellular_automaton.cellular_automaton import CellularAutomaton + + +class DisplayFor2D: + def __init__(self, grid_rect: list, cellular_automaton: CellularAutomaton, screen): + self.grid_size = grid_rect[-2:] + self.grid_pos = grid_rect[:2] + self._cellular_automaton = cellular_automaton + self.screen = screen + + self.cell_size = self._calculate_cell_display_size() + + self._surfaces_to_update = [] + + def set_cellular_automaton(self, cellular_automaton): + self._cellular_automaton = cellular_automaton + + def _redraw_cellular_automaton(self): + for cell in self._cellular_automaton.grid.get_cells().values(): + self._redraw_cell(cell) + pygame.display.update(self._surfaces_to_update) + self._surfaces_to_update = [] + + def _redraw_cell(self, cell): + if cell.is_set_for_redrawing(): + cell_color = cell.state.get_state_draw_color(self._cellular_automaton.get_iteration_index()) + surface_pos = self._calculate_cell_position(cell) + surface_pos = list(map(operator.add, surface_pos, self.grid_pos)) + self._surfaces_to_update.append(self.screen.fill(cell_color, (surface_pos, self.cell_size))) + cell.release_from_redraw() + + def _calculate_cell_position(self, cell): + return list(map(operator.mul, self.cell_size, cell.coordinate)) + + def _calculate_cell_display_size(self): + grid_dimension = self._cellular_automaton.grid.get_dimension() + return list(map(operator.truediv, self.grid_size, grid_dimension)) + + +class PyGameFor2D: + def __init__(self, windows_size: list, cellular_automaton: CellularAutomaton): + self.window_size = windows_size + self._cellular_automaton = cellular_automaton + + pygame.init() + pygame.display.set_caption("Cellular Automaton") + self.screen = pygame.display.set_mode(self.window_size) + self.font = pygame.font.SysFont("monospace", 15) + + self.ca_display = DisplayFor2D([0, 30, windows_size[0], windows_size[1]-30], cellular_automaton, self.screen) + + def _print_process_duration(self, time_ca_end, time_ca_start, time_ds_end): + self.screen.fill([0, 0, 0], ((0, 0), (self.window_size[0], 30))) + self._write_text((10, 5), "CA: " + "{0:.4f}".format(time_ca_end - time_ca_start) + "s") + self._write_text((310, 5), "Display: " + "{0:.4f}".format(time_ds_end - time_ca_end) + "s") + + def _write_text(self, pos, text, color=(0, 255, 0)): + label = self.font.render(text, 1, color) + update_rect = self.screen.blit(label, pos) + pygame.display.update(update_rect) + + def main_loop(self): + running = True + + while running: + time_ca_start = time.time() + self._cellular_automaton.evolve_x_times(1) + time_ca_end = time.time() + self.ca_display._redraw_cellular_automaton() + time_ds_end = time.time() + self._print_process_duration(time_ca_end, time_ca_start, time_ds_end) + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + diff --git a/src/cellular_automaton/cellular_automaton.py b/src/cellular_automaton/cellular_automaton.py index 3ce68c2..b433bfe 100644 --- a/src/cellular_automaton/cellular_automaton.py +++ b/src/cellular_automaton/cellular_automaton.py @@ -59,4 +59,4 @@ class CellularAutomaton: active = self.evolution_rule.evolve_cell(cell_info[0], cell_info[1], self.iteration) if active: - self.grid.set_cells_active(cell_info[0] + cell_info[1]) + self.grid.set_cells_active([cell_info[0]] + cell_info[1])