Diesmal ist das Feld wirklich "unendlich" gross und man kann im Konstruktor einen Ausschnitt zum Anzeigen, sowie die Regeln für das Überleben bzw. die Geburt einer Zelle, übergeben.
Code: Alles auswählen
#!/usr/bin/env python
"""Conway's *Game Of Life* in Python.
Conway's *Game Of Life* is a cellular automaton. There is a field with cells
which are either alive or dead. In each step of the "game" cells die or are
born, depending on the number of living cells in their neighbourhood. The
rules of the classic game are:
- a living cell stays alive if there are two or three living neighbours,
- and a dead cell is resurrected if there are exactly three living neighbours.
This can be expressed as the string ``23/3`` where the digits before the slash
tell how many living neighbours are needed for a cell to stay alive and the
digits after the slash how many cells are needed to spawn a new cell. There
are other rules that produce interesting patterns too.
"""
from __future__ import division
import random
import time
__author__ = "Marc 'BlackJack' Rintsch"
__version__ = '0.2.0'
__date__ = '$Date: 2006-04-17 16:59:11 +0200 (Mon, 17 Apr 2006) $'
__revision__ = '$Rev: 843 $'
def bounding_box(points):
"""Return the minimal rectangular bounding box that contains all given
`points`.
All coordinates are "screen coordinates", i.e. the higher a *y* value the
lower is the point "on screen".
If `points` contains no points at all the value ``(0, 0, 0, 0)`` is
returned.
points : iterable of (number, number)
The (x, y) coordinates.
Return a tuple with four numbers that represent the bounding box. The
first two numbers describe the upper left corner and the last two numbers
describe the lower right corner.
"""
points = iter(points)
try:
left, upper = right, lower = points.next()
except StopIteration:
left, upper = right, lower = (0, 0)
for x, y in points:
if x < left:
left = x
elif x > right:
right = x
if y < upper:
upper = y
elif y > lower:
lower = y
assert upper <= lower and left <= right
return (left, upper, right, lower)
def contains((x_1, y_1, x_2, y_2), (x, y)):
"""Check if the given box contains the point."""
return x_1 <= x <= x_2 and y_1 <= y <= y_2
class GameOfLife(object):
"""Conway's *Game of Life*
NEIGHBOUR_POSITIONS : [(int, int)]
Relative coordinates of eight neighbour fields.
cells : set((int, int))
Coordinates of the living cells.
viewport : (int, int, int, int)
Bounding box of the viewport.
clock : int
Current step number.
rule : str
The rule for the game.
"""
NEIGHBOUR_POSITIONS = [(x, y) for x in xrange(-1, 2)
for y in xrange(-1, 2)
if not x == y == 0]
def __init__(self, cells=None, viewport=None, rule='23/3'):
self.cells = set(cells)
self.viewport = viewport or bounding_box(cells)
self.clock = 0
self._life_rule = None
self._resurrect_rule = None
self.rule = rule
def __len__(self):
"""Return the number of living cells."""
return len(self.cells)
def __str__(self):
"""Return a map of the viewport."""
width = self.viewport[2] - self.viewport[0]
height = self.viewport[3] - self.viewport[1]
assert width >= 0 and height >= 0
worldmap = [['.'] * (width + 1) for dummy in xrange(height + 1)]
for x, y in self.iter_cells_in_viewport():
worldmap[y][x] = '#'
return ('Clock: %d Cells: %d\n' % (self.clock, len(self))
+ '\n'.join([''.join(row) for row in worldmap]))
def _get_rule(self):
return (''.join(map(str, self._life_rule))
+ '/'
+ ''.join(map(str, self._resurrect_rule)))
def _set_rule(self, rule):
try:
life, resurrect = rule.split('/')
self._life_rule = map(int, life)
self._resurrect_rule = map(int, resurrect)
except ValueError:
raise ValueError('invalid rule %r' % rule)
rule = property(_get_rule, _set_rule)
def _neighbour_fields(self, (x, y)):
"""Return iterator over the coordinates of the neighbour fields of
the field (`x`, `y`).
"""
return ((x + delta_x, y + delta_y)
for (delta_x, delta_y) in self.NEIGHBOUR_POSITIONS)
def _fields_of_interest(self):
"""Determine the fields that can change.
Return an iterable with the coordinates of living cells and their
neighbour fields.
"""
fields = set(self.cells)
for cell in self.cells:
fields.update(self._neighbour_fields(cell))
return fields
def random_fill(self, probability=0.5, box=None):
"""Spawn cells randomly.
Each cell within `box` has a chance of `probability` to become alive.
If no `box` is given, this method acts on `self.viewport`.
"""
if box is None:
box = self.viewport
for x in xrange(box[0], box[2]):
for y in xrange(box[1], box[3]):
print x, y, random.random()
if random.random() <= probability:
self.cells.add((x, y))
def iter_cells_in_viewport(self):
"""Iterate over living cells within viewport."""
return (cell for cell in self.cells if contains(self.viewport, cell))
def step(self):
"""Calculate the next step."""
new_cells = set()
for field in self._fields_of_interest():
alive_neighbours = sum((int(neighbour_field in self.cells)
for neighbour_field
in self._neighbour_fields(field)))
if field in self.cells:
if alive_neighbours in self._life_rule:
new_cells.add(field)
else:
if alive_neighbours in self._resurrect_rule:
new_cells.add(field)
self.cells = new_cells
self.clock += 1
def run(self, callback=lambda game: None, steps=1000000):
"""Run the game the given `steps` and optionally takes a `callback`
that is called after each step with the game (`self`) as argument.
"""
while len(self) and self.clock < steps:
self.step()
callback(self)
def main():
"""Main function."""
def display(obj, sleep_time=0.25):
"""Clear the screen, display `obj` as string and wait `sleep_time`
seconds.
Uses an ANSI escape sequence to clear the screen.
"""
print '\033[H%s' % obj # Cursor home and `obj` as string.
time.sleep(sleep_time)
#
# Sample game data.
#
game = GameOfLife(((5, 3), (4, 4), (3, 5), (4, 6),
(5, 7), (6, 6), (7, 5), (6, 4)),
(0, 0, 78, 22), '23/3')
game.random_fill(0.5, (30, 5, 50, 15))
print '\033[2J' # Clear screen.
try:
game.run(display)
except KeyboardInterrupt:
print 'Bye...'
if __name__ == '__main__':
main()