def test_cells(): """ Create a Cells object """ from pica.cells import Cells cells = Cells(3, 2, 'state') assert_equals(cells.width, 3) assert_equals(cells.height, 2) for x in range(3): for y in range(2): assert_equals(cells.cell(x, y), 'state') for x, y in ((-1, -1), (0, 4), (3, 2)): assert_is_none(cells.cell(x, y)) for y in (-1, 2, 3): with assert_raises(ValueError): # because row is a generator, we can't just call cells.row, # as the ValueError won't be thrown until access. list(cells.row(y)) for y in range(2): for cell in cells.row(y): assert_equals(cell, 'state') for x, y in ((-1, -1), (0, 4), (3, 2)): with assert_raises(ValueError): cells.update(x, y, 'new state') for y in range(2): for cell in cells.row(y): assert_equals(cell, 'state') for x in range(3): for y in range(2): assert_equals(cells.cell(x, y), 'state') cells.update(x, y, (x, y)) assert_equals(cells.cell(x, y), (x, y)) for y in range(2): for x, cell in enumerate(cells.row(y)): assert_equals(cell, (x, y))
class Automata: """ A cellular automata. """ def __init__(self, width, height, initial_state, *rules): self._cells = Cells(width, height, initial_state) self._rules = rules @property def cells(self): return self._cells def randomize(self, states): """ randomize the automata. states: an iterable of possible states. """ if not states: raise ValueError(states) for x in range(self._cells.width): for y in range(self._cells.height): self._cells.update(x, y, choice(states)) def step(self): """ Take a step in the simulation. Returns a set of Changes. """ changes = set() for x in range(self._cells.width): for y in range(self._cells.height): possibilities = Counter() forbidden = set() for rule in self._rules: rule.reset() result = rule(self._cells, x, y) if result: if result.difference is None: forbidden.add(result.state) else: possibilities[result.state] += result.difference for state in forbidden: del possibilities[state] # works even if state not a key total = sum(possibilities.values()) if total: # at least one possibility value = total * random() tally = 0 for state, probability in possibilities.items(): tally += probability if value < tally: if self._cells.cell(x, y) != state: # new state changes.add(Change(x, y, state)) break for change in changes: self._cells.update(change.x, change.y, change.state) return changes