fixed tests and refactored cellstate

This commit is contained in:
Richard Feistenauer 2019-02-09 19:06:18 +01:00
parent 823fdbc307
commit b0a8fb05a4
9 changed files with 46 additions and 54 deletions

View File

@ -15,3 +15,6 @@ SIZE: 20.2575MB # Factory instead of grid
TOTAL TIME: 0.1152s # dict instead of list for cells TOTAL TIME: 0.1152s # dict instead of list for cells
SIZE: 20.2575MB # process size 53 / 75,8 SIZE: 20.2575MB # process size 53 / 75,8
TOTAL TIME: 0.1135s # evolve is static
SIZE: 20.2575MB # process size 50.8 / 76 (no more cell objects in processes)

View File

@ -48,7 +48,7 @@ if __name__ == "__main__":
freeze_support() freeze_support()
random.seed(1000) random.seed(1000)
# best is 400/400 with 0,2 ca speed and 0,09 redraw # best single is 400/400 with 0,2 ca speed and 0,09 redraw / multi is 300/300 with 0.083
neighborhood = MooreNeighborhood(EdgeRule.FIRST_AND_LAST_CELL_OF_DIMENSION_ARE_NEIGHBORS) neighborhood = MooreNeighborhood(EdgeRule.FIRST_AND_LAST_CELL_OF_DIMENSION_ARE_NEIGHBORS)
ca = make_cellular_automaton(dimension=[100, 100], neighborhood=neighborhood, rule=TestRule(), state_class=MyState) ca = make_cellular_automaton(dimension=[100, 100], neighborhood=neighborhood, rule=TestRule(), state_class=MyState)
ca_window = PyGameFor2D(window_size=[1000, 800], cellular_automaton=ca) ca_window = PyGameFor2D(window_size=[1000, 800], cellular_automaton=ca)

Binary file not shown.

View File

@ -4,33 +4,21 @@ from typing import Type
class Cell: class Cell:
def __init__(self, state_class: Type[CellState], coordinate): def __init__(self, state_class: Type[CellState], coordinate):
self._coordinate = coordinate self.coordinate = coordinate
self.state = state_class() self.state = state_class()
self.neighbours = [] self.neighbours = []
def set_neighbours(self, neighbours): @staticmethod
self.neighbours = neighbours def evolve_if_ready(cell, rule, iteration):
if cell[0].is_active(iteration):
new_state = rule(cell[0].get_state_of_last_iteration(iteration),
[n.get_state_of_last_iteration(iteration) for n in cell[1]])
Cell.set_new_state_and_activate(cell, new_state, iteration)
def get_state(self): @staticmethod
return self.state def set_new_state_and_activate(cell, new_state: CellState, iteration):
changed = cell[0].set_state_of_iteration(new_state, iteration)
def get_coordinate(self):
return self._coordinate
def evolve_if_ready(self, rule, iteration):
if self.state.is_active(iteration):
new_state = rule(self.state.get_state_of_last_iteration(iteration), self.get_neighbour_states(iteration))
self.set_new_state_and_activate(new_state, iteration)
def get_neighbour_states(self, index):
return [n.get_state_of_last_iteration(index) for n in self.neighbours]
def set_new_state_and_activate(self, new_state: CellState, iteration):
changed = self.state.set_state_of_iteration(new_state, iteration)
if changed: if changed:
self._set_active_for_next_iteration(iteration) cell[0].set_active_for_next_iteration(iteration)
for n in cell[1]:
def _set_active_for_next_iteration(self, iteration):
self.state.set_active_for_next_iteration(iteration)
for n in self.neighbours:
n.set_active_for_next_iteration(iteration) n.set_active_for_next_iteration(iteration)

View File

@ -17,16 +17,6 @@ class CellState:
else: else:
self._dirty = RawValue(c_bool, False) self._dirty = RawValue(c_bool, False)
def get_state_of_iteration(self, iteration):
""" Will return the state for the iteration modulo number of saved states.
:param iteration: Uses the iteration index, to differ between concurrent states.
:return The state for this iteration.
"""
return self._state_slots[self.__calculate_slot(iteration)]
def __calculate_slot(self, iteration):
return iteration % self.__class__._state_save_slot_count
def is_active(self, iteration): def is_active(self, iteration):
return self._active[self.__calculate_slot(iteration)] return self._active[self.__calculate_slot(iteration)]
@ -42,6 +32,13 @@ class CellState:
def get_state_of_last_iteration(self, current_iteration_index): def get_state_of_last_iteration(self, current_iteration_index):
return self.get_state_of_iteration(current_iteration_index - 1) return self.get_state_of_iteration(current_iteration_index - 1)
def get_state_of_iteration(self, iteration):
""" Will return the state for the iteration modulo number of saved states.
:param iteration: Uses the iteration index, to differ between concurrent states.
:return The state for this iteration.
"""
return self._state_slots[self.__calculate_slot(iteration)]
def set_state_of_iteration(self, new_state, iteration): def set_state_of_iteration(self, new_state, iteration):
""" Will set the new state for the iteration modulo number of saved states. """ Will set the new state for the iteration modulo number of saved states.
:param new_state: The new state to set. :param new_state: The new state to set.
@ -66,3 +63,7 @@ class CellState:
def get_state_draw_color(self, iteration): def get_state_draw_color(self, iteration):
raise NotImplementedError raise NotImplementedError
@classmethod
def __calculate_slot(cls, iteration):
return iteration % cls._state_save_slot_count

View File

