HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Generating a 1D cellular automata in Python

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
cellulargeneratingautomatapython

Problem

I wrote a program that generates a set of 1D cellular automaton. Basically, it generates this (rule 150):

...and rotates it to form this:

I then introduced an element of randomness to it, so it could form images like this:

I was hoping I could get reviews for the code I have, focusing around how to optimize it and improve its performance. This program feels like it takes a second or two to generate a 511x511 square on my machine, which makes it feel a bit laggy.

Here's the complete code:

``
#!/usr/bin/env python
'''
Code that will generate a fractal or a pseudo-random fractal
based on an initial seed and any acceptable 1-d cellular automata
algorithm.
'''
import sys
import random
import time
import pygame

class Rules(object):
'''Contains a variety of rules that determines if a cell should turn black
based on the cells in the row above. Each function is namespaced inside
the 'Rules' class for convenience.
'''
@staticmethod
def rule150(above):
'''Colors a cell black if there is an odd number of black cells
above it.'''
return sum(above) in (1, 3)

@staticmethod
def rule150randomized(above):
'''Colors a cell black if there's an odd number of black cells above it
(although this rule will be ignored 0.05% of the time.'''
if sum(above) in (1, 3):
return random.randint(0, 2000) != 0
else:
return False

class Generator(object):
'''An object which generates a single wedge based on an initial seed
and a rule. If the seed is
None, a random one will be generated.'''
def __init__(self, seed=None, rule=Rules.rule150):
self.seed = seed
self.rule = rule

def _generate_seed(self, seed=None):
'''Takes a seed and converts it into an integer.
If the seed is
None`, a random seed based on system time
will be generated.'''
to_int = lambda item : int(''.join([str(ord(x)) for x in str(item)]))
if

Solution

Warning: this hasn't been rigorously tested (i.e., with a known seed)!

There are a couple of things that can be tried without majorly changing things:

-
Grid.center - you already know what it is on initialisation, so create a normal variable for it and remove the @property version. This avoids calculating it several hundred thousand times.

self.center = (int(x / 2), int(y / 2))


-
Enumerate over the array/columns when displaying as you do in other places. This will stop you doing an list index that's repeated:

def render(self, grid):
    '''Renders the grid, and prints the current seed to stdout.'''
    self.grid = grid
    self.surface.fill(self.background)
    for x,col in enumerate( grid.array ):
        xc = x * self.pixel_size
        for y,cell in enumerate( col ):
            if cell:
                self.surface.blit(
                    self.ftile,
                    (xc, y * self.pixel_size)
                )
    pygame.display.flip()


-
The lambdas in Generator.create_grid can be removed (function calls)

xc,yc = grid.center
for index, row in enumerate(self.generate(n)):
    raw_x = index
    for i, cell in enumerate(row):
        if cell:  
            raw_y = i - index
            # Rotates a wedge four times to form a square.
            grid.set( xc + raw_x, yc + raw_y )
            grid.set( xc - raw_x, yc - raw_y )
            grid.set( xc - raw_y, yc + raw_x )
            grid.set( xc + raw_y, yc - raw_x )


-
Changing the rules. rather than doing sum(above) in (1,3), why not explicitly state the combinations?

black_set = ( [False, False, True], [False, True, False], [True, False, False], [True, True, True] )
...
if above in Rules.black_set:
    #etc


All these might give you ~60-65% of the original.

Code Snippets

self.center = (int(x / 2), int(y / 2))
def render(self, grid):
    '''Renders the grid, and prints the current seed to stdout.'''
    self.grid = grid
    self.surface.fill(self.background)
    for x,col in enumerate( grid.array ):
        xc = x * self.pixel_size
        for y,cell in enumerate( col ):
            if cell:
                self.surface.blit(
                    self.ftile,
                    (xc, y * self.pixel_size)
                )
    pygame.display.flip()
xc,yc = grid.center
for index, row in enumerate(self.generate(n)):
    raw_x = index
    for i, cell in enumerate(row):
        if cell:  
            raw_y = i - index
            # Rotates a wedge four times to form a square.
            grid.set( xc + raw_x, yc + raw_y )
            grid.set( xc - raw_x, yc - raw_y )
            grid.set( xc - raw_y, yc + raw_x )
            grid.set( xc + raw_y, yc - raw_x )
black_set = ( [False, False, True], [False, True, False], [True, False, False], [True, True, True] )
...
if above in Rules.black_set:
    #etc

Context

StackExchange Code Review Q#15304, answer score: 4

Revisions (0)

No revisions yet.