def test_requirement(): """ A Requirement """ from pica.cells import Cells from pica.conditions import Equals from pica.rules import Requirement, Result requirement = Requirement('from', 'to', Equals(1, 0, 'no match')) cells = Cells(2, 2, 'from') cells.update(0, 0, 'no match') cells.update(1, 1, 'no match') assert_is_none(requirement(cells, -1, -1)) for x in range(2): for y in range(2): requirement.reset() if (x, y) == (0, 1) or x == y: assert_is_none(requirement(cells, x, y)) else: assert_equals(requirement(cells, x, y), Result('to', None))
def test_rule(): """ A Rule """ from pica.cells import Cells from pica.conditions import Equals from pica.rules import Rule, Result rule = Rule('from', 'to', Equals(1, 0, 'no match'), 0.5) cells = Cells(2, 2, 'from') cells.update(0, 0, 'no match') cells.update(1, 1, 'no match') assert_is_none(rule(cells, -1, -1)) for x in range(2): for y in range(2): rule.reset() if (x, y) == (0, 1): assert_equals(rule(cells, x, y), Result('to', 0.5)) else: assert_is_none(rule(cells, x, y))
def __init__(self, width, height, initial_state, *rules): self._cells = Cells(width, height, initial_state) self._rules = rules
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
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))
def test_abstract_rule(): """ The AbstractRule object """ from pica.cells import Cells from pica.conditions import Equals from pica.rules import AbstractRule, Result with assert_raises(TypeError): AbstractRule() class Implementation(AbstractRule): """ An implementation of the Condition class """ def __init__(self, from_state, to_state, condition): super().__init__() self._counter = 0 self._from_state = from_state self._to_state = to_state self._condition = condition @property def from_state(self): return self._from_state @property def condition(self): return self._condition def evaluate(self, cells, x, y): """ An implementation of evaluate. """ if self._condition(cells, x, y): self._counter += 1 return Result(self._to_state, self._counter - 1) rule = Implementation('from', 'to', Equals(0, 0, 'from')) cells = Cells(2, 2, 'from') cells.update(0, 0, 'no match') cells.update(1, 1, 'no match') assert_equals(rule(cells, 0, 1), Result('to', 0)) assert_equals(rule(cells, 0, 1), Result('to', 0)) assert_equals(rule(cells, 0, 1), Result('to', 0)) assert_equals(rule(cells, 0, 1), Result('to', 0)) assert_equals(rule(cells, 0, 1), Result('to', 0)) rule.reset() assert_equals(rule(cells, 0, 1), Result('to', 1)) assert_equals(rule(cells, 0, 1), Result('to', 1)) assert_equals(rule(cells, 0, 1), Result('to', 1)) assert_equals(rule(cells, 0, 1), Result('to', 1)) assert_equals(rule(cells, 0, 1), Result('to', 1)) rule.reset() assert_is_none(rule(cells, 0, 0)) rule.reset() assert_equals(rule(cells, 0, 1), Result('to', 2))