diff --git a/Final/python files/ackland_simulation.py b/Final/python files/ackland_simulation.py
new file mode 100644
index 0000000000000000000000000000000000000000..8ef9c755689f335597f6f37e45b3162922223676
--- /dev/null
+++ b/Final/python files/ackland_simulation.py	
@@ -0,0 +1,609 @@
+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()