Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/CogitoNTNU/CrawlAI
Browse files Browse the repository at this point in the history
  • Loading branch information
Parleenb committed Nov 5, 2024
2 parents 0f4e545 + 4b2a960 commit 6be9b02
Show file tree
Hide file tree
Showing 11 changed files with 298 additions and 209 deletions.
175 changes: 10 additions & 165 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from src.render_object import RenderObject
from src.interface import Button, Interface
from src.agent_parts.limb import Limb
from src.agent_parts.rectangle import Point, Rectangle, rectangle_factory
from src.agent_parts.rectangle import Point


from src.agent_parts.rectangle import Point
Expand All @@ -33,7 +33,7 @@
def create_creatures(amount, space):
creatures = []
for i in range(amount):
creature = Creature(space)
creature: Creature = Creature(space)
# 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 @@ -45,13 +45,13 @@ def create_creatures(amount, space):
limb2,
(50, 0),
(-25, 0),
rate=-2, tolerance=30)
rate=0, tolerance=30)
creature.add_motor(
limb2,
limb3,
(37, 0),
(-23, 0),
rate=2,
rate=0,
tolerance=50)

creatures.append(creature)
Expand All @@ -73,124 +73,22 @@ def create_population(population_size, creature: Creature):
return population

def main():


#Initializes Genetic Algorithm

genetic_algorithm = GeneticAlgorithm()

def create_creature(in_nodes: int,out_nodes: int):
population = genetic_algorithm.initialize_population(200, in_nodes, out_nodes)

return population

fitness = []

# Initialize Pygame and Pymunk

pygame.init()
screen_width, screen_height = SCREEN_WIDTH, SCREEN_HEIGHT
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Pymunk Rectangle Physics")
interface = Interface()


# Track whether physics is on or off
physics_on = False
physics_value = 0

def handle_physics():
nonlocal physics_on
nonlocal physics_value
physics_value = 1/60.0 if physics_value == 0 else 0
physics_on = True if physics_value != 0 else False
print("Physics Enabled" if physics_value != 0 else "Physics Disabled")

font = pygame.font.Font(None, 20)

pause_button = Button(
text="Pause",
pos=(10, 10),
width=100,
height=30,
font=font,
color=(70, 130, 180),
hover_color=(100, 149, 237),
text_color=(255, 255, 255),
active_color=(200, 100, 100),
callback=handle_physics
)

make_limb_mode = False

def make_limb():
nonlocal make_limb_mode
make_limb_mode = not make_limb_mode

limb_button = Button(
text="Add limb",
pos=(10, 50),
width=100,
height=30,
font=font,
color=(70, 130, 180),
hover_color=(100, 149, 237),
text_color=(255, 255, 255),
active_color=(200, 100, 100),
callback=make_limb
)

interface.add_button(pause_button)


environment = Environment(screen)
environment.ground_type = GroundType.BASIC_GROUND

# Set up the Pymunk space
space = pymunk.Space()
space.gravity = (0, 981) # Gravity pointing downward

for environment_segment in environment.ground.terrain_segments:
environment_segment.init_pymunk_polygon(space)

creature = Creature(space)

# Add limbs to the creature, placing them above the ground
#limb1 = creature.add_limb(100, 20, (300, 100), mass=1) # Positioned above the ground
#limb2 = creature.add_limb(100, 20, (350, 100), mass=1) # Positioned above the ground
#limb3 = creature.add_limb(110, 20, (400, 100), mass=5)

# Add a motor between limbs
#creature.add_motor(limb1, limb2, (50, 0), (-25, 0), rate=2, tolerance=30)
#creature.add_motor(limb2, limb3, (37, 0), (-23, 0), rate=-2, tolerance=50)

# Add limbs to the creature
limb1 = creature.add_limb(100, 20, (300, 300), mass=1)
limb2 = creature.add_limb(100, 20, (350, 300), mass=3)
limb3 = creature.add_limb(80, 40, (400, 300), mass=5)