@ -92,13 +92,13 @@ class PyGameFor2D:
def _cell_redraw_rectangles(cells, evolution_index, display_info): def _cell_redraw_rectangles(cells, evolution_index, display_info):
for cell in cells: for cell in cells:
if cell.get_state().is_set_for_redraw(): if cell.state.is_set_for_redraw():
cell_color = cell.get_state().get_state_draw_color(evolution_index) cell_color = cell.state.get_state_draw_color(evolution_index)
cell_pos = _calculate_cell_position(display_info.cell_size, cell) cell_pos = _calculate_cell_position(display_info.cell_size, cell)
surface_pos = list(map(operator.add, cell_pos, display_info.grid_pos)) surface_pos = list(map(operator.add, cell_pos, display_info.grid_pos))
yield display_info.screen.fill(cell_color, (surface_pos, display_info.cell_size)) yield display_info.screen.fill(cell_color, (surface_pos, display_info.cell_size))
cell.get_state().was_redrawn() cell.state.was_redrawn()
def _calculate_cell_position(cell_size, cell): def _calculate_cell_position(cell_size, cell):
return list(map(operator.mul, cell_size, cell.get_coordinate())) return list(map(operator.mul, cell_size, cell.coordinate))

View File

@ -25,9 +25,9 @@ class CAFactory:
@staticmethod @staticmethod
def _apply_neighbourhood_to_cells(cells, neighborhood, dimension): def _apply_neighbourhood_to_cells(cells, neighborhood, dimension):
for cell in cells.values(): for cell in cells.values():
n_coordinates = neighborhood.calculate_cell_neighbor_coordinates(cell.get_coordinate(), n_coordinates = neighborhood.calculate_cell_neighbor_coordinates(cell.coordinate,
dimension) dimension)
cell.set_neighbours([cells[_join_coordinate(coordinate)].get_state() for coordinate in n_coordinates]) cell.neighbours = [cells[_join_coordinate(coordinate)].state for coordinate in n_coordinates]
def _join_coordinate(coordinate): def _join_coordinate(coordinate):

View File

@ -1,6 +1,7 @@
import multiprocessing import multiprocessing
from cellular_automaton.ca_rule import Rule from cellular_automaton.ca_rule import Rule
from cellular_automaton.ca_cell import Cell
class CellularAutomaton: class CellularAutomaton:
@ -14,7 +15,7 @@ class CellularAutomaton:
class CellularAutomatonProcessor: class CellularAutomatonProcessor:
def __init__(self, cellular_automaton, process_count: int = 1): def __init__(self, cellular_automaton, process_count: int = 1):
self.ca = cellular_automaton self.ca = cellular_automaton
cells = {i: self.ca.cells[i] for i in range(len(self.ca.cells))} cells = {i: (c.state, c.neighbours) for i, c in enumerate(self.ca.cells)}
self.evolve_range = range(len(self.ca.cells)) self.evolve_range = range(len(self.ca.cells))
self.pool = multiprocessing.Pool(processes=process_count, self.pool = multiprocessing.Pool(processes=process_count,
initializer=_init_process, initializer=_init_process,
@ -22,7 +23,7 @@ class CellularAutomatonProcessor:
self.ca.evolution_rule, self.ca.evolution_rule,
self.ca.evolution_iteration_index)) self.ca.evolution_iteration_index))
for cell in self.ca.cells: for cell in self.ca.cells:
cell.set_neighbours(None) del cell.neighbours
def evolve_x_times(self, x): def evolve_x_times(self, x):
for x in range(x): for x in range(x):
@ -46,5 +47,5 @@ def _init_process(cells, rule, index):
def _process_routine(i): def _process_routine(i):
global_cells[i].evolve_if_ready(global_rule.evolve_cell, global_iteration.value) Cell.evolve_if_ready(global_cells[i], global_rule.evolve_cell, global_iteration.value)

View File

@ -1,26 +1,25 @@
import sys import sys
sys.path.append('../src') sys.path.append('../src')
import cellular_automaton.ca_cell_state as cas from cellular_automaton import *
import cellular_automaton.ca_cell as cac
import unittest import unittest
class TestState(cas.CellState): class TestState(CellState):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
class TestCellState(unittest.TestCase): class TestCellState(unittest.TestCase):
def setUp(self): def setUp(self):
self.cell = cac.Cell(TestState, []) self.cell = [TestState(), []]
self.neighbours = [TestState() for x in range(5)] self.neighbours = [TestState() for x in range(5)]
for neighbour in self.neighbours: for neighbour in self.neighbours:
neighbour.set_state_of_iteration((0, ), 0) neighbour.set_state_of_iteration((0, ), 0)
self.cell.set_neighbours(self.neighbours) self.cell[1] = self.neighbours
def cell_and_neighbours_active(self, iteration): def cell_and_neighbours_active(self, iteration):
self.neighbours.append(self.cell.get_state()) self.neighbours.append(self.cell[0])
all_active = True all_active = True
for state in self.neighbours: for state in self.neighbours:
if not state.is_active(iteration): if not state.is_active(iteration):
@ -28,12 +27,12 @@ class TestCellState(unittest.TestCase):
return all_active return all_active
def test_evolve_activation(self): def test_evolve_activation(self):
self.cell.evolve_if_ready((lambda a, b: (1,)), 0) Cell.evolve_if_ready(self.cell, (lambda a, b: (1,)), 0)
all_active = self.cell_and_neighbours_active(1) all_active = self.cell_and_neighbours_active(1)
self.assertTrue(all_active) self.assertTrue(all_active)
def test_evolve_activation_on_no_change(self): def test_evolve_activation_on_no_change(self):
self.cell.evolve_if_ready((lambda a, b: (0,)), 0) Cell.evolve_if_ready(self.cell, (lambda a, b: (0,)), 0)
all_active = self.cell_and_neighbours_active(1) all_active = self.cell_and_neighbours_active(1)
self.assertFalse(all_active) self.assertFalse(all_active)