classes-creature3
# --- GEOMETRY --------------------------------------------------------------

from math import degrees, atan2
from math import sqrt, pow
from math import radians, sin, cos

def angle(x0, y0, x1, y1):
    """ Returns the angle between two points.
    """
    return degrees( atan2(y1-y0, x1-x0) )

def distance(x0, y0, x1, y1):
    """ Returns the distance between two points.
    """
    return sqrt(pow(x1-x0, 2) + pow(y1-y0, 2))

def coordinates(x0, y0, distance, angle):
    """ Returns the coordinates of given distance and angle from a point.
    """
    return (x0 + cos(radians(angle)) * distance,
            y0 + sin(radians(angle)) * distance)

# --- WORLD -----------------------------------------------------------------

class World:
    def __init__(self):
        self.obstacles = []

# --- OBSTACLE --------------------------------------------------------------

class Obstacle:
    def __init__(self, x, y, radius):
        self.x = x
        self.y = y
        self.radius = radius

# --- CREATURE --------------------------------------------------------------

class Creature:

    def __init__(self, world, x, y, speed=1.0, size=4):

        self.x = x
        self.y = y
        self.speed = speed
        self.size = size

        self.world = world
        self.feeler_length = 25

        self._vx = 0
        self._vy = 0

    def heading(self):

        """ Returns the creature's heading as angle in degrees.
        """

        return angle(self.x, self.y, self.x+self._vx, self.y+self._vy)

    def avoid_obstacles(self, m=0.4, collision=4):

        # Find out where the creature is going.
        a = self.heading()

        for obstacle in self.world.obstacles:

            # Calculate the distance between the creature and the obstacle.
            d = distance(self.x, self.y, obstacle.x, obstacle.y)

            # Respond faster if the creature is very close to an obstacle.
            if d - obstacle.radius < 4: m *= 10

            # Check if the tip of the feeler falls inside the obstacle.
            # This is never true if the feeler length
            # is smaller than the distance to the obstable.
            if d - obstacle.radius <= self.feeler_length:
                tip_x, tip_y = coordinates(self.x, self.y, d, a)
                if distance(obstacle.x, obstacle.y,
                            tip_x, tip_y) < obstacle.radius:

                    # Nudge the creature away from the obstacle.
                    m *= self.speed

                    if tip_x < obstacle.x: self._vx -= random(m)
                    if tip_y < obstacle.y: self._vy -= random(m)
                    if tip_x > obstacle.x: self._vx += random(m)
                    if tip_y > obstacle.y: self._vy += random(m)

                    if d - obstacle.radius < 4: return

    def roam(self):

        """ Creature changes heading aimlessly.
        With its feeler it will scan for obstacles and steer away.
        """

        self.avoid_obstacles()

        v = self.speed
        self._vx += random(-v, v)
        self._vy += random(-v, v)
        self._vx = max(-v, min(self._vx, v))
        self._vy = max(-v, min(self._vy, v))

        self.x += self._vx
        self.y += self._vy

# ---------------------------------------------------------------------------

size(550, 200)

# Create a world with obstacles at random positions.
world = World()
for i in range(15):
    obstacle = Obstacle(random(WIDTH), random(HEIGHT), random(10, 30))
    world.obstacles.append(obstacle)

# Create a number of ants.
ants = []
for i in range(4):
    ant = Creature(world, 225, 100, speed=2.0)
    ants.append(ant)

speed(30)
def draw():

    background(0.2)

    # Draw all the obstacles in the world.
    fill(0.5, 0.5)
    for obstacle in world.obstacles:
        oval(obstacle.x - obstacle.radius,
             obstacle.y - obstacle.radius,
             obstacle.radius * 2,
             obstacle.radius * 2)

    # Draw each ant and its feeler.
    stroke(1)
    fill(1, 0.5)
    for ant in ants:
        push()
        transform(CORNER)
        translate(ant.x, ant.y)
        rotate(-ant.heading())
        line(0, 0, ant.feeler_length, 0)
        pop()
        oval(ant.x-ant.size*0.5, ant.y-ant.size*0.5, ant.size, ant.size)

    # Move all the ants around.
    for ant in ants:
        ant.roam()