Sierpinski triangle (Python)

From LiteratePrograms
Jump to: navigation, search
Other implementations: Forth | JavaScript | OCaml | Python | Scheme | Sed

The following is an implementation of the one-dimensional cellular automata, and the Sierpinski cellular automaton in particular, in Python.

A one-dimensional cellular automaton is a linear space of cells, each of which has a state. The automaton changes in discrete time steps called generations. In each generation the state of each cell is decided according to a set of rules that is based on the states of neighboring cells. In the case of one-dimensional cellular automaton each cell has its state decided by considering three cells, itself and its left and right neighboring cells. Neighbouring the first and last cells of the space, we will assume virtual cells that are always in a default state.

The Sierpinski cellular automaton has two states, Empty and Full. The only rule states that a cell will be set to Full if and only if of the three states considered exactly one or two are Full.

When run output similar to the following will be produced.

                                        @                                       
                                       @@@                                      
                                      @@ @@                                     
                                     @@@@@@@                                    
                                    @@     @@                                   
                                   @@@@   @@@@                                  
                                  @@  @@ @@  @@                                 
                                 @@@@@@@@@@@@@@@                                
                                @@             @@                               
                               @@@@           @@@@                              
                              @@  @@         @@  @@                             
                             @@@@@@@@       @@@@@@@@                            
                            @@      @@     @@      @@                           
                           @@@@    @@@@   @@@@    @@@@                          
                          @@  @@  @@  @@ @@  @@  @@  @@                         
                         @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                        
                        @@                             @@                       
                       @@@@                           @@@@                      
                      @@  @@                         @@  @@                     
                     @@@@@@@@                       @@@@@@@@                    
                    @@      @@                     @@      @@                   
                   @@@@    @@@@                   @@@@    @@@@                  
                  @@  @@  @@  @@                 @@  @@  @@  @@                 
                 @@@@@@@@@@@@@@@@               @@@@@@@@@@@@@@@@                
                @@              @@             @@              @@               
               @@@@            @@@@           @@@@            @@@@              
              @@  @@          @@  @@         @@  @@          @@  @@             
             @@@@@@@@        @@@@@@@@       @@@@@@@@        @@@@@@@@            
            @@      @@      @@      @@     @@      @@      @@      @@           
           @@@@    @@@@    @@@@    @@@@   @@@@    @@@@    @@@@    @@@@          
          @@  @@  @@  @@  @@  @@  @@  @@ @@  @@  @@  @@  @@  @@  @@  @@         
         @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

[edit] Implementation

We internally represent the Empty and Full states as 0 and 1 respectively. The state transformation rule can be expressed as a dictionary keyed on tuples of state values. For clarity, we'll write out the complete ruleset.

<<rules>>=
rules = {(0, 0, 0):0, 
         (0, 0, 1):1,
         (0, 1, 0):1,
         (0, 1, 1):1,
         (1, 0, 0):1,
         (1, 0, 1):1,
         (1, 1, 0):1,
         (1, 1, 1):0}

We represent the state of the cellular automaton in a given generation as a list of cell states of size AUTOMATA_SIZE. The initial automaton state is set to Empty, with the exception of the middle cell in the list, which is Full.

<<initial state>>=
AUTOMATA_SIZE = 80
init_states = [0]*(AUTOMATA_SIZE/2 - 1) + [1] + [0]*(AUTOMATA_SIZE/2)

To transform the automaton from one generation to the next we apply the rules to each cell in the automaton in turn. Rule application consists of performing a dictionary lookup on the tuple of states corresponding to cell to which the rule is being applied, along with it's neighbors to the left and right.

<<apply rules>>=
def apply_rules(states):
    width = len(states)
    new_states = states[:]
    end states
    for i in range(1, width - 2):
        new_states[i] = rules[(states[i - 1], states[i], states[i + 1])]
    return new_states

We assume that the states at either end of the automaton space have neighbors that are always empty:

<<end states>>=
new_states[0] = rules[(0, states[0], states[1])]
new_states[width - 1] = rules[(states[width - 2], states[width - 1], 0)]

To run the automaton we provide a simple function that takes as an argument the number of generations to create, and prints successive generations to the screen. For display purposes, we represent Empty cells as a space, and Full cells as a @ character (although these choices can easily be changed by modifying the state_to_char function).

<<sierpinski.py>>=
initial state
rules
apply rules
def state_to_char(state):
    if state==0: 
        return ' '
    else: 
        return '@'
        
def states_to_string(states):
    return ''.join(map(state_to_char, states))
    
def run_and_display(n_gens):
    states = init_states
    for i in range(n_gens):
        print states_to_string(states)
        states = apply_rules(states)

if __name__ == "__main__":
    run_and_display(32)
Download code
hijacker
hijacker
hijacker
hijacker