Emergence

Refers to the acquisition of new traits that appear when multiple entities group together. Such attributes cannot be found at lower levels of organization. Emergence is everywhere, from the chemical properties that matter obtains during the formation of covalent bonds to make molecules, the organization of biological tissues and organs, and even human consciousness.
Surprisingly, elements at lower levels of organization can interact with each other following very simple rules, which undoubtedly contributes to our capacity to model them with computers. We will visualize the concept of emergence using agents that live in chessboard-like grids, modelling their interactions through simple mathematical rules.
We will start with the visualization of 1D cellular automata, sometimes referred to as Elementary Cellular Automata. In this system the identity of a tile, the tile to its left, and the tile to its right, determine the future state of the tile in the next generation. In this system, every row is a generation, and the purpose of the model is to render a diagram where the y axis represents a history on the changes in the 1 row system.
The rules implemented in this algorithm using python and the logic behind Elementary Cellular Automata can be found in Wolfram's Mathworld website



The parameters of the Algorithm

  • RES: int - tile size
  • DIMS: (int,int) - Number of columns and rows of tiles that make the grid
  • SCREEN: (int,int) - Dimensions of the screen in pixels
  • FPS: int - Frames per second of animation
  • TILE: Tile() - Class Tile that represent the basic unit of information in the system
  • GRID: [[TILE, ..., n=DIMS[0]], ..., n = DIMS[1]] - a 2D matrix of TILE with dimensions given by the columns and rows of DIMS
  • RULES: [int, ..., n=8] - set of rules that map a decimal to a TILE's state 0 or 1
######################################## MODULES ######################################
import pygame
######################################## DD ######################################
RES = 4
DIMS = (256,128)
SCREEN = (DIMS[0] * RES,DIMS[1] * RES)
display = pygame.display.set_mode(SCREEN)
FPS = 4

# DD. TILE
# tile = Tile()
# interp. a square that belongs to a grid, and can have a state 0 and 1
class Tile:
    def __init__(self,c,r):
        self.c = c 
        self.r = r 
        self.x = self.c * RES
        self.y = self.r * RES
        self.rect = pygame.Rect(self.x, self.y, RES, RES)
        self.state = 0

    def draw(self,display):
        pygame.draw.rect(display,self.getcolor(),self.rect)
    
    def getcolor(self):
        if self.state == 0:
            return "white"
        return "black"

# DD. GRID
# grid = [[TILE, ..., n=DIMS[0]], ..., n=DIMS[1]]
# interp. a 2D array of tiles
grid = []
for r in range(DIMS[1]):
    row = []
    for c in range(DIMS[0]):
        tile = Tile(c,r)
        row.append(tile)
    grid.append(row)

# TEMPLATE FOR GRID
# for row in grid:
#   for tile in row:
#       ... tile

# DD. RULES
# rules = [int, n=7]
# interp. the set of rules that map a decimal to a state
rules = [0,1,1,1,1,0,0,0]

The Algorithm

  • For every TILE in a ROW N:
    • If TILE has: neighbor to the left, neighbor to the right, and is not in the second last row (row - 1):
      • Find rule that matches the binary configuration given by the states of TILE-LEFT, TILE, TILE-RIGHT
      • Change the state of the TILE below current TILE given by the respective rule

Here's a step by step tutorial on how to implement Langton's algorithm!
######################################## MODULES ######################################
import pygame
######################################## DD ######################################
RES = 4
DIMS = (256,128)
SCREEN = (DIMS[0] * RES,DIMS[1] * RES)
display = pygame.display.set_mode(SCREEN)
FPS = 4

# DD. TILE
# tile = Tile()
# interp. a square that belongs to a grid, and can have a state 0 and 1
class Tile:
    def __init__(self,c,r):
        self.c = c 
        self.r = r 
        self.x = self.c * RES
        self.y = self.r * RES
        self.rect = pygame.Rect(self.x, self.y, RES, RES)
        self.state = 0

    def draw(self,display):
        pygame.draw.rect(display,self.getcolor(),self.rect)
    
    def getcolor(self):
        if self.state == 0:
            return "white"
        return "black"

# DD. GRID
# grid = [[TILE, ..., n=DIMS[0]], ..., n=DIMS[1]]
# interp. a 2D array of tiles
grid = []
for r in range(DIMS[1]):
    row = []
    for c in range(DIMS[0]):
        tile = Tile(c,r)
        row.append(tile)
    grid.append(row)

# TEMPLATE FOR GRID
# for row in grid:
#   for tile in row:
#       ... tile

# DD. RULES
# rules = [int, n=7]
# interp. the set of rules that map a decimal to a state
rules = [0,1,1,1,1,0,0,0]



# CODE

def processBinary(strb):
    while len(strb)<3:
        strb = "0" + strb
    return strb

grid[0][DIMS[0]//2].state = 1
for r,row in enumerate(grid):
    for c,tile in enumerate(row):
        if c >0 and c < DIMS[0]-1 and r < DIMS[1]-1:
            # count the neighbors to the left, center, right
            states = [grid[r][c-1].state,grid[r][c].state,grid[r][c+1].state]
            states = "".join([str(i) for i in states])
            for i in range(8):
                ib = processBinary(str(bin(i)[2:]))
                if ib == states:
                    grid[r+1][c].state = rules[i]


def draw():
    display.fill("green")
    for row in grid:
        for tile in row:
            tile.draw(display)
    pygame.display.flip()
    pygame.time.Clock().tick(FPS)

def update():
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()

while True:
    draw()
    update()