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
tobiasfremming committed Nov 3, 2024
2 parents 797eae0 + 54c8a20 commit 9ff22c4
Show file tree
Hide file tree
Showing 5 changed files with 256 additions and 13 deletions.
160 changes: 152 additions & 8 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
from src.genome import Genome
from src.globals import SCREEN_WIDTH, SCREEN_HEIGHT
from src.environment import Environment, GroundType
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.genetic_algoritm import GeneticAlgorithm
from src.globals import (
Expand Down Expand Up @@ -86,10 +92,99 @@ def create_creature(in_nodes: int,out_nodes: int):
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

Expand All @@ -109,18 +204,71 @@ def create_creature(in_nodes: int,out_nodes: int):
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
interface.handle_events(event)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
print("Left arrow pressed")
if event.key == pygame.K_RIGHT:
print("Right arrow pressed")

space.step(1/60.0)
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)

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 Down Expand Up @@ -170,9 +318,5 @@ def create_creature(in_nodes: int,out_nodes: int):







if __name__ == "__main__":
main()
38 changes: 36 additions & 2 deletions src/agent_parts/creature.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,52 @@ def __init__(self, space):
self.space = space
self.limbs = []
self.motors = []
self.relative_vectors = []

def add_limb(self, width: float, height: float, position: tuple[float,float], mass=1, color=(0, 255, 0)) -> Limb:
"""Add a limb to the creature."""
limb = Limb(self.space, width, height, position, mass, color)
self.limbs.append(limb)
return limb

def start_dragging(self, dragged_limb: Limb):
for limb in self.limbs:
if limb != dragged_limb:
vector = (limb.body.position.x - dragged_limb.body.position.x,
limb.body.position.y - dragged_limb.body.position.y)
self.relative_vectors.append((limb, vector))


def update_creature_position(self, dragged_limb: Limb, new_position: tuple[float, float]):
dragged_limb.body.position = new_position[0], new_position[1]
for limb, vector in self.relative_vectors:
new_position = (dragged_limb.body.position.x + vector[0],
dragged_limb.body.position.y + vector[1])
limb.body.position = new_position


def add_motor(self, limb_a: Limb, limb_b: Limb, anchor_a: tuple[float,float], anchor_b: tuple[float,float], rate: float, tolerance: float) -> MotorJoint|None:

def add_motor_on_limbs(self, limb_a: Limb, limb_b: Limb, position: tuple[float, float]) -> MotorJoint|None:
if(limb_a.contains_point(position) and limb_b.contains_point(position)):
anchor1 = limb_a.global_to_local(position)
anchor2 = limb_b.global_to_local(position)
print("true")
return self.add_motor(limb_a, limb_b, anchor1, anchor2, 2.0)
else:
print("false")
return None


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."""
if(abs(limb_a.body.position + anchor_a - limb_b.body.position + anchor_b) < tolerance):
global_a = self.local_to_global(limb_a, anchor_a)
global_b = self.local_to_global(limb_b, anchor_b)

# Check if the global points are within the tolerance
if abs(global_a[0] - global_b[0]) < tolerance and abs(global_a[1] - global_b[1]) < tolerance:
motor = MotorJoint(self.space, limb_a.body, limb_b.body, anchor_a, anchor_b, rate)
self.motors.append(motor)
print("add_motor: true")
return motor

def render(self, screen: pygame.display):
Expand Down
2 changes: 1 addition & 1 deletion src/agent_parts/limb.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ def render(self, screen):
vertices = self.shape.get_vertices()
vertices = [v.rotated(angle) + pos for v in vertices]
# Convert pymunk Vec2d vertices to pygame coordinates
vertices = [(int(v.x), int(v.y)) for v in vertices]
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)
65 changes: 65 additions & 0 deletions src/interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import pygame

class Button:
"""General button functions"""

def __init__(self, text, pos, width, height, font, color, hover_color, text_color, active_color = None, callback=None):
"""Initializes a button with the function name as input"""
self.text = text
self.pos = pos
self.width = width
self.height = height
self.font = font
self.color = color
self.hover_color = hover_color
self.text_color = text_color
self.active_color = active_color if active_color else color
self.rect = pygame.Rect(pos[0], pos[1], width, height)
self.callback = callback
self.toggled = False

def render(self, screen):
"""mouse_pos = pygame.mouse.get_pos()
is_hovered = self.rect.collidepoint(mouse_pos)
color = self.hover_color if is_hovered else self.color"""
current_color = self.active_color if self.toggled else self.color
pygame.draw.rect(screen, current_color, self.rect)
text_surf = self.font.render(self.text, True, self.text_color)
text_rect = text_surf.get_rect(center=self.rect.center)
screen.blit(text_surf, text_rect)

def is_clicked(self, event):
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
if self.rect.collidepoint(event.pos):
self.toggled = not self.toggled
if self.callback: # Check if there’s a function to call
self.callback() # Call the button's callback function
return True
return False


class Interface:
def __init__(self):
"""Initializes the elements in the interface"""
self.elements = []

def add_button(self, button: Button) -> Button:
self.elements.append(button)
return button

def remove_button(self, button: Button) -> Button:
self.elements.remove(button)
return button

def render(self, screen):
"""Render all UI elements."""
for element in self.elements:
element.render(screen)

def handle_events(self, event):
"""Handle events for all UI elements."""
for element in self.elements:
if isinstance(element, Button):
element.is_clicked(event) # This will call the callback if the button is clicked


4 changes: 2 additions & 2 deletions tests/test_enviroment.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from src.enviroment import Enviroment
from src.environment import Environment

def test_create_enviroment():
env = Enviroment(state="Env")
env = Environment(state="Env")
assert env is not None


Expand Down

0 comments on commit 9ff22c4

Please sign in to comment.