Skip to content

Commit

Permalink
A generic cellular automata in Python
Browse files Browse the repository at this point in the history
  • Loading branch information
lefticus committed Jan 25, 2023
1 parent c49022d commit 5961287
Showing 1 changed file with 119 additions and 0 deletions.
119 changes: 119 additions & 0 deletions python/conway_game_of_life/cellular_automata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import dataclasses
import random
import pygame

@dataclasses.dataclass
class Point:
x: int
y: int

def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)

@dataclasses.dataclass
class Cell:
rule_number: int
value: int

class Neighborhood:
def __init__(self, array, array_width, array_height, center):
self.array = array
self.array_width = array_width
self.array_height = array_height
self.center = center

def get(self,p):
loc = self.center + p
return self.array[loc.x + (loc.y * self.array_width)]


class Automata:

def __init__(self, width, height, rules):
self.width = width
self.height = height
self.data = [Cell(0, 0)] * (width * height)
self.rules = rules

def index(self, p: Point):
return (p.y % self.height) * self.width + (p.x % self.width)

def get(self, p: Point):
return self.data[self.index(p)]

def set(self, p: Point, value: Cell):
self.data[self.index(p)] = value

def next(self, skip_borders = False):
result = Automata(self.width, self.height, self.rules)

for y in range(0, self.height):
for x in range(0, self.width):
p = Point(x, y)
cell = self.get(p)
if not skip_borders or (skip_borders and not (y == 0 or x == 0 or y == self.height-1 or x == self.width-1)):
result.set(p, self.rules[cell.rule_number](cell, Neighborhood(self.data, self.width, self.height, p)))
else:
result.set(p, cell)



return result

def randomize_values(self, value_min, value_max):
self.data = [Cell(c.rule_number, random.randint(value_min, value_max)) for c in self.data]





def gravity(cell : Cell, neighbors : Neighborhood):
if cell.value == 0 and neighbors.get(Point(0, -1)).value == 1:
return Cell(cell.rule_number, 1)
if cell.value == 1 and neighbors.get(Point(0, 1)).value == 0:
return Cell(cell.rule_number, 0)

return cell

def fire(cell : Cell, neighbors : Neighborhood):
falloff = .99
new_value = int(cell.value * (random.random()*falloff)) + int(neighbors.get(Point(0, 1)).value * (random.random()*falloff))

new_value = new_value if new_value <= 255 else 255

return Cell(cell.rule_number, new_value)


width = 320
height = 240

obj = Automata(
width,
height,
[fire]
)

obj.randomize_values(0, 255)

for x in range(0, obj.width):
obj.set(Point(x, 0), Cell(0, 0))
obj.set(Point(x, obj.height-1), Cell(0, 255))

for y in range(0, obj.height):
obj.set(Point(0, y), Cell(0, 0))
obj.set(Point(obj.width-1, y), Cell(0, 0))


pygame.init()
screen = pygame.display.set_mode((width, height))

while True:
pygame.display.get_surface().fill((0,0,0))
for y in range(0, obj.height):
for x in range(0, obj.width):
value = obj.get(Point(x,y)).value
screen.set_at((x,y), (value, value, value))

pygame.display.flip()
obj = obj.next(skip_borders = True)

0 comments on commit 5961287

Please sign in to comment.