Select Git revision
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
ackland_simulation.py 18.81 KiB
from graphics import * # get here: http://mcsp.wartburg.edu/zelle/python/graphics.py
import time
import random
import math
import csv
# imports from our files
from help_functions import *
from classes import *
from check_boundarys import *
'''
plot is not used here, because we could not figure
out a way to close the plot and continue with the next generation
'''
# ------------------------------------------------------------------
# global varibles
# window dimensions
winWidth = 700
winHeight = 700
# set true to show visualisation/write data in csv
visual = True
writecsv = False
# names for csvs
GENERATIONS = "generations.csv"
TIMESTEPS = "timesteps.csv"
if visual:
window = GraphWin("Window", winWidth, winHeight)
else:
window = None
# max time for one generation
maxTime = 10000
# number of agents
agentNum = 80
# number of predators
predNum = agentNum//10
# number of generations
generations = 500
# food cluster variables
# soll insgesamt immer 128 ergeben
foodCluster = 16
clusterSize = 8
clusterradius = 20
# isFood turns on food simulation,
# isPred turns on predator simulation
isFood = False
isPred = True
Both = False
# timestep
t = 0.1
# values of Agents as given in the Paper
rr = 5
# area an agent can see
As = 5000 * (rr**2)
Am = 5 * (rr**2) * t
# mutation values,
# determine how fast aspects of Agents can mutate
m_zoo_r = 5/2.0
m_zoa_r = 10/2.0
m_speed = 0.1/2.0
m_food = 0.1/2.0
m_pred = 0.1/2.0
m_noise = 0.05/2.0
# ------------------------------------------------------------------
# Ackland
# returns three lists, one for each zone,
# contaning all other agent in the zone.
# ignores al agents in the angle behind the current agent defined by blind.
# the zones zor/zoo/zoa are defined by their radii zor_r/zoo_r/zoa_r
# exactly the same as in couzin, but gets more arguments,
# because they are not global here
def neigbour_in_zones(agent, agents, zor_r, zoo_r, zoa_r, blind):
zor = []
zoo = []
zoa = []
for neighbour in agents:
# find distance in x and y direction,
# and use them to calculate the angle between
# the movement-vector of agent and
# the directiction-vector between agent and neighbour
disVecX = neighbour.point.getX() - agent.point.getX()
disVecY = neighbour.point.getY() - agent.point.getY()
alpha = calc_angle(agent.x_velocity, agent.y_velocity, disVecX, disVecY)
if (agent == neighbour):
True
elif alpha < 180 - blind and alpha > 180 + blind:
True
# if neighbour can be seen:
# get distance dis between agent and neighbour,
# and check if neighbour is in a zone of agent
else:
dis = absvec(neighbour.point.getX() - agent.point.getX(), neighbour.point.getY() - agent.point.getY())
if dis <= zor_r:
zor.append(neighbour)
elif dis <= zoo_r:
zoo.append(neighbour)
elif dis <= zoa_r:
zoa.append(neighbour)
return [zor, zoo, zoa]
# update Velocity a la couzin
# exactly the same as in couzin, but noise is
# not global and attributes are named differently
# for some reason...
def updateV_couzin(agent, zones):
dx = 0
dy = 0
#zor not empty
if zones[0] != []:
for neighbour in zones[0]:
disX = neighbour.point.getX() - agent.point.getX()
disY = neighbour.point.getY() - agent.point.getY()
rX = disX / absvec(disX, disY)
rY = disY / absvec(disX, disY)
dx += rX / absvec(rX, rY)
dy += rY / absvec(rX, rY)
dx = -dx
dy = -dy
# zoo not empty; zoa empty
elif zones[1] != [] and zones[2] == []:
for neighbour in zones[1]:
dx += neighbour.x_velocity / absvec(neighbour.x_velocity, neighbour.y_velocity)
dy += neighbour.y_velocity / absvec(neighbour.x_velocity, neighbour.y_velocity)
dx += agent.x_velocity / absvec(agent.x_velocity, agent.y_velocity)
dy += agent.y_velocity / absvec(agent.x_velocity, agent.y_velocity)
# zoo empty; zoa not empty
elif zones[1] == [] and zones[2] != []:
for neighbour in zones[2]:
disX = neighbour.point.getX() - agent.point.getX()
disY = neighbour.point.getY() - agent.point.getY()
rX = disX / absvec(disX, disY)
rY = disY / absvec(disX, disY)
dx += rX / absvec(rX, rY)
dy += rY / absvec(rX, rY)
# zoo and zoa not empty
elif zones[1] != [] and zones[2] != []:
for neighbour in zones[1]:
dx += neighbour.x_velocity / absvec(neighbour.x_velocity, neighbour.y_velocity)
dy += neighbour.y_velocity / absvec(neighbour.x_velocity, neighbour.y_velocity)
dx += agent.x_velocity / absvec(agent.x_velocity, agent.y_velocity)
dy += agent.y_velocity / absvec(agent.x_velocity, agent.y_velocity)
for neighbour in zones[2]:
disX = neighbour.point.getX() - agent.point.getX()
disY = neighbour.point.getY() - agent.point.getY()
rX = disX / absvec(disX, disY)
rY = disY / absvec(disX, disY)
dx += rX / absvec(rX, rY)
dy += rY / absvec(rX, rY)
dx = 0.5*dx
dy = 0.5*dy
# all zones empty
else:
dx = agent.x_velocity
dy = agent.y_velocity
# randomness factor / error
dx += random.uniform(-agent.noise/2, agent.noise/2)
dy += random.uniform(-agent.noise/2, agent.noise/2)
return [dx, dy]
# checks if neares food is in zor, if yes, eat it
# else checks if neares food is in zoa,
# and returns vector pointing in it´s direction
def check_food(agent, foods):
dX = 0
dY = 0
if foods == []:
return [dX,dY]
nearestFood = nearest_neighbour(agent, foods)
dis = distance(agent.point.getX(), nearestFood.point.getX(), agent.point.getY(), nearestFood.point.getY())
if dis <= agent.zor_r:
if visual:
nearestFood.point.undraw()
foods.remove(nearestFood)
agent.foodLevel += 1
elif dis <= agent.zoa_r:
disX = nearestFood.point.getX() - agent.point.getX()
disY = nearestFood.point.getY() - agent.point.getY()
rX = disX / absvec(disX, disY)
rY = disY / absvec(disX, disY)
dX = rX / absvec(rX, rY)
dY = rY / absvec(rX, rY)
return [dX, dY]
# else checks if neares predator is in zoa,
# and returns vector pointing in it´s direction
def check_predator(agent, predators):
dX=0
dY=0
if predators == []:
return [dX,dY]
nearestPred = nearest_neighbour(agent, predators)
dis = distance(agent.point.getX(), nearestPred.point.getX(), agent.point.getY(), nearestPred.point.getY())
if dis <= agent.zoa_r:
disX = nearestPred.point.getX() - agent.point.getX()
disY = nearestPred.point.getY() - agent.point.getY()
rX = disX / absvec(disX, disY)
rY = disY / absvec(disX, disY)
dX = rX / absvec(rX, rY)
dY = rY / absvec(rX, rY)
return [dX, dY]
# checks if neares prey is in zor, if yes, eat it
# else checks if neares prey is in zoa,
# and returns vector pointing in it´s direction
def check_prey(predator, agents):
dX = predator.x_velocity
dY = predator.y_velocity
if agents == []:
return [dX,dY]
nearestPrey = nearest_neighbour(predator, agents)
dis = distance(predator.point.getX(), nearestPrey.point.getX(), predator.point.getY(), nearestPrey.point.getY())
if dis <= predator.zor_r:
if visual:
nearestPrey.point.undraw()
nearestPrey.line.undraw()
agents.remove(nearestPrey)
predator.hasEaten = True
elif dis <= predator.zoa_r:
disX = nearestPrey.point.getX() - predator.point.getX()
disY = nearestPrey.point.getY() - predator.point.getY()
rX = disX / absvec(disX, disY)
rY = disY / absvec(disX, disY)
dX = rX / absvec(rX, rY)
dY = rY / absvec(rX, rY)
return [dX, dY]
# return new velocity, taking food and predators into acount
def updateV_final(agent, matrix, foods, predators):
vc = updateV_couzin(agent, matrix)
vf = check_food(agent, foods)
vp = check_predator(agent, predators)
vvX = vc[0] + agent.food_pref * vf[0] - agent.anti_pred * vp[0]
vvY = vc[1] + agent.food_pref * vf[1] - agent.anti_pred * vp[1]
return [vvX, vvY]
# ------------------------------------------------------------------
# update functions
#update agents
def update_couzin(agents, foods, predators, winWidth, winHeight, window):
# Velocity update
for agent in agents:
neigh_matrix = neigbour_in_zones(agent, agents, agent.zor_r, agent.zoo_r, agent.zoa_r, agent.blind_angle,)
agent.tempvelocity = updateV_final(agent, neigh_matrix, foods, predators)
# move, draw
for agent in agents:
# test if in ragne of agent.turn_angle, if not rotate angle by maxTurn in
# direction of new direction
radTurn = math.radians(agent.turn_angle)
negRadTurn = math.radians(360-agent.turn_angle)
# check if rotation is in range of turn-angle
agent = move_agent_couzin(agent, agent.turn_angle, radTurn, negRadTurn)
# normalise direction vector to 1, and multiply by constant speed
x = agent.x_velocity/absvec(agent.x_velocity, agent.y_velocity) * agent.speed
agent.y_velocity = agent.y_velocity/absvec(agent.x_velocity, agent.y_velocity) * agent.speed
agent.x_velocity = x
agent = checkBoundary(agent, winWidth, winHeight)
# draw agent
if visual:
agent.drawLine()
return agents
#update predator
def update_predator(predator, agents, winWidth, winHeight, window):
predator.tempvelocity = check_prey(predator, agents)
radTurn = math.radians(predator.turn_angle)
negRadTurn = math.radians(360-predator.turn_angle)
# check if rotation is in range of turn-angle
predator = move_agent_couzin(predator, predator.turn_angle, radTurn, negRadTurn)
# normalise diection vector to 1, and multiply by constant speed
x = predator.x_velocity/absvec(predator.x_velocity, predator.y_velocity) * predator.speed
predator.y_velocity = predator.y_velocity/absvec(predator.x_velocity, predator.y_velocity) * predator.speed
predator.x_velocity = x
predator = checkBoundary(predator, winWidth, winHeight)
# draw predator
if visual:
predator.drawLine()
return predator
# ------------------------------------------------------------------
# next gen functions
# mutate agent in order to get a child
def mutate(parent):
point = Point(random.uniform(0,winWidth/2), random.uniform(0,winHeight/2))
child = AcklandAgent(point, window)
# give them a random starting direction
child.x_velocity = random.uniform(-1, 1)
child.y_velocity = random.uniform(-1, 1)
child.zoa_r = random.uniform(parent.zoa_r - m_zoa_r, parent.zoa_r + m_zoa_r )
child.zoa_r = max (child.zoa_r, math.sqrt(As/(2*math.pi)) )
child.zoo_r = random.uniform(parent.zoo_r - m_zoo_r, parent.zoo_r + m_zoo_r)
child.zoo_r = min( child.zoa_r ,max(child.zoo_r, child.zor_r))
child.speed = random.uniform(parent.speed - m_speed, parent.speed + m_speed)
child.speed = min( 5 ,max(child.speed, 1))
child.blind_angle = (360 - math.degrees(As/(child.zoa_r**2)) ) / 2
child.turn_angle = math.degrees(Am/(2*(child.speed**2)))
child.food_pref = random.uniform(parent.food_pref - m_food, parent.food_pref + m_food)
child.anti_pred = random.uniform(parent.anti_pred - m_pred, parent.anti_pred + m_pred)
child.noise = random.uniform(parent.noise - m_noise, parent.noise + m_noise)
return child
# --------------------------------------------------------------
# get next generation food simulation
# generate a "wheel of fortune"
# depending on foodLevel of agent:
# has eaten more <->
# higher probability to get picked as parent
def generateWheel(agents):
wheel = []
currentsum = 0
for agent in agents:
currentsum += agent.foodLevel
wheel.append(currentsum)
return wheel
# use wheel to pick an agent to mutate
# in order to get a child
def pickParent(agents, wheel):
r = random.randrange(wheel[-1]) +1
for i in range(len(wheel)):
if r <= wheel[i]:
return agents[i]
# generate next Generation of agents for the food simulation
def nextGen_food(agents):
wheel = generateWheel(agents)
tempAgents = []
for i in range(agentNum):
if wheel[-1] == 0:
parent = pickParent_simple_random(agents)
else:
parent = pickParent(agents, wheel)
child = mutate(parent)
tempAgents.append(child)
return tempAgents
# --------------------------------------------------------------
# get next generation predator simulation
# just pick one of those that survived randomly as parent
def pickParent_simple_random(agents):
r = random.randrange(len(agents))
return agents[r]
# generate next Generation of agents for the predator simulation
def nextGen_pred(agents):
tempAgents = []
for i in range(agentNum):
parent = pickParent_simple_random(agents)
child = mutate(parent)
tempAgents.append(child)
return tempAgents
# --------------------------------------------------------------
# main
def main():
agents = []
foods = []
predators = []
# open csv to collect data
if writecsv:
with open(GENERATIONS,'w') as csvfile:
filewriter = csv.writer(csvfile, delimiter=',',quotechar='|',quoting = csv.QUOTE_MINIMAL)
filewriter.writerow(["Iterations","Speed","turn","food pref","zoa","zoo","blind","noise","antipred"])
with open(TIMESTEPS,'w') as csvfile:
filewriter = csv.writer(csvfile, delimiter=',',quotechar='|',quoting = csv.QUOTE_MINIMAL)
filewriter.writerow(["Generations","Timestep","x","y","x_velocity","y_velocity","foodlevel"])
widthTemp = winWidth
heightTemp = winHeight
maxLife = 1000
global window
# first generation , random
for i in range (agentNum):
agent = AcklandAgent(Point(random.uniform(0,winWidth/2), random.uniform(0,winHeight/2)) , window)
agents.append(agent.create_random_agent(rr, As, Am))
for j in range(generations):
if isPred or Both:
for i in range(predNum):
predator = Predator(Point(random.uniform(0,winWidth), random.uniform(0,winHeight)) , window)
predators.append(predator.create_random_predator(rr, As))
# -----ausgabe------
print ("generation " + str(j))
avgSpeed = 0
avgTurn = 0
avgFood = 0
avgPred = 0
avgZOA = 0
avgZOO = 0
avgBlind = 0
avgNoise = 0
maxSpeed = 0
maxTurnAngle = 0
for agent in agents:
avgSpeed += agent.speed
avgPred += agent.anti_pred
avgTurn += agent.turn_angle
avgFood += agent.food_pref
avgZOA += agent.zoa_r
avgZOO += agent.zoo_r
avgBlind += agent.blind_angle
avgNoise += agent.noise
if agent.speed > maxSpeed:
maxSpeed = agent.speed
if agent.turn_angle > maxTurnAngle:
maxTurnAngle = agent.turn_angle
avgSpeed /= agentNum
avgTurn /= agentNum
avgFood /= agentNum
avgZOA /= agentNum
avgZOO /= agentNum
avgBlind /= agentNum
avgNoise /= agentNum
avgPred /= agentNum
if writecsv:
for agent in agents:
with open(GENERATIONS,'a') as csvfile:
filewriter = csv.writer(csvfile, delimiter=',',quotechar='|',quoting = csv.QUOTE_MINIMAL)
filewriter.writerow([j, agent.speed, agent.turn_angle, agent.food_pref, agent.zoa_r, agent.zoo_r, agent.blind_angle, agent.noise, agent.anti_pred])
# -----ausgabe ende------
if isFood or Both:
widthTemp = winWidth/2
heightTemp = winHeight/2
else:
widthTemp = winWidth
heightTemp = winHeight
# main loop
for timeStep in range(maxTime):
# every 50 timesteps collect data
if timeStep % 50 == 0 and j % 50 == 0 and writecsv:
for agent in agents:
with open(TIMESTEPS,'a') as csvfile:
filewriter = csv.writer(csvfile, delimiter=',',quotechar='|',quoting = csv.QUOTE_MINIMAL)
filewriter.writerow([j,timeStep,agent.point.getX(),agent.point.getY(),agent.x_velocity,agent.y_velocity,agent.foodLevel])
# generate food and release agents at 1/4 of maxtime
if timeStep == maxTime * 0.025 and (isFood or Both):
foods = []
for j in range (foodCluster):
x = random.uniform(widthTemp + clusterradius, winWidth-clusterradius)
y = random.uniform(heightTemp + clusterradius, winHeight- clusterradius)
for k in range (clusterSize):
xk = random.uniform(x - clusterradius, x + clusterradius)
yk = random.uniform(y - clusterradius, y + clusterradius)
f = Food(Point(xk, yk))
if visual:
f.point.setFill("blue")
f.point.draw(window)
foods.append(f)
widthTemp = winWidth
heightTemp = winHeight
# exit generation if enough food has been eaten
if timeStep >= maxTime * 0.025 and isFood:
if len(foods) <= 1.0/8 * clusterSize * foodCluster:
break
# start predating / update predators
if timeStep >= maxTime * 0.025 and (isPred or Both):
if predators == []:
break
else:
# delete current predator if it has eaten
if(predators[-1].hasEaten or predators[-1].lifeTime >= maxLife):
if visual:
predators[-1].line.undraw()
predators.pop()
else:
predators[-1] = update_predator(predators[-1], agents, winWidth, winHeight, window)
predators[-1].lifeTime += 1
# update agents
agents = update_couzin(agents, foods, predators, widthTemp, heightTemp, window)
print ("eating time: " + str(timeStep))
# new windos and new generation
if visual:
window.close()
window = GraphWin("Window", winWidth, winHeight)
if isFood or Both:
agents = nextGen_food(agents)
elif isPred:
agents = nextGen_pred(agents)
main()