Skip to content

Commit

Permalink
refactor: put vision into creature
Browse files Browse the repository at this point in the history
  • Loading branch information
tobiasfremming committed Nov 11, 2024
1 parent 8aeb354 commit 786d3bb
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 107 deletions.
41 changes: 16 additions & 25 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import random
import numpy as np
import pygame
from src.agent_parts.vision import Vision
from src.genetic_algoritm import GeneticAlgorithm
import pymunk
from pygame.locals import *
Expand Down Expand Up @@ -33,7 +34,8 @@
def create_creatures(amount, space):
creatures = []
for i in range(amount):
creature: Creature = Creature(space)
vision = Vision(Point(0, 0))
creature: Creature = Creature(space, vision)
# Add limbs to the creature, placing them above the ground
limb1 = creature.add_limb(100, 60, (300, 100), mass=1)
limb2 = creature.add_limb(100, 20, (350, 100), mass=1)
Expand All @@ -56,6 +58,7 @@ def create_creatures(amount, space):

creatures.append(creature)


return creatures

def create_population(population_size, creature: Creature):
Expand Down Expand Up @@ -144,14 +147,11 @@ def main():
environment.update()
environment.render()


# TODO: vision should be part of a creature, and not environment
inputs = np.array([environment.vision.get_near_periphery().x,
environment.vision.get_near_periphery().y,
environment.vision.get_far_periphery().x,
environment.vision.get_far_periphery().y])

for index, creature in enumerate(creatures):
inputs = np.array([creature.vision.get_near_periphery().x,
creature.vision.get_near_periphery().y,
creature.vision.get_far_periphery().x,
creature.vision.get_far_periphery().y])
network = population[index]
for joint_rate in creature.get_joint_rates():
inputs = np.append(inputs, joint_rate)
Expand All @@ -163,23 +163,14 @@ def main():
outputs = neat_networks[index].forward(inputs)
creature.set_joint_rates(outputs)

vision_y = round(creature_instance.limbs[0].body.position.y)
vision_x = round(creature_instance.limbs[0].body.position.x)

match environment.ground_type:
case GroundType.BASIC_GROUND:
environment.vision.update(
environment.screen,
Point(vision_x, vision_y),
environment.ground,
environment.offset)

case GroundType.PERLIN:
environment.vision.update(
environment.screen,
Point(vision_x, vision_y),
environment.ground,
0)
vision_y = round(creature.limbs[0].body.position.y)
vision_x = round(creature.limbs[0].body.position.x)
creature.vision.update(
Point(vision_x, vision_y),
environment.ground,
environment.offset) # if perlin, offset = 0, if basic, offset = environment.offset



#creature_instance.render(screen)
for creature in creatures:
Expand Down
21 changes: 18 additions & 3 deletions src/agent_parts/creature.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@
import pygame
from src.agent_parts.limb import Limb
from src.agent_parts.motorjoint import MotorJoint
from src.agent_parts.vision import Vision
from src.ground import Ground
from src.agent_parts.rectangle import Point



class Creature:
limbs: list[Limb]
motors: list[MotorJoint]
vision: Vision

