cellular automaton
This commit is contained in:
parent
dc3527697e
commit
ec83e4b6ea
39
cellular_automaton/README.md
Normal file
39
cellular_automaton/README.md
Normal file
@ -0,0 +1,39 @@
|
||||
# cellular automaton
|
||||
|
||||
for an ascii output run:
|
||||
|
||||
```
|
||||
usage: cellular.py [-h] [--w W] [--code CODE] [--random] [--r R]
|
||||
|
||||
one dimensional cellular automaton for k=2
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--w W field width
|
||||
--code CODE code for the automaton
|
||||
--random
|
||||
--r R radius. can be 1 or 2
|
||||
```
|
||||
|
||||
for opengl output:
|
||||
|
||||
```
|
||||
usage: glCellular.py [-h] [--steps STEPS] [--w W] [--h H] [--fullscreen]
|
||||
[--window_w WIN_W] [--window_h WIN_H] [--code CODE]
|
||||
[--random] [--r R]
|
||||
|
||||
one dimensional cellular automaton for k=2 with opengl output
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--steps STEPS steps per second
|
||||
--w W field width
|
||||
--h H field height
|
||||
--fullscreen
|
||||
--window_w WIN_W window width
|
||||
--window_h WIN_H window height
|
||||
--code CODE code for the automaton
|
||||
--random
|
||||
--r R radius. can be 1 or 2
|
||||
```
|
||||
|
117
cellular_automaton/cellular.py
Executable file
117
cellular_automaton/cellular.py
Executable file
@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env python3
|
||||
import numpy as np
|
||||
import argparse
|
||||
|
||||
# cellular automaton functions:
|
||||
def generatestart(startcondition,border,size):
|
||||
"""
|
||||
:param startcondition: r for random numers zero or one
|
||||
:param border: the size of the border one or two
|
||||
:param size: size of the whole array
|
||||
:return: the initialized array to work with
|
||||
"""
|
||||
|
||||
#all cells
|
||||
cells = np.zeros((size),int)
|
||||
#cells without borders
|
||||
writableCells = cells[border:size-border]
|
||||
|
||||
#startcondition for seed
|
||||
if(startcondition == "s"):
|
||||
cells[size // 2] = 1
|
||||
#condition for setting random values
|
||||
else:
|
||||
for i in range(writableCells.shape[0]):
|
||||
writableCells[i]=np.round(np.random.rand())
|
||||
#print(np.shape(writableCells))
|
||||
return cells
|
||||
|
||||
def code2FunctionTable(code, r):
|
||||
result = {}
|
||||
|
||||
c = code
|
||||
|
||||
if r == 1:
|
||||
e = 8
|
||||
for i in range(2):
|
||||
for j in range(2):
|
||||
for k in range(2):
|
||||
if 2**e > c:
|
||||
result[(i,j,k)] = 0
|
||||
else:
|
||||
result[(i,j,k)] = 1
|
||||
c -= 2 ** e
|
||||
e -= 1
|
||||
elif r == 2:
|
||||
e = 32
|
||||
for i in range(2):
|
||||
for j in range(2):
|
||||
for k in range(2):
|
||||
for l in range(2):
|
||||
for m in range(2):
|
||||
if 2 ** e > c:
|
||||
result[(i, j, k, l, m)] = 0
|
||||
else:
|
||||
result[(i, j, k, l, m)] = 1
|
||||
c -= 2 ** e
|
||||
e -= 1
|
||||
return result
|
||||
|
||||
def getNextCellValue(functionTable, row, pos, r):
|
||||
if r == 1:
|
||||
i = row[pos - 1]
|
||||
j = row[pos]
|
||||
k = row[pos + 1]
|
||||
return functionTable[(i,j,k)]
|
||||
elif r == 2:
|
||||
i = row[pos - 2]
|
||||
j = row[pos - 1]
|
||||
k = row[pos]
|
||||
l = row[pos + 1]
|
||||
m = row[pos + 2]
|
||||
return functionTable[(i,j,k,l,m)]
|
||||
return None
|
||||
|
||||
def calculateNextStep(functionTable, size, row, r):
|
||||
result = np.zeros((size),int)
|
||||
for i in range(r, size - r):
|
||||
result[i] = getNextCellValue(functionTable, row, i, r)
|
||||
return result
|
||||
|
||||
if __name__ == "__main__":
|
||||
# parsing args:
|
||||
parser = argparse.ArgumentParser(description="one dimensional cellular automaton for k=2")
|
||||
#parser.add_argument('--steps', dest='steps', default=20, help='steps per second')
|
||||
parser.add_argument('--w', dest='w', default=84, help='field width')
|
||||
#parser.add_argument('--h', dest='h', default=livingSpaceHeight, help='field height')
|
||||
#parser.add_argument('--fullscreen', dest='fullscreen', action='store_true')
|
||||
#parser.add_argument('--window_w', dest='win_w', default=window_w, help='window width')
|
||||
#parser.add_argument('--window_h', dest='win_h', default=window_h, help='window height')
|
||||
parser.add_argument('--code', dest='code', default='17', help='code for the automaton')
|
||||
parser.add_argument('--random', dest='random', action='store_true')
|
||||
parser.add_argument('--r', dest='r', default=1, help='radius. can be 1 or 2')
|
||||
parser.set_defaults(random=False)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
r = int(args.r)
|
||||
c = int(args.code)
|
||||
size = int(args.w)
|
||||
startcondition = ""
|
||||
|
||||
if args.random:
|
||||
startcondition = "r"
|
||||
else:
|
||||
startcondition = "s"
|
||||
|
||||
functionTable = code2FunctionTable(c, r)
|
||||
print(functionTable)
|
||||
cells = generatestart(startcondition, r, size)
|
||||
print(cells)
|
||||
|
||||
for i in range(20):
|
||||
cells = calculateNextStep(functionTable,size,cells, r)
|
||||
print(cells)
|
||||
|
||||
|
||||
|
337
cellular_automaton/glCellular.py
Executable file
337
cellular_automaton/glCellular.py
Executable file
@ -0,0 +1,337 @@
|
||||
#!/usr/bin/env python3
|
||||
import pygame
|
||||
from OpenGL.GL import *
|
||||
from pygame.locals import *
|
||||
from cellular import *
|
||||
|
||||
import argparse
|
||||
|
||||
#TODO: remove global vars
|
||||
|
||||
# NOTE: modified python script from a multi color langton's ant implementation, so
|
||||
# maybe some variable names are a little bit confusing...
|
||||
|
||||
pygame.init()
|
||||
|
||||
window_w = 1600
|
||||
window_h = 900
|
||||
|
||||
livingSpaceWidth = 84
|
||||
livingSpaceHeight = 47
|
||||
|
||||
creatureW = window_w/(livingSpaceWidth)
|
||||
creatureH = window_h/(livingSpaceHeight)
|
||||
|
||||
FPS = 30
|
||||
|
||||
livingSpace = []
|
||||
livingSpaceColor = []
|
||||
|
||||
update_queue = []
|
||||
old_update_queue = []
|
||||
|
||||
# doubled draw buffer because of display double buffering
|
||||
draw_buffer = []
|
||||
draw_buffer_old = []
|
||||
|
||||
current_iteration = 0
|
||||
|
||||
num_colors = 2
|
||||
color_list = []
|
||||
code = 0
|
||||
r = 1
|
||||
|
||||
# helper function for colors:
|
||||
def HSVtoRGB(h,s,v):
|
||||
c = v*s
|
||||
x = c*(1-abs((h/60)% 2 -1))
|
||||
m = v-c
|
||||
rr=0
|
||||
gg=0
|
||||
bb=0
|
||||
if(h<60):
|
||||
rr=c
|
||||
gg=x
|
||||
bb=0
|
||||
elif (h < 120):
|
||||
rr = x
|
||||
gg = c
|
||||
bb = 0
|
||||
elif (h < 180):
|
||||
rr = 0
|
||||
gg = c
|
||||
bb = x
|
||||
elif (h < 240):
|
||||
rr=0
|
||||
gg=x
|
||||
bb=c
|
||||
elif (h < 300):
|
||||
rr = x
|
||||
gg = 0
|
||||
bb = c
|
||||
elif (h < 360):
|
||||
rr = c
|
||||
gg = 0
|
||||
bb = x
|
||||
|
||||
return(rr+m,gg+m,bb+m)
|
||||
|
||||
def generate_colors():
|
||||
"""
|
||||
generate colors linear over HSV Space (one color for every possible state, not yet supported, but maybe in the future)
|
||||
:return:
|
||||
"""
|
||||
global color_list
|
||||
global num_colors
|
||||
color_list = []
|
||||
for i in range(num_colors):
|
||||
color_list.append(HSVtoRGB(i * 360.0 / num_colors,1,1))
|
||||
|
||||
|
||||
def resize(shape):
|
||||
"""
|
||||
Init opengl viewport
|
||||
:param shape:
|
||||
:return:
|
||||
"""
|
||||
width, height = shape
|
||||
if height == 0:
|
||||
height = 1
|
||||
glViewport(0, 0, width, height)
|
||||
glMatrixMode(GL_PROJECTION)
|
||||
glLoadIdentity()
|
||||
glOrtho(0.0, livingSpaceWidth * creatureW, livingSpaceHeight * creatureH, 0.0, -6.0, 0.0)
|
||||
glMatrixMode(GL_MODELVIEW)
|
||||
glLoadIdentity()
|
||||
|
||||
def init():
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0)
|
||||
|
||||
def initLivingSpace():
|
||||
for x in range(livingSpaceWidth):
|
||||
livingSpace.append([])
|
||||
livingSpaceColor.append([])
|
||||
for y in range(livingSpaceHeight):
|
||||
livingSpace[x].append(0)
|
||||
rgba = [float(x)/float(livingSpaceWidth), float(livingSpaceWidth-x)/float(livingSpaceWidth), float(y)/float(livingSpaceHeight),0.15]
|
||||
livingSpaceColor[x].append(rgba)
|
||||
draw_buffer.append((x,y))
|
||||
|
||||
def isAlive(x,y):
|
||||
return livingSpace[x][y] != 0
|
||||
|
||||
def draw():
|
||||
global draw_buffer
|
||||
global draw_buffer_old
|
||||
#glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
|
||||
glLoadIdentity()
|
||||
glTranslatef(0.0,0.0,3.0)
|
||||
|
||||
|
||||
glBegin(GL_QUADS)
|
||||
|
||||
for column,row in draw_buffer + draw_buffer_old:
|
||||
r,g,b,a = livingSpaceColor[column][row]
|
||||
|
||||
#glColor4f(255, 0, 0, 1.0)
|
||||
glColor4f(a * r, a * g, a * b, 1.0)
|
||||
x = column * creatureW
|
||||
y = row * creatureH
|
||||
|
||||
glVertex3f(x,y,0.0)
|
||||
glVertex3f(x + creatureW-1.0,y,0.0)
|
||||
glVertex3f(x+creatureW-1,y+creatureH-1,0.0)
|
||||
glVertex3f(x,y+creatureH-1,0.0)
|
||||
|
||||
|
||||
draw_buffer_old = draw_buffer
|
||||
draw_buffer = []
|
||||
|
||||
|
||||
def activate(i,j,key = 1):
|
||||
"""
|
||||
activate field (i,j)
|
||||
:param i:
|
||||
:param j:
|
||||
:param key:
|
||||
:return:
|
||||
"""
|
||||
livingSpace[i][j] = key
|
||||
if num_colors > 2:
|
||||
livingSpaceColor[i][j] = [
|
||||
color_list[key - 1][0],
|
||||
color_list[key - 1][1],
|
||||
color_list[key - 1][2],
|
||||
1.0
|
||||
]
|
||||
else:
|
||||
livingSpaceColor[i][j][3] = 1.0
|
||||
update_queue.append((i,j))
|
||||
|
||||
def deactivate(i,j):
|
||||
"""
|
||||
deactivate field (i,j)
|
||||
:param i:
|
||||
:param j:
|
||||
:return:
|
||||
"""
|
||||
livingSpace[i][j] = 0
|
||||
# correct color:
|
||||
livingSpaceColor[i][j] = [
|
||||
float(i) / float(livingSpaceWidth),
|
||||
float(livingSpaceWidth - i) / float(livingSpaceWidth),
|
||||
float(j) / float(livingSpaceHeight),
|
||||
0.6
|
||||
]
|
||||
|
||||
update_queue.append((i,j))
|
||||
|
||||
def update_field():
|
||||
global old_update_queue
|
||||
global update_queue
|
||||
|
||||
old_update_queue = update_queue
|
||||
update_queue = []
|
||||
for i,j in old_update_queue:
|
||||
draw_buffer.append((i,j))
|
||||
if livingSpace[i][j] <= 0 and livingSpaceColor[i][j][3] > 0.15:
|
||||
livingSpaceColor[i][j][3] *= 0.98
|
||||
if livingSpaceColor[i][j][3] < 0.15:
|
||||
livingSpaceColor[i][j][3] = 0.15
|
||||
update_queue.append((i,j))
|
||||
elif livingSpace[i][j] > 0 and livingSpaceColor[i][j][3] > 0.6:
|
||||
livingSpaceColor[i][j][3] *= 0.98
|
||||
if livingSpaceColor[i][j][3] < 0.6:
|
||||
livingSpaceColor[i][j][3] = 0.6
|
||||
update_queue.append((i,j))
|
||||
|
||||
def updateAutomaton(cells):
|
||||
global livingSpaceWidth
|
||||
global current_iteration
|
||||
|
||||
for i in range(livingSpaceWidth):
|
||||
if cells[i] != 0:
|
||||
activate(i, current_iteration)
|
||||
|
||||
def main():
|
||||
|
||||
global livingSpaceWidth
|
||||
global livingSpaceHeight
|
||||
global creatureW
|
||||
global creatureH
|
||||
global window_w
|
||||
global window_h
|
||||
global color_list
|
||||
global num_colors
|
||||
global code
|
||||
global current_iteration
|
||||
global r
|
||||
|
||||
# parsing args:
|
||||
parser = argparse.ArgumentParser(description="one dimensional cellular automaton for k=2 with opengl output")
|
||||
parser.add_argument('--steps', dest='steps', default = 60 , help='steps per second')
|
||||
parser.add_argument('--w', dest='w', default = livingSpaceWidth, help = 'field width')
|
||||
parser.add_argument('--h', dest='h', default=livingSpaceHeight, help = 'field height')
|
||||
parser.add_argument('--fullscreen', dest='fullscreen', action='store_true')
|
||||
parser.add_argument('--window_w', dest='win_w', default=window_w, help='window width')
|
||||
parser.add_argument('--window_h', dest='win_h', default=window_h, help='window height')
|
||||
parser.add_argument('--code', dest='code', default='150', help='code for the automaton')
|
||||
parser.add_argument('--random', dest='random', action='store_true')
|
||||
parser.add_argument('--r', dest='r', default=r, help='radius. can be 1 or 2')
|
||||
|
||||
parser.set_defaults(fullscreen=False)
|
||||
parser.set_defaults(random=False)
|
||||
#parser.set_defaults(configurator=False)
|
||||
|
||||
args = parser.parse_args()
|
||||
steps_per_sec = int(args.steps)
|
||||
livingSpaceWidth = int(args.w)
|
||||
livingSpaceHeight = int(args.h)
|
||||
|
||||
video_flags = OPENGL | HWSURFACE | DOUBLEBUF
|
||||
|
||||
r = int(args.r)
|
||||
c = int(args.code)
|
||||
startcondition = ""
|
||||
|
||||
if args.random:
|
||||
startcondition = "r"
|
||||
else:
|
||||
startcondition = "s"
|
||||
|
||||
functionTable = code2FunctionTable(c, r)
|
||||
cells = generatestart(startcondition, r, livingSpaceWidth)
|
||||
|
||||
# generate colors:
|
||||
num_colors = 2 # k is fix for now
|
||||
generate_colors()
|
||||
|
||||
|
||||
if args.fullscreen:
|
||||
video_flags = OPENGL | HWSURFACE | DOUBLEBUF | FULLSCREEN
|
||||
dinfo = pygame.display.Info()
|
||||
window_w = dinfo.current_w
|
||||
window_h = dinfo.current_h
|
||||
else:
|
||||
window_w = int(args.win_w)
|
||||
window_h = int(args.win_h)
|
||||
|
||||
|
||||
creatureW = window_w / (livingSpaceWidth)
|
||||
creatureH = window_h / (livingSpaceHeight)
|
||||
|
||||
pygame.display.set_mode((window_w,window_h),video_flags)
|
||||
|
||||
initLivingSpace()
|
||||
|
||||
updateAutomaton(cells)
|
||||
|
||||
resize((window_w, window_h))
|
||||
init()
|
||||
|
||||
clock = pygame.time.Clock()
|
||||
|
||||
frames = 0
|
||||
counter = 0
|
||||
logic_frame_pause = FPS / float(steps_per_sec)
|
||||
|
||||
field_draws = 0
|
||||
|
||||
#main loop:
|
||||
while True:
|
||||
|
||||
|
||||
ticktime = clock.tick(FPS)
|
||||
#print ticktime
|
||||
event = pygame.event.poll()
|
||||
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
|
||||
break
|
||||
|
||||
update_field();
|
||||
draw()
|
||||
|
||||
field_draws += len(draw_buffer) + len(draw_buffer_old)
|
||||
frames += 1
|
||||
|
||||
if frames % FPS == 0:
|
||||
print("average field draws per frame: " + str(field_draws/FPS))
|
||||
field_draws = 0
|
||||
|
||||
|
||||
pygame.display.flip()
|
||||
|
||||
counter += 1
|
||||
|
||||
if current_iteration < livingSpaceHeight-1:
|
||||
if counter > logic_frame_pause:
|
||||
cells = calculateNextStep(functionTable, livingSpaceWidth, cells, r)
|
||||
current_iteration += 1
|
||||
updateAutomaton(cells)
|
||||
print(current_iteration)
|
||||
counter = 0
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Loading…
Reference in New Issue
Block a user