# Add motors between limbs
#creature.add_motor(limb1, limb2, (25, 0), (-25, 0), rate=2)
#creature.add_motor(limb2, limb3, (37, 0), (-23, 0), rate=-2)
creature.add_motor_on_limbs(limb1, limb2, (325, 300))
creature.add_motor_on_limbs(limb2, limb3, (375, 300))

#dragging creature properties
dragging = False
dragged_limb = None
drag_offset = []

# creating rectangles properties
start_pos = None
end_pos = None
limbs = []

environment = Environment(screen, space)
environment.ground_type = GroundType.PERLIN
environment: Environment = Environment(screen, space)
environment.ground_type = GroundType.BASIC_GROUND

population_size = 10
creatures = create_creatures(population_size, space)
creature_instance = creatures[0]
creatures: list[Creature] = create_creatures(population_size, space)
creature_instance: Creature = creatures[0]
population = create_population(population_size, creature_instance)
neat_networks: list[NEATNetwork] = []
for genome in population:
Expand All @@ -210,65 +108,12 @@ def make_limb():
print("Left arrow pressed")
if event.key == pygame.K_RIGHT:
print("Right arrow pressed")
if event.key == pygame.K_SPACE:
handle_physics()
elif event.type == pygame.MOUSEBUTTONDOWN:
if not physics_on:
mouse_x, mouse_y = event.pos
mouse_pos = (mouse_x, mouse_y)
# For dragging creature: Check if the mouse is over any limb
if not make_limb_mode:
for limb in creature.limbs:
if limb.contains_point(mouse_pos):
dragging = True
dragged_limb = limb
creature.start_dragging(dragged_limb)
drag_offset = (limb.body.position.x - mouse_x, limb.body.position.y - mouse_y)
break
# For creating rectangles
elif make_limb_mode:
start_pos = mouse_pos

elif event.type == MOUSEMOTION and make_limb_mode:
mouse_x, mouse_y = event.pos
mouse_pos = (mouse_x, mouse_y)
end_pos = mouse_pos

elif event.type == pygame.MOUSEBUTTONUP:
dragging = False
if make_limb_mode and start_pos and end_pos:
width = abs(start_pos[0] - end_pos[0])
height = abs(start_pos[1] - end_pos[1])
position = ((start_pos[0] + end_pos[0]) / 2, (start_pos[1] + end_pos[1]) / 2)
limb = creature.add_limb(width, height, position)

# Reset start and end positions
start_pos = None
end_pos = None


space.step(physics_value)

if dragging and dragged_limb and not make_limb_mode:
mouse_x, mouse_y = pygame.mouse.get_pos()
new_position = (mouse_x + drag_offset[0], mouse_y + drag_offset[1])
creature.update_creature_position(dragged_limb, new_position)

space.step(1/60.0)
screen.fill((135, 206, 235))
environment.update()
environment.render()

#creature.set_joint_rates([random.random()*2, random.random()*2])
# Render the creature
creature.render(screen)

if not physics_on:
interface.add_button(limb_button)
else:
interface.remove_button(limb_button)
interface.render(screen)



