Square Dancing in Python

RedXIII | Oct. 22, 2021, 7:26 a.m.

An Experiment in Cellular Automata

This tutorial will teach you how to create a very basic simulation in Python. Together, we’ll create a world of tiny squares, dancing to the rhythm of our program’s pulse.

This will be an exercise in Cellular Automata, a field of computational science with many applications. Our goals will be simple and twofold: art and education. We’ll make use of the Pygame library so that we can visualize the simulation.

Using detailed explanations and plenty of coding samples, we’ll cover all the steps needed to build a simulation in Python. And our final program will be less than 100 lines of code!

A Brief Overview of Cellular Automata

Before getting started, we need to introduce the tools we’ll be using to simulate our tiny world. You already know about Python, I hope. We’ll also be using Cellular Automata. This theoretical tool is useful for modeling our world.

With Cellular Automata, we create a grid of cells, each cell having a certain state. With each tick of the simulation clock, a set of rules is applied to every cell in the grid. We call this a new generation.

With each generation, the simulation changes. By altering the rules of the simulation step, the automata can be used to achieve a variety of outcomes.

Getting Started with Pygame

Pygame is an open source library for creating multimedia programs in Python. We’ll use it to create graphics for our simulated world.

The easiest way to install Pygame is to use pip from the command prompt.

pip install pygame

Once pip installs Pygame, we can use it in our project with the importkeyword.

import pygame

Setting up the Simulation Loop

With Pygame installed, we’re ready to code our game loop. The game loop repeats forever. This way, our simulation will keep running until we tell it to end. For this, we need to use a while loop.

The end condition for the loop will be when the player presses the quit button. With each tick of the simulation, we’ll check to see if the player has hit the quit button or not. If they have, we’ll end the program. Otherwise the simulation will continue to the next step.

import pygame, random
from random import randrange, shuffle
pygame.init()screen = pygame.display.set_mode([400,400])
clock = pygame.time.Clock()
cells = []MAP_WIDTH = 20
size = 20
steps = 0

Set the Frame Rate

We can set the frame rate of Pygame so that the simulation passes in set intervals. Doing so takes two steps.

First we need to grab the clock object.

clock = pygame.time.Clock()

Once we have the clock, we can set the game’s framerate to anything we want. We’ll start with 3 frames per second, which will give us time to observe the simulation steps. Using the clock’s tick() method, we’ll set the clock’s fps. This statement goes inside our while loop.

done = Falsewhile not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill((255,255,255)) draw_map() do_step() pygame.display.flip() clock.tick(3)
print("Steps: " + str(steps))

Creating Cellular Objects

To create our cells, we’ll make a new Python class. This class will store information about the cells in our grid. For example, the location of each cell and whether or not the cell is alive.

By saving each cell as its own object, we can keep track of each cell in the grid. We’ll do so by saving all the cells to a Python list.

size = 20# the model for our cells
class Cell:
def __init__(self,x,y,dead,value):
self.x = x
self.y = y
self.dead = dead
self.value = value

Setting Up The Simulation Environment

Next, we’ll set up the simulation environment. We’ll do so by first defining a width and height for our map. We’ll define these dimensions in terms of the number of cells. In this example, we’ll be using a map that is 20 cells wide and 20 cells tall, for a grand total of 400 cells.

steps = 0

Initializing the Map

With the map’s dimensions defined, we can create our cells. Using a nested for loop, our program will create a new cell for each position on the map. Each cell is given randomly generated data to start with. For this we need the help of the randrange() method.

# initialize the simulation map
def init_map():
for y in range(MAP_HEIGHT):
for x in range(MAP_WIDTH):
dead = True if randrange(10) > 3 else False
power = randrange(1,255)
cell = Cell(x,y,dead,power)

Drawing the Map

Once the map has been defined, it’s possible to create a graphic representation of it using the Pygame library. To draw the map, we’ll paint a rectangle for each cell in the grid. The color of the cell will depend on the value that was randomly attributed to it. The cell size was defined earlier, when we created our cell object.

# draw graphics for the map with Pygame
def draw_map():
for cell in cells:
color = (0, 0, cell.value)
pygame.draw.rect(screen, color, pygame.Rect(cell.x*size, cell.y*size, size, size))

Coding the Simulation Step

Before our simulation will dance, we’ll need to create a simulation loop. This loop will be responsible for creating each new generation of the simulation.

Firstly, the function shuffles the cells. Secondly, each living cell in the grid is examined and, if the cell has neighbors, we’ll swap the cells around, creating a little dance. A square dance.

# do the next simulation step (generation)
def do_step():
for cell in cells:
if not cell.dead:
neighbors = get_living_neighbors(cell)
if len(neighbors) > 0:
prey = neighbors[randrange(len(neighbors))]
swap_cells(cell, prey)

Getting a Cell from the Map

The simulation step is coded, but it’s missing some functions. Before it will work, we’ll need to code a way to retrieve a cell from the grid. This way we can find the neighbors of a cell. We’ll write a little helper function to make that possible.

# get a cell from the grid
def get_cell(x,y):
for cell in cells:
if cell.x == x and cell.y == y:
return cell

Finding the Neighbors of a Cell

Finding the neighbors of a cell is a common problem. While there are many ways of finding this data, we’ll go with a nested for loop. This code will inspect the given cell’s neighbors by first making sure that the cell is inside the grid boundaries. If it is, the code checks whether or not the cell is alive. If the cell is alive, it’s added to the list of living cells.

# find the neighbors of a cell that are alive
def get_living_neighbors(cell):
neighbors = []
for y in range(-1,1):
for x in range(-1,1):
if cell.x + x < 0 or cell.x + x > MAP_WIDTH-1 or cell.y + y < 0 or cell.y + y > MAP_HEIGHT-1:
neighbor_cell = get_cell(cell.x + x, cell.y + y)
if not neighbor_cell.dead:
return neighbors

Swapping Cells

Making the simulation dance is all about swapping the positions of cells. Just like on a real dance floor, our cells will move and trade places. Swapping cells is another common job that’s easily accomplished with the aid of a temp variable.

def swap_cells(cellA, cellB):
tempX = cellB.x
tempY = cellB.y
cellB.x = cellA.x
cellB.y = cellA.y
cellA.x = tempX
cellA.y = tempY

Running the Simulation

With all the parts in place, we can run the code and see our squares dancing on our screen.

# we start the program here
done = Falsewhile not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill((255,255,255)) draw_map() do_step() pygame.display.flip() clock.tick(3)
print("Steps: " + str(steps))

Related Posts

I hope you’ve enjoyed this tutorial on creating simulations in Python. While the outcome was a bit whimsical, the applications of our work are far reaching. For game designers and cryptographers alike, Cellular Automata is a useful subject to know.

If you enjoyed this post and would like to learn more about computer programming and Python, follow these links for more tutorials.

About Us

Learning at the speed of light.

We created Start Prism to help students learn programming. You can find exercises and recent tutorials below.

Topics Quizzes Tutorials


Leave a comment