Fix/display stop when done

This commit is contained in:
Richard Feistenauer 2021-01-09 12:51:20 +00:00
parent 9e211aa581
commit 6d1666bb8b
8 changed files with 54 additions and 12 deletions

View File

@ -98,8 +98,12 @@ There ist still quite some work to do.
And for all others, don't hesitate to open issues when you have problems!
## Changelog
#### 1.0.8
- Fixes automaton using edge cells with radius > 1 not working
- Fixes automaton is not stopping after evolution ended
#### 1.0.7
- Fixes automaton ont active on reactivation
- Fixes automaton not active on reactivation
#### 1.0.6
- Fixes reactivation not redrawing all cells

View File

@ -75,15 +75,19 @@ class CellularAutomatonCreator(abc.ABC):
class CellularAutomaton(CellularAutomatonCreator, abc.ABC):
"""
This class represents a cellular automaton.
""" This class represents a cellular automaton.
It can be created with n dimensions and can handle different neighborhood definitions.
:param dimension: Iterable of len = dimensions
(e.g. [4, 3, 3, 3] = 4 x 3 x 3 x 3 cells in a four dimensional cube).
:param neighborhood: Defines which cells are considered neighbors.
It is intended to be uses as base class.
Override `init_cell_state()` to define the state the cell(s) are initiated with.
Override `evolve()` to define the rule that is aplied on every evolution step of this automaton.
"""
def __init__(self, neighborhood: Neighborhood, *args, **kwargs):
""" Initiates a cellular automaton by the use of the `init_cell_state` method.
:param neighborhood: Defines which cells are considered neighbors.
:param dimension: Iterable of len = dimensions
(e.g. [4, 3, 3, 3] = 4 x 3 x 3 x 3 cells in a four dimensional cube).
"""
super().__init__(neighborhood=neighborhood, *args, **kwargs)
self._evolution_step = 0
self._active = True
@ -132,7 +136,7 @@ class CellularAutomaton(CellularAutomatonCreator, abc.ABC):
evolution_rule = self.evolve_rule
for old, new in zip(this_state.values(), next_state.values()):
if old.is_active:
new_state = evolution_rule(old.state, [n.state for n in old.neighbors])
new_state = evolution_rule(old.state.copy(), [n.state for n in old.neighbors])
old.is_active = False
evolve_cell(old, new, new_state)

View File

@ -117,7 +117,8 @@ class CAWindow:
time.sleep(rest_time)
def _not_at_the_end(self, last_evolution_step):
return self._cellular_automaton.evolution_step < last_evolution_step or last_evolution_step <= 0
return (self._cellular_automaton.evolution_step < last_evolution_step or last_evolution_step <= 0) \
and self._cellular_automaton.active
def __calculate_cell_display_size(self, stretch_cells): # pragma: no cover
grid_dimension = self._cellular_automaton.dimension

View File

@ -83,7 +83,7 @@ class Neighborhood:
yield tuple(map(operator.add, rel_n, cell_coordinate))
def __is_coordinate_on_an_edge(self, coordinate):
return any(ci in [0, di-1] for ci, di in zip(coordinate, self._grid_dimensions))
return any(not(self._radius-1 < ci < di-self._radius) for ci, di in zip(coordinate, self._grid_dimensions))
class MooreNeighborhood(Neighborhood):
@ -154,7 +154,6 @@ class RadialNeighborhood(Neighborhood):
cross_sum = 0
for coordinate_i in rel_neighbor:
cross_sum += pow(coordinate_i, 2)
return math.sqrt(cross_sum) <= self._radius + self.delta

View File

@ -59,5 +59,7 @@ def profile(code):
if __name__ == "__main__":
with contextlib.suppress(KeyboardInterrupt):
print("=== CREATION ===")
profile('ca = StarFallAutomaton()')
print("=== COMPUTATION ===")
profile('ca.evolve(times=10)')

View File

@ -7,7 +7,7 @@ with open('README.md') as f:
setup(
name="cellular_automaton",
version="1.0.7",
version="1.0.8",
author="Richard Feistenauer",
author_email="r.feistenauer@web.de",
packages=find_packages(exclude=('tests', 'docs', 'examples')),

View File

@ -52,7 +52,10 @@ class TAutomaton(ca.CellularAutomaton):
return [1] if cell_coordinate == (1, 1) else [0]
def evolve_rule(self, last_cell_state, neighbors_last_states):
return [last_cell_state[0] + 1] if neighbors_last_states else last_cell_state
ns = last_cell_state[:]
if 0 < last_cell_state[0] < 40:
ns[0] += 1
return ns
@pytest.fixture
@ -70,3 +73,12 @@ def test_evolution_steps_per_draw(automaton, pygame_mock):
def test_updated_rectangle_calls(automaton, pygame_mock):
ca.CAWindow(cellular_automaton=automaton, window_size=(10, 10)).run(last_evolution_step=4)
assert pygame_mock.display.update.call_count == 4 * (3 + 1) # steps * (texts + changed cells)
@import_mock(module='pygame')
def test_ends_when_ca_is_done(automaton, pygame_mock):
automaton.evolve(39)
assert automaton.active == True
assert automaton.evolution_step == 39
ca.CAWindow(cellular_automaton=automaton, window_size=(10, 10)).run(last_evolution_step=45)
assert automaton.active == False
assert automaton.evolution_step == 40

View File

@ -93,6 +93,26 @@ def test_radial():
(1, 4), (2, 4), (3, 4))
def test_radial_neighbor_coords():
neighborhood = ca.RadialNeighborhood(edge_rule=ca.EdgeRule.FIRST_AND_LAST_CELL_OF_DIMENSION_ARE_NEIGHBORS, radius=2)
neighbor_coords = neighborhood.calculate_cell_neighbor_coordinates((0, 0), (10, 10))
assert neighbor_coords == ((9, 8), (0, 8), (1, 8),
(8, 9), (9, 9), (0, 9), (1, 9), (2, 9),
(8, 0), (9, 0), (1, 0), (2, 0),
(8, 1), (9, 1), (0, 1), (1, 1), (2, 1),
(9, 2), (0, 2), (1, 2))
def test_radial_neighbor_coords():
neighborhood = ca.RadialNeighborhood(edge_rule=ca.EdgeRule.FIRST_AND_LAST_CELL_OF_DIMENSION_ARE_NEIGHBORS, radius=2)
neighbor_coords = neighborhood.calculate_cell_neighbor_coordinates((1, 1), (10, 10))
assert neighbor_coords == ((0, 9), (1, 9), (2, 9),
(9, 0), (0, 0), (1, 0), (2, 0), (3, 0),
(9, 1), (0, 1), (2, 1), (3, 1),
(9, 2), (0, 2), (1, 2), (2, 2), (3, 2),
(0, 3), (1, 3), (2, 3))
@pytest.mark.parametrize(('coordinate', 'expected_neighborhood'),
(((2, 2), ((1, 0), (2, 0), (3, 0),
(0, 1), (1, 1), (2, 1), (3, 1),