# TODO: vision should be part of a creature, and not environment
inputs = np.array([environment.vision.get_near_periphery().x,
Expand All @@ -286,7 +131,7 @@ def make_limb():
inputs = np.append(inputs, limb.body.position.y)

outputs = neat_networks[index].forward(inputs)
creature.set_joint_rates(outputs)
#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)
Expand Down
1 change: 0 additions & 1 deletion src/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

from src.agent_parts_old.limb import Limb, LimbType, limb_factory
from src.agent_parts_old.creature import Creature, creature_factory
from CrawlAI.src.agent_parts.rectangle import Rectangle, rectangle_factory


class Agent(ABC):
Expand Down
19 changes: 16 additions & 3 deletions src/agent_parts/creature.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import pymunk
import pygame
from src.agent_parts.limb import Limb
from src.agent_parts.motorjoint import MotorJoint
from agent_parts.limb import Limb
from agent_parts.motorjoint import MotorJoint


class Creature:

limbs: list[Limb]
motors: list[MotorJoint]

def __init__(self, space):
"""Initialize a creature with an empty list of limbs and motors."""
self.space = space
Expand Down Expand Up @@ -46,6 +48,11 @@ def add_motor_on_limbs(self, limb_a: Limb, limb_b: Limb, position: tuple[float,
print("false")
return None

def local_to_global(self, limb: Limb, anchor: tuple[float,float]) -> tuple[float,float]:
"""Convert a local anchor point to a global anchor point."""
return limb.body.local_to_world(anchor)



def add_motor(self, limb_a: Limb, limb_b: Limb, anchor_a: tuple[float,float], anchor_b: tuple[float,float], rate = 0.0, tolerance = 30) -> MotorJoint|None:
"""Add a motor connecting two limbs."""
Expand All @@ -58,7 +65,13 @@ def add_motor(self, limb_a: Limb, limb_b: Limb, anchor_a: tuple[float,float], an
self.motors.append(motor)
print("add_motor: true")
return motor

def local_to_global(self, limb: Limb, point: tuple[float, float]) -> tuple[float, float]|None:
return limb.local_to_global(point)

def global_to_local(self, limb: Limb, point: tuple[float, float]) -> tuple[float, float]|None:
return limb.global_to_local(point)

def render(self, screen: pygame.display):
"""Render the entire creature by rendering all limbs."""
for limb in self.limbs:
Expand Down
44 changes: 42 additions & 2 deletions src/agent_parts/limb.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@


class Limb:
def __init__(self, space, width, height, position, mass=1, color=(0, 255, 0)):
def __init__(self, space, width, height, position, mass=3, color=(0, 255, 0)):
"""Initialize a limb as a rectangular body."""
self.width = width
self.height = height
Expand Down Expand Up @@ -36,4 +36,44 @@ def render(self, screen):
# Convert pymunk Vec2d vertices to pygame coordinates
vertices = [(float(v.x), float(v.y)) for v in vertices]
# Draw the polygon onto the screen
pygame.draw.polygon(surface=screen, color=(0, 255, 0), points=vertices, width=0)
pygame.draw.polygon(surface=screen, color=(0, 255, 0), points=vertices, width=0)

def contains_point(self, point: tuple[float,float]) -> bool:
"""
Check if a given point is inside the limb.
Args:
- point: A tuple representing the x and y coordinates of the point.
Returns:
- True if the point is inside the limb, False otherwise.
"""
x, y = point
point_vec = pymunk.Vec2d(x, y)

# Perform a point query to check if the point is within the shape
return self.shape.point_query(point_vec).distance <= 0

def global_to_local(self, position: tuple[float, float]) -> tuple[float,float]|None:
if not isinstance(position, (tuple, list)) or len(position) != 2:
raise ValueError("Position must be a tuple or list with two elements: (x, y)")

# Convert position to Vec2d
global_position = pymunk.Vec2d(position[0], position[1])

# Transform from global to local coordinates
local_position = self.body.world_to_local(global_position)

return float(local_position.x), float(local_position.y)

def local_to_global(self, position: tuple[float, float]) -> tuple[float, float]|None:
if not isinstance(position, (tuple, list)) or len(position) != 2:
raise ValueError("Position must be a tuple or list with two elements: (x, y)")

# Convert position to Vec2d
local_position = pymunk.Vec2d(position[0], position[1])

# Transform from local to global coordinates
global_position = self.body.local_to_world(local_position)

return float(global_position.x), float(global_position.y)
2 changes: 1 addition & 1 deletion src/agent_parts/motorjoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def render(self, screen, body_a, body_b):
pygame.draw.circle(
surface=screen,
color=(255, 0, 0),
point=(int(pos_a_world.x),
center=(int(pos_a_world.x),
int(pos_a_world.y)),
radius=3)

Expand Down
2 changes: 1 addition & 1 deletion src/agent_parts/rectangle.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import pygame
import pymunk
import math
from src.render_object import RenderObject
from render_object import RenderObject


class Point:
Expand Down
Loading

0 comments on commit 6be9b02

Please sign in to comment.