def __init__(self, space):
def __init__(self, space, vision: Vision):
"""
Initialize a creature with an empty list of limbs and motors.
Expand All @@ -18,6 +23,7 @@ def __init__(self, space):
self.limbs = []
self.motors = []
self.relative_vectors = []
self.vision = vision

def add_limb(self, width: float, height: float, position: tuple[float,float], mass=1, color=(0, 255, 0)) -> Limb:
"""
Expand All @@ -34,6 +40,15 @@ def add_limb(self, width: float, height: float, position: tuple[float,float], ma
self.limbs.append(limb)
return limb

def update_vision(self, x: int, y: int):
"""
Update the vision position of the creature
Args:
x (int): x coordinate
y (int): y coordinate
"""
self.vision.update(x, y)

def start_dragging(self, dragged_limb: Limb):
for limb in self.limbs:
if limb != dragged_limb:
Expand Down Expand Up @@ -91,7 +106,7 @@ def render(self, screen: pygame.display):
limb.render(screen)
for motor in self.motors:
motor.render(screen, motor.pivot.a, motor.pivot.b) # Render motor joints

self.vision.render_vision(screen)
def get_joint_rates(self) -> list[float]:
"""Return the current rates of all motor joints."""
return [motor.motor.rate for motor in self.motors]
Expand Down Expand Up @@ -122,4 +137,4 @@ def get_joint_positions(self) -> list[tuple[float,float]]:
def get_limb_positions(self) -> list[tuple[float,float]]:
return [(limb.body.position.x, limb.body.position.y) for limb in self.limbs]


82 changes: 82 additions & 0 deletions src/agent_parts/vision.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import pygame as pg
from src.agent_parts.rectangle import Point
from src.ground import Ground
from src.globals import BLACK, RED



class Vision:
eye_position: Point
sight_width = 50
x_offset = 50
near_periphery: Point
far_periphery: Point

def __init__(self, eye_position: Point):
self.eye_position = eye_position
self.near_periphery = Point(0, 0)
self.far_periphery = Point(0, 0)

def update(
self,
eye_position: Point,
ground: Ground,
scroll_offset: int
) -> None:

self.eye_position = eye_position
x1 = eye_position.x + self.x_offset
x2 = x1 + self.sight_width
try:
y1=ground.get_y(x1+scroll_offset)
self.near_periphery = Point(x1, y1)
except:
pass
try:
y2=ground.get_y(x2+scroll_offset)
self.far_periphery = Point(x2, y2)
except:
pass

def render_vision(self, screen):
pg.draw.circle(
screen,
BLACK,
(self.eye_position.x, self.eye_position.y),
5,
2
)
pg.draw.line(
screen, RED, (self.eye_position.x, self.eye_position.y),
(self.near_periphery.x, self.near_periphery.y),
2
)
pg.draw.line(
screen, RED, (self.eye_position.x, self.eye_position.y),
(self.far_periphery.x, self.far_periphery.y),
2
)

def get_lower_periphery(self):
return self.near_periphery

def get_upper_periphery(self):
return self.far_periphery

def get_eye_position(self):
return self.eye_position

def get_sight_width(self):
return self.sight_width

def get_near_periphery(self) -> Point:
if self.near_periphery is None:
return Point(0, 0)
return self.near_periphery

def get_far_periphery(self) -> Point:
if self.far_periphery is None:
return Point(0, 0)
return self.far_periphery


80 changes: 1 addition & 79 deletions src/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def __init__(self, screen, space):
self.ground: Ground = self.ground_factory(self.ground_type)
self.starting_xx = 50
self.point = Point(self.starting_xx, 100)
self.vision: Vision = Vision(self.point)


self.offset = 0
self.offset_speed = 1
Expand Down Expand Up @@ -107,82 +107,4 @@ def draw_mark(surface, color, coord):
pg.draw.circle(surface, color, coord, 3)


class Vision:
eye_position: Point
sight_width = 50
x_offset = 50
near_periphery: Point
far_periphery: Point

def __init__(self, eye_position: Point):
self.eye_position = eye_position
self.near_periphery = Point(0, 0)
self.far_periphery = Point(0, 0)

def update(
self,
screen: pg.display,
eye_position: Point,
ground: Ground,
scroll_offset: int
) -> None:

self.eye_position = eye_position
x1 = eye_position.x + self.x_offset
x2 = x1 + self.sight_width
try:
y1=ground.get_y(x1+scroll_offset)
self.near_periphery = Point(x1, y1)
except:
pass
try:
y2=ground.get_y(x2+scroll_offset)
self.far_periphery = Point(x2, y2)
except:
pass

self.render_vision(screen)

def render_vision(self, screen):
pg.draw.circle(
screen,
BLACK,
(self.eye_position.x, self.eye_position.y),
5,
2
)
pg.draw.line(
screen, RED, (self.eye_position.x, self.eye_position.y),
(self.near_periphery.x, self.near_periphery.y),
2
)
pg.draw.line(
screen, RED, (self.eye_position.x, self.eye_position.y),
(self.far_periphery.x, self.far_periphery.y),
2
)

def get_lower_periphery(self):
return self.near_periphery

def get_upper_periphery(self):
return self.far_periphery

def get_eye_position(self):
return self.eye_position

def get_sight_width(self):
return self.sight_width

def get_near_periphery(self) -> Point:
if self.near_periphery is None:
return Point(0, 0)
return self.near_periphery

def get_far_periphery(self) -> Point:
if self.far_periphery is None:
return Point(0, 0)
return self.far_periphery



0 comments on commit 786d3bb

Please sign in to comment.