Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bf5729246 | ||
|
|
b34cee0806 | ||
|
|
dcd1826f1a | ||
|
|
8bb0e53177 | ||
|
|
e8ce216d8c | ||
|
|
29602d038c | ||
|
|
9a2ff0163f | ||
|
|
a2d0ace9e9 | ||
|
|
4b78f8bca5 | ||
|
|
c82c4da27c | ||
|
|
ae0f9b797e | ||
|
|
136f888631 |
+2
-1
@@ -1 +1,2 @@
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
|
.venv/
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
## How to run
|
||||||
|
1. Install Python 3.12
|
||||||
|
2. Install necessary libraries using `pip install -r requirements.txt`
|
||||||
|
3. Edit parametres in `main.py` (Optionally)
|
||||||
|
4. Run simulation using `py main.py` or `python3 main.py`
|
||||||
|
|
||||||
|
## Note
|
||||||
|
You can see the generated fitness statistics plot in fitness_plot.png after closing the game.
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
+140
-97
@@ -3,62 +3,88 @@ import random
|
|||||||
|
|
||||||
WIDTH, HEIGHT = 800, 600
|
WIDTH, HEIGHT = 800, 600
|
||||||
HUD_HEIGHT = 150
|
HUD_HEIGHT = 150
|
||||||
|
BARRIER_SEED = 20
|
||||||
|
|
||||||
class GameObject:
|
class GameObject:
|
||||||
|
"""
|
||||||
|
Base class for all game objects.
|
||||||
|
Handles image loading, positioning, drawing, and child objects.
|
||||||
|
"""
|
||||||
x = 0
|
x = 0
|
||||||
y = 0
|
y = 0
|
||||||
|
|
||||||
def __init__(self, image_path, x=0, y=0, offset = (0, 0)):
|
def __init__(self, image_path, x=0, y=0, offset=(0, 0)):
|
||||||
self.children = []
|
self.children = []
|
||||||
self.image = pygame.image.load(image_path) # Load the image
|
self.image = pygame.image.load(image_path)
|
||||||
self.rect = self.image.get_rect(topleft=(x,y)) # Set the rectangle for the image
|
self.rect = self.image.get_rect(topleft=(x, y))
|
||||||
self.set_position(x, y) # Set initial position
|
self.set_position(x, y)
|
||||||
self.offset = offset
|
self.offset = offset
|
||||||
|
|
||||||
def draw(self, surface):
|
def draw(self, surface):
|
||||||
surface.blit(self.image, self.rect) # Draw the image on the surface
|
# Draw self and all children recursively
|
||||||
if len(self.children) == 0: return
|
surface.blit(self.image, self.rect)
|
||||||
|
if not self.children:
|
||||||
|
return
|
||||||
for child in self.children:
|
for child in self.children:
|
||||||
child.draw(surface)
|
child.draw(surface)
|
||||||
|
|
||||||
def set_position(self, x, y):
|
def set_position(self, x, y):
|
||||||
self.rect.x = x # Update rectangle position
|
# Set position and update children positions with offsets
|
||||||
|
self.rect.x = x
|
||||||
self.rect.y = y
|
self.rect.y = y
|
||||||
self.x = x # Update object position
|
self.x = x
|
||||||
self.y = y
|
self.y = y
|
||||||
if len(self.children) == 0: return
|
if not self.children:
|
||||||
|
return
|
||||||
for child in self.children:
|
for child in self.children:
|
||||||
child.set_position(x + child.offset[0], y + child.offset[1])
|
child.set_position(x + child.offset[0], y + child.offset[1])
|
||||||
|
|
||||||
def move(self, dx, dy):
|
def move(self, dx, dy):
|
||||||
self.set_position(self.rect.x + dx, self.rect.y + dy) # Move object by dx and dy
|
# Move object by delta and update children accordingly
|
||||||
if len(self.children) == 0: return
|
self.set_position(self.rect.x + dx, self.rect.y + dy)
|
||||||
|
if not self.children:
|
||||||
|
return
|
||||||
for child in self.children:
|
for child in self.children:
|
||||||
self.set_position(self.rect.x + dx, self.rect.y + dy)
|
self.set_position(self.rect.x + dx, self.rect.y + dy)
|
||||||
|
|
||||||
def set_size(self, width, height):
|
def set_size(self, width, height):
|
||||||
self.image = pygame.transform.scale(self.image, (width,height)) # Resize the image
|
# Resize image and update rect
|
||||||
self.rect = self.image.get_rect(topleft=(self.x,self.y)) # Update rectangle size
|
self.image = pygame.transform.scale(self.image, (width, height))
|
||||||
|
self.rect = self.image.get_rect(topleft=(self.x, self.y))
|
||||||
|
|
||||||
def scale_by(self, scale):
|
def scale_by(self, scale):
|
||||||
self.image = pygame.transform.scale(self.image, (self.image.get_width() * scale, self.image.get_height() * scale)) # Scale the image
|
# Scale image by a factor
|
||||||
self.rect = self.image.get_rect(topleft=(self.x,self.y)) # Update rectangle size
|
new_size = (self.image.get_width() * scale, self.image.get_height() * scale)
|
||||||
|
self.image = pygame.transform.scale(self.image, new_size)
|
||||||
|
self.rect = self.image.get_rect(topleft=(self.x, self.y))
|
||||||
|
|
||||||
def get_position(self):
|
def get_position(self):
|
||||||
return (self.x, self.y) # Return current position
|
return (self.x, self.y)
|
||||||
|
|
||||||
def set_children(self, child):
|
def set_children(self, child):
|
||||||
|
# Add a child GameObject
|
||||||
self.children.append(child)
|
self.children.append(child)
|
||||||
def recolor(self, color): #Method to recolor
|
|
||||||
|
def recolor(self, color):
|
||||||
|
# Apply color tint to image
|
||||||
self.image.fill(color, special_flags=pygame.BLEND_MULT)
|
self.image.fill(color, special_flags=pygame.BLEND_MULT)
|
||||||
self.image.set_colorkey(None) # Explicitly disable colorkey
|
self.image.set_colorkey(None)
|
||||||
|
|
||||||
class Horse(GameObject):
|
class Horse(GameObject):
|
||||||
default_vacceleration = 0.2 # Default acceleration value
|
"""
|
||||||
vspeed = 0 # Vertical speed
|
Represents a horse character with vertical movement, animation, and fitness tracking.
|
||||||
vacceleration = 0 # Vertical acceleration
|
Supports acceleration, speed, and stopping.
|
||||||
stopped = False # Whether the horse is stopped
|
"""
|
||||||
color = () # Color of the horse's mark
|
default_vacceleration = 0.2
|
||||||
ribbon_fill = None
|
|
||||||
ribbon_out = None
|
|
||||||
def __init__(self, image_path, x, y):
|
def __init__(self, image_path, x, y):
|
||||||
super().__init__(image_path, x, y)
|
super().__init__(image_path, x, y)
|
||||||
self.images = []
|
# Load animation frames
|
||||||
self.images.append(pygame.image.load("images/Horse_1.png"))
|
self.images = [
|
||||||
self.images.append(pygame.image.load("images/Horse_2.png"))
|
pygame.image.load("images/Horse_1.png"),
|
||||||
self.images.append(pygame.image.load("images/Horse_3.png"))
|
pygame.image.load("images/Horse_2.png"),
|
||||||
|
pygame.image.load("images/Horse_3.png")
|
||||||
|
]
|
||||||
self.animation_speed = 25
|
self.animation_speed = 25
|
||||||
self.frame_counter = 0
|
self.frame_counter = 0
|
||||||
self.current_frame = 0
|
self.current_frame = 0
|
||||||
@@ -66,126 +92,143 @@ class Horse(GameObject):
|
|||||||
self.vacceleration = 0
|
self.vacceleration = 0
|
||||||
self.stopped = False
|
self.stopped = False
|
||||||
self.fitness = 0
|
self.fitness = 0
|
||||||
self.color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) # Random color for the mark
|
# Assign random color for ribbons
|
||||||
|
self.color = (random.randint(0,255), random.randint(0,255), random.randint(0,255))
|
||||||
self.ribbon_fill = GameObject("images/Ribbon_fill.png", self.x, self.y, (16, -1))
|
self.ribbon_fill = GameObject("images/Ribbon_fill.png", self.x, self.y, (16, -1))
|
||||||
self.ribbon_fill.recolor(self.color)
|
self.ribbon_fill.recolor(self.color)
|
||||||
self.ribbon_out = GameObject("images/Ribbon_out.png", self.x, self.y, (14, -2))
|
self.ribbon_out = GameObject("images/Ribbon_out.png", self.x, self.y, (14, -2))
|
||||||
self.set_children(self.ribbon_fill)
|
self.set_children(self.ribbon_fill)
|
||||||
self.set_children(self.ribbon_out)
|
self.set_children(self.ribbon_out)
|
||||||
# self.set_size(75, 75) # Set the size of the horse
|
|
||||||
def apply_vspeed(self):
|
def apply_vspeed(self):
|
||||||
pos = self.get_position()
|
# Move vertically by current speed
|
||||||
self.set_position(pos[0], pos[1] + self.vspeed) # Apply vertical speed to position
|
self.set_position(self.x, self.y + self.vspeed)
|
||||||
|
|
||||||
def apply_vacceleration(self):
|
def apply_vacceleration(self):
|
||||||
self.vspeed += self.vacceleration # Apply acceleration to speed
|
# Update speed by acceleration
|
||||||
|
self.vspeed += self.vacceleration
|
||||||
|
|
||||||
def set_vspeed(self, speed):
|
def set_vspeed(self, speed):
|
||||||
self.vspeed = speed # Set vertical speed
|
self.vspeed = speed
|
||||||
|
|
||||||
def add_vspeed(self, delta):
|
def add_vspeed(self, delta):
|
||||||
self.vspeed += delta # Add to vertical speed
|
self.vspeed += delta
|
||||||
|
|
||||||
def set_vacceleration(self, vacceleration):
|
def set_vacceleration(self, vacceleration):
|
||||||
self.vacceleration = vacceleration # Set vertical acceleration
|
self.vacceleration = vacceleration
|
||||||
|
|
||||||
def up(self):
|
def up(self):
|
||||||
self.set_vacceleration(-self.default_vacceleration) # Move up
|
# Accelerate upward
|
||||||
|
self.set_vacceleration(-self.default_vacceleration)
|
||||||
|
|
||||||
def down(self):
|
def down(self):
|
||||||
self.set_vacceleration(self.default_vacceleration) # Move down
|
# Accelerate downward
|
||||||
|
self.set_vacceleration(self.default_vacceleration)
|
||||||
|
|
||||||
def stay(self):
|
def stay(self):
|
||||||
self.set_vacceleration(0) # Stop vertical movement
|
# No vertical acceleration
|
||||||
|
self.set_vacceleration(0)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.stopped = True # Stop the horse
|
# Stop all movement
|
||||||
|
self.stopped = True
|
||||||
self.set_vacceleration(0)
|
self.set_vacceleration(0)
|
||||||
self.set_vspeed(0)
|
self.set_vspeed(0)
|
||||||
def draw(self, surface):
|
|
||||||
super().draw(surface) # Draw the horse
|
|
||||||
#pygame.draw.rect(surface, self.color, (self.x + 40, self.y + 20, 25, 25)) # Draw the horse's mark
|
|
||||||
def update_animation(self):
|
def update_animation(self):
|
||||||
|
# Animate horse by cycling frames
|
||||||
self.frame_counter += 1
|
self.frame_counter += 1
|
||||||
if self.frame_counter >= self.animation_speed:
|
if self.frame_counter >= self.animation_speed:
|
||||||
self.frame_counter = 0
|
self.frame_counter = 0
|
||||||
self.current_frame = (self.current_frame + 1) % len(self.images)
|
self.current_frame = (self.current_frame + 1) % len(self.images)
|
||||||
self.image = self.images[self.current_frame]
|
self.image = self.images[self.current_frame]
|
||||||
|
|
||||||
def count_fitness(self):
|
def count_fitness(self):
|
||||||
if self.stopped == False:
|
# Increase fitness if moving; penalize if speed zero
|
||||||
|
if not self.stopped:
|
||||||
self.fitness += 1
|
self.fitness += 1
|
||||||
if self.vspeed == 0: self.fitness -= 0.9
|
if self.vspeed == 0:
|
||||||
|
self.fitness -= 0.9
|
||||||
|
|
||||||
class Background(GameObject):
|
class Background(GameObject):
|
||||||
|
"""
|
||||||
|
Background image for the game scene.
|
||||||
|
"""
|
||||||
def __init__(self, image_path, x, y):
|
def __init__(self, image_path, x, y):
|
||||||
super().__init__(image_path, x, y) # Initialize background
|
super().__init__(image_path, x, y)
|
||||||
|
|
||||||
class Barrier(GameObject):
|
class Barrier(GameObject):
|
||||||
|
"""
|
||||||
|
Obstacles that move horizontally and interact with horses.
|
||||||
|
"""
|
||||||
def __init__(self, image_path, x, y):
|
def __init__(self, image_path, x, y):
|
||||||
super().__init__(image_path, x, y) # Initialize barrier
|
super().__init__(image_path, x, y)
|
||||||
self.scale_by(5)
|
self.scale_by(5) # Make barrier larger for visibility
|
||||||
|
|
||||||
class UI:
|
class UI:
|
||||||
|
"""
|
||||||
|
Handles HUD display and horse status markers.
|
||||||
|
"""
|
||||||
iteration_num = 0
|
iteration_num = 0
|
||||||
horses = [] # List of child objects
|
horses = []
|
||||||
horizontal_offset = 25 # Horizontal spacing between marks
|
horizontal_offset = 25
|
||||||
vertical_offset = -25 # Vertical spacing between marks
|
vertical_offset = -25
|
||||||
mark_size = 25 # Size of the marks
|
mark_size = 25
|
||||||
|
|
||||||
def __init__(self, hud_image_path, x=0, y=HEIGHT):
|
def __init__(self, hud_image_path, x=0, y=HEIGHT):
|
||||||
self.horses = [] # Initialize children list
|
self.horses = []
|
||||||
self.hud_image = pygame.image.load(hud_image_path)
|
self.hud_image = pygame.image.load(hud_image_path)
|
||||||
self.hud_rect = self.hud_image.get_rect(topleft=(x,y))
|
self.hud_rect = self.hud_image.get_rect(topleft=(x, y))
|
||||||
|
|
||||||
def add_horses(self, children):
|
def add_horses(self, children):
|
||||||
new_horses = [child for child in children if child not in self.horses] # Add new children
|
# Add horses to UI tracking list
|
||||||
self.horses.extend(new_horses)
|
self.horses.extend([child for child in children if child not in self.horses])
|
||||||
|
|
||||||
def draw_marks(self, screen):
|
def draw_marks(self, screen):
|
||||||
active_marks = [horse for horse in self.horses if not horse.stopped]
|
# Draw colored squares representing active and stopped horses
|
||||||
unactive_marks = [horse for horse in self.horses if horse.stopped]
|
active = [h for h in self.horses if not h.stopped]
|
||||||
|
inactive = [h for h in self.horses if h.stopped]
|
||||||
width = 10 # Number of marks per row
|
|
||||||
|
xa, ya = 0, HEIGHT + HUD_HEIGHT - self.mark_size
|
||||||
# Отрисовка активных меток
|
for i, horse in enumerate(active):
|
||||||
xa = 0 # Horizontal position for active marks
|
|
||||||
ya = HEIGHT + HUD_HEIGHT - self.mark_size # Vertical position for active marks
|
|
||||||
|
|
||||||
for i, horse in enumerate(active_marks):
|
|
||||||
pygame.draw.rect(screen, horse.color, (xa, ya, self.mark_size, self.mark_size))
|
pygame.draw.rect(screen, horse.color, (xa, ya, self.mark_size, self.mark_size))
|
||||||
xa += self.horizontal_offset # Move to the next position
|
xa += self.horizontal_offset
|
||||||
|
if (i+1) % 10 == 0:
|
||||||
# Move to the next row if the row is full
|
|
||||||
if (i + 1) % width == 0: # Check if the next mark would be on a new row
|
|
||||||
ya += self.vertical_offset
|
ya += self.vertical_offset
|
||||||
xa = 0 # Reset horizontal position for the new row
|
xa = 0
|
||||||
|
|
||||||
# Отрисовка неактивных меток
|
xu, yu = WIDTH - self.mark_size, HEIGHT + HUD_HEIGHT - self.mark_size
|
||||||
xu = WIDTH - self.mark_size # Horizontal position for inactive marks
|
for i, horse in enumerate(inactive):
|
||||||
yu = HEIGHT + HUD_HEIGHT - self.mark_size # Vertical position for inactive marks
|
|
||||||
|
|
||||||
for i, horse in enumerate(unactive_marks):
|
|
||||||
pygame.draw.rect(screen, horse.color, (xu, yu, self.mark_size, self.mark_size))
|
pygame.draw.rect(screen, horse.color, (xu, yu, self.mark_size, self.mark_size))
|
||||||
xu -= self.horizontal_offset # Move to the next position
|
xu -= self.horizontal_offset
|
||||||
|
if (i+1) % 10 == 0:
|
||||||
# Move to the next row if the row is full
|
|
||||||
if (i + 1) % width == 0: # Check if the next mark would be on a new row
|
|
||||||
yu += self.vertical_offset
|
yu += self.vertical_offset
|
||||||
xu = WIDTH - self.mark_size # Reset horizontal position for the new row
|
xu = WIDTH - self.mark_size
|
||||||
|
|
||||||
def draw(self, surface):
|
def draw(self, surface):
|
||||||
|
# Draw HUD image
|
||||||
surface.blit(self.hud_image, self.hud_rect)
|
surface.blit(self.hud_image, self.hud_rect)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Spawner:
|
class Spawner:
|
||||||
active = True # Whether the spawner is active
|
"""
|
||||||
delay = 500 # Number of ticks between spawns
|
Spawns barriers at intervals to challenge horses.
|
||||||
tick_counter = 0 # Counter for ticks
|
"""
|
||||||
|
active = True
|
||||||
|
delay = 500
|
||||||
|
tick_counter = 0
|
||||||
|
|
||||||
def __init__(self, barrier_image_path, delay):
|
def __init__(self, barrier_image_path, delay):
|
||||||
self.barrier_image_path = barrier_image_path # Path to the barrier image
|
self.barrier_image_path = barrier_image_path
|
||||||
self.delay = delay
|
self.delay = delay
|
||||||
|
|
||||||
def handle(self):
|
def handle(self):
|
||||||
|
# Increment tick counter and spawn barrier when delay reached
|
||||||
if self.active:
|
if self.active:
|
||||||
self.tick_counter += 1 # Increment tick counter
|
self.tick_counter += 1
|
||||||
if self.tick_counter >= self.delay: # Check if it's time to spawn
|
if self.tick_counter >= self.delay:
|
||||||
self.spawn()
|
self.spawn()
|
||||||
self.tick_counter = 0 # Reset tick counter
|
self.tick_counter = 0
|
||||||
|
|
||||||
def spawn(self):
|
def spawn(self):
|
||||||
random_y = random.randint(0, HEIGHT - 100) # Random Y position for the barrier
|
# Create a new barrier at a random vertical position on the right edge
|
||||||
new_barrier = Barrier(self.barrier_image_path, WIDTH, random_y) # Create a new barrier
|
return Barrier(self.barrier_image_path, WIDTH, random.randint(0, HEIGHT - 100))
|
||||||
return new_barrier
|
|
||||||
|
|||||||
@@ -29,12 +29,15 @@ class GeneticAlgorithm:
|
|||||||
print(self.device)
|
print(self.device)
|
||||||
self.initialize_population()
|
self.initialize_population()
|
||||||
|
|
||||||
|
# create random individual
|
||||||
def create_ind(self):
|
def create_ind(self):
|
||||||
return NeuralNetwork(self.inputSize).to(self.device)
|
return NeuralNetwork(self.inputSize).to(self.device)
|
||||||
|
|
||||||
|
# create random population
|
||||||
def initialize_population(self):
|
def initialize_population(self):
|
||||||
self.population = [self.create_ind() for _ in range(self.populationSize)]
|
self.population = [self.create_ind() for _ in range(self.populationSize)]
|
||||||
|
|
||||||
|
# perform crossofer operation over 2 parents
|
||||||
def crossover(self, parent1: NeuralNetwork, parent2: NeuralNetwork):
|
def crossover(self, parent1: NeuralNetwork, parent2: NeuralNetwork):
|
||||||
child1 = self.create_ind()
|
child1 = self.create_ind()
|
||||||
child2 = self.create_ind()
|
child2 = self.create_ind()
|
||||||
@@ -55,22 +58,28 @@ class GeneticAlgorithm:
|
|||||||
param.data += torch.randn_like(param.data) * 0.1
|
param.data += torch.randn_like(param.data) * 0.1
|
||||||
return model
|
return model
|
||||||
|
|
||||||
|
# learn using given fitness for all neural networks
|
||||||
def learn(self, fitness: list):
|
def learn(self, fitness: list):
|
||||||
sortedFitnessArg = np.argsort(fitness)[::-1]
|
sortedFitnessArg = np.argsort(fitness)[::-1]
|
||||||
self.fitnessBest.append(fitness[sortedFitnessArg[0]])
|
self.fitnessBest.append(fitness[sortedFitnessArg[0]])
|
||||||
|
# print best fitness for this population
|
||||||
print(self.fitnessBest[-1])
|
print(self.fitnessBest[-1])
|
||||||
self.population = [self.population[x] for x in sortedFitnessArg]
|
self.population = [self.population[x] for x in sortedFitnessArg]
|
||||||
numBest = int(self.populationSize * self.percentageBest)
|
numBest = int(self.populationSize * self.percentageBest)
|
||||||
|
# left only best individuals
|
||||||
self.population = self.population[:numBest]
|
self.population = self.population[:numBest]
|
||||||
|
# perform crossover
|
||||||
while len(self.population) < self.populationSize - self.populationSize * self.percentageNew:
|
while len(self.population) < self.populationSize - self.populationSize * self.percentageNew:
|
||||||
parent1, parent2 = np.random.choice(self.population), np.random.choice(self.population)
|
parent1, parent2 = np.random.choice(self.population), np.random.choice(self.population)
|
||||||
child1, child2 = self.crossover(parent1, parent2)
|
child1, child2 = self.crossover(parent1, parent2)
|
||||||
child1 = self.mutate(child1)
|
child1 = self.mutate(child1)
|
||||||
child2 = self.mutate(child2)
|
child2 = self.mutate(child2)
|
||||||
self.population.extend([child1, child2])
|
self.population.extend([child1, child2])
|
||||||
|
# add new individuals
|
||||||
while len(self.population) < self.populationSize: self.population.append(self.create_ind())
|
while len(self.population) < self.populationSize: self.population.append(self.create_ind())
|
||||||
while len(self.population) > self.populationSize: self.population.pop()
|
while len(self.population) > self.populationSize: self.population.pop()
|
||||||
|
|
||||||
|
# return predicted direction for desired horse
|
||||||
def predict(self, data: list, i):
|
def predict(self, data: list, i):
|
||||||
data = torch.tensor(data, requires_grad=False).float().to(self.device)
|
data = torch.tensor(data, requires_grad=False).float().to(self.device)
|
||||||
return self.population[i](data)
|
return self.population[i](data)
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 62 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 892 B After Width: | Height: | Size: 1.3 KiB |
@@ -2,23 +2,36 @@ import pygame
|
|||||||
import sys
|
import sys
|
||||||
from gameobjects import *
|
from gameobjects import *
|
||||||
from genetic_alg import GeneticAlgorithm
|
from genetic_alg import GeneticAlgorithm
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use('Agg')
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import random
|
||||||
|
|
||||||
POPULATION_SIZE = 100
|
POPULATION_SIZE = 50 # Number of horses in population
|
||||||
MUTATION_RATE = 0.5
|
MUTATION_RATE = 0.5
|
||||||
POPULATION_NEW = 0.1
|
POPULATION_NEW = 0.1
|
||||||
POPULATION_BEST = 0.3
|
POPULATION_BEST = 0.3
|
||||||
FRAME_RATE = 300 # TODO: FIX THE INCORRECT FRAME RATE CORRELATION
|
FRAME_RATE = 300
|
||||||
BARRIER_SPEED = 10
|
BARRIER_SPEED = 10
|
||||||
BARRIER_DELAY = 100
|
BARRIER_DELAY = 100
|
||||||
|
MAX_ITERATIONS = 50
|
||||||
|
BARRIER_SEED = 42
|
||||||
|
|
||||||
|
current_iteration = 1
|
||||||
|
best_fitnesses = []
|
||||||
|
running_fitnesses = []
|
||||||
|
|
||||||
pygame.init()
|
pygame.init()
|
||||||
|
|
||||||
screen = pygame.display.set_mode((WIDTH, HEIGHT + HUD_HEIGHT)) # Initialize the screen
|
screen = pygame.display.set_mode((WIDTH, HEIGHT + HUD_HEIGHT))
|
||||||
pygame.display.set_caption("NIC_Project") # Set window title
|
pygame.display.set_caption("NIC_Project")
|
||||||
|
font = pygame.font.SysFont("Arial", 36)
|
||||||
|
|
||||||
WHITE = (255, 255, 255) # Define white color
|
WHITE = (255, 255, 255)
|
||||||
|
BLACK = (0, 0, 0)
|
||||||
|
TEXT_COLOUR = (10, 20, 200)
|
||||||
|
|
||||||
UI = UI("images/HUD.png") # Initialize UI
|
UI = UI("images/HUD.png")
|
||||||
gameobjects = []
|
gameobjects = []
|
||||||
horses = []
|
horses = []
|
||||||
barriers = []
|
barriers = []
|
||||||
@@ -30,15 +43,17 @@ last_barrier = None
|
|||||||
|
|
||||||
def init_game():
|
def init_game():
|
||||||
global gameobjects, horses, barriers, upper_bound_rect, lower_bound_rect, spawner, last_barrier, BARRIER_SPEED
|
global gameobjects, horses, barriers, upper_bound_rect, lower_bound_rect, spawner, last_barrier, BARRIER_SPEED
|
||||||
|
# Initialize background and game objects for a new iteration
|
||||||
grass = Background("images/Grass.jpg", 0, 0)
|
#random.seed(BARRIER_SEED) #SET SEED
|
||||||
|
grass = Background("images/Grass.png", 0, 0)
|
||||||
grass.set_size(WIDTH, HEIGHT)
|
grass.set_size(WIDTH, HEIGHT)
|
||||||
|
|
||||||
gameobjects = [grass]
|
gameobjects = [grass]
|
||||||
barriers = []
|
barriers = []
|
||||||
|
|
||||||
|
# Reset horses to starting position and state
|
||||||
for horse in horses:
|
for horse in horses:
|
||||||
horse.set_position(50, HEIGHT/2)
|
horse.set_position(50, HEIGHT / 2)
|
||||||
horse.stopped = False
|
horse.stopped = False
|
||||||
horse.set_vacceleration(0)
|
horse.set_vacceleration(0)
|
||||||
horse.set_vspeed(0)
|
horse.set_vspeed(0)
|
||||||
@@ -52,6 +67,7 @@ def init_game():
|
|||||||
gameobjects.extend(horses)
|
gameobjects.extend(horses)
|
||||||
gameobjects.extend(barriers)
|
gameobjects.extend(barriers)
|
||||||
|
|
||||||
|
# Define upper and lower bounds for horse movement
|
||||||
upper_bound_rect = pygame.Rect(0, 0, WIDTH, -10)
|
upper_bound_rect = pygame.Rect(0, 0, WIDTH, -10)
|
||||||
lower_bound_rect = pygame.Rect(0, HEIGHT, WIDTH, 10)
|
lower_bound_rect = pygame.Rect(0, HEIGHT, WIDTH, 10)
|
||||||
|
|
||||||
@@ -61,6 +77,7 @@ def init_game():
|
|||||||
|
|
||||||
|
|
||||||
def get_features(horse: Horse):
|
def get_features(horse: Horse):
|
||||||
|
# Extract normalized features for neural network input
|
||||||
features = [last_barrier.rect.topleft[0], last_barrier.rect.topleft[1],
|
features = [last_barrier.rect.topleft[0], last_barrier.rect.topleft[1],
|
||||||
last_barrier.rect.bottomright[0], last_barrier.rect.bottomright[1],
|
last_barrier.rect.bottomright[0], last_barrier.rect.bottomright[1],
|
||||||
horse.rect.topleft[0], horse.rect.topleft[1],
|
horse.rect.topleft[0], horse.rect.topleft[1],
|
||||||
@@ -70,7 +87,7 @@ def get_features(horse: Horse):
|
|||||||
return features
|
return features
|
||||||
|
|
||||||
|
|
||||||
horses = [Horse("images/Horse_1.png", 50, HEIGHT/2 + 0 * i) for i in range(POPULATION_SIZE)]
|
horses = [Horse("images/Horse_1.png", 50, HEIGHT / 2 + 0 * i) for i in range(POPULATION_SIZE)]
|
||||||
|
|
||||||
init_game()
|
init_game()
|
||||||
|
|
||||||
@@ -79,16 +96,23 @@ genecticAlg = GeneticAlgorithm(POPULATION_SIZE, MUTATION_RATE, POPULATION_BEST,
|
|||||||
|
|
||||||
while True:
|
while True:
|
||||||
for event in pygame.event.get():
|
for event in pygame.event.get():
|
||||||
if event.type == pygame.QUIT: # Handle window close event
|
if event.type == pygame.QUIT:
|
||||||
|
# Save fitness plot on exit
|
||||||
|
plt.plot(running_fitnesses)
|
||||||
|
plt.xlabel("Iteration")
|
||||||
|
plt.ylabel("Current Fitness")
|
||||||
|
plt.title("Current Fitness per Iteration")
|
||||||
|
plt.savefig('fitness_plot.png')
|
||||||
pygame.quit()
|
pygame.quit()
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
keys = pygame.key.get_pressed() # Get pressed keys
|
keys = pygame.key.get_pressed()
|
||||||
|
|
||||||
for i, horse in enumerate(horses):
|
for i, horse in enumerate(horses):
|
||||||
if not horse.stopped: # If the horse is not stopped
|
if not horse.stopped:
|
||||||
data = get_features(horse)
|
data = get_features(horse)
|
||||||
res = genecticAlg.predict(data, i)
|
res = genecticAlg.predict(data, i)
|
||||||
|
# Control horse movement based on neural network output
|
||||||
if res == 0:
|
if res == 0:
|
||||||
horse.up()
|
horse.up()
|
||||||
elif res == 1:
|
elif res == 1:
|
||||||
@@ -96,26 +120,29 @@ while True:
|
|||||||
else:
|
else:
|
||||||
horse.stay()
|
horse.stay()
|
||||||
else:
|
else:
|
||||||
horse.move(-BARRIER_SPEED, 0) # Move stopped horse to the left
|
# Move stopped horses left with barriers
|
||||||
|
horse.move(-BARRIER_SPEED, 0)
|
||||||
|
|
||||||
horse.apply_vacceleration() # Apply acceleration to horse
|
horse.apply_vacceleration()
|
||||||
horse.apply_vspeed() # Apply speed to horse
|
horse.apply_vspeed()
|
||||||
horse.draw(screen) # Draw the horse
|
horse.draw(screen)
|
||||||
|
|
||||||
spawner.handle() # Handle spawner logic
|
spawner.handle()
|
||||||
if spawner.tick_counter == 0: # If a new barrier is spawned
|
if spawner.tick_counter == 0:
|
||||||
new_barrier = spawner.spawn() # Spawn a new barrier
|
# Spawn new barrier periodically
|
||||||
barriers.append(new_barrier) # Add barrier to barriers list
|
new_barrier = spawner.spawn()
|
||||||
gameobjects.append(new_barrier) # Add barrier to game objects
|
barriers.append(new_barrier)
|
||||||
|
gameobjects.append(new_barrier)
|
||||||
last_barrier = new_barrier
|
last_barrier = new_barrier
|
||||||
|
|
||||||
for barrier in barriers:
|
for barrier in barriers:
|
||||||
barrier.move(-BARRIER_SPEED, 0) # Move barriers to the left
|
barrier.move(-BARRIER_SPEED, 0)
|
||||||
|
|
||||||
for horse in horses:
|
for horse in horses:
|
||||||
horse.update_animation()
|
horse.update_animation()
|
||||||
|
|
||||||
if not horse.stopped:
|
if not horse.stopped:
|
||||||
|
# Check collisions with barriers and bounds
|
||||||
for barrier in barriers:
|
for barrier in barriers:
|
||||||
if horse.rect.colliderect(barrier.rect):
|
if horse.rect.colliderect(barrier.rect):
|
||||||
horse.stop()
|
horse.stop()
|
||||||
@@ -123,17 +150,36 @@ while True:
|
|||||||
horse.stop()
|
horse.stop()
|
||||||
horse.count_fitness()
|
horse.count_fitness()
|
||||||
|
|
||||||
|
|
||||||
for object in gameobjects:
|
for object in gameobjects:
|
||||||
object.draw(screen) # Draw all game objects
|
object.draw(screen)
|
||||||
|
|
||||||
|
current_fitnesses = [horse.fitness for horse in horses]
|
||||||
|
current_fitness = max(current_fitnesses)
|
||||||
|
|
||||||
if all(horse.stopped for horse in horses):
|
if all(horse.stopped for horse in horses):
|
||||||
|
# All horses stopped: evolve population and start new iteration
|
||||||
UI.iteration_num += 1
|
UI.iteration_num += 1
|
||||||
genecticAlg.learn([x.fitness for x in horses])
|
genecticAlg.learn([x.fitness for x in horses])
|
||||||
|
|
||||||
|
local_best_fitness = max(horse.fitness for horse in horses)
|
||||||
|
best_fitnesses.append(local_best_fitness)
|
||||||
|
running_fitnesses.append(current_fitness)
|
||||||
|
current_iteration += 1
|
||||||
init_game()
|
init_game()
|
||||||
|
|
||||||
UI.draw(screen)
|
UI.draw(screen)
|
||||||
UI.draw_marks(screen)
|
UI.draw_marks(screen)
|
||||||
|
|
||||||
pygame.display.flip() # Update the display
|
# Display iteration and fitness info
|
||||||
pygame.time.Clock().tick(FRAME_RATE) # Limit the frame rate to 160 FPS
|
iteration_num_txt = font.render(f"Iteration: {current_iteration}", True, TEXT_COLOUR)
|
||||||
|
current_fitness_txt = font.render(
|
||||||
|
f"Current Fitness: {current_fitness}", True, TEXT_COLOUR)
|
||||||
|
best_fitness_txt = font.render(
|
||||||
|
f"Best Fitness: {best_fitnesses[-1] if best_fitnesses else 0}", True, TEXT_COLOUR)
|
||||||
|
|
||||||
|
screen.blit(iteration_num_txt, (WIDTH / 2 - 90, HEIGHT + HUD_HEIGHT - 100))
|
||||||
|
screen.blit(best_fitness_txt, (10, 5))
|
||||||
|
screen.blit(current_fitness_txt, (10, 40))
|
||||||
|
|
||||||
|
pygame.display.flip()
|
||||||
|
pygame.time.Clock().tick(FRAME_RATE)
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
contourpy==1.3.1
|
||||||
|
cycler==0.12.1
|
||||||
|
filelock==3.18.0
|
||||||
|
fonttools==4.57.0
|
||||||
|
fsspec==2025.3.2
|
||||||
|
Jinja2==3.1.6
|
||||||
|
kiwisolver==1.4.8
|
||||||
|
MarkupSafe==3.0.2
|
||||||
|
matplotlib==3.10.1
|
||||||
|
mpmath==1.3.0
|
||||||
|
networkx==3.4.2
|
||||||
|
numpy==2.2.4
|
||||||
|
nvidia-cublas-cu12==12.4.5.8
|
||||||
|
nvidia-cuda-cupti-cu12==12.4.127
|
||||||
|
nvidia-cuda-nvrtc-cu12==12.4.127
|
||||||
|
nvidia-cuda-runtime-cu12==12.4.127
|
||||||
|
nvidia-cudnn-cu12==9.1.0.70
|
||||||
|
nvidia-cufft-cu12==11.2.1.3
|
||||||
|
nvidia-curand-cu12==10.3.5.147
|
||||||
|
nvidia-cusolver-cu12==11.6.1.9
|
||||||
|
nvidia-cusparse-cu12==12.3.1.170
|
||||||
|
nvidia-cusparselt-cu12==0.6.2
|
||||||
|
nvidia-nccl-cu12==2.21.5
|
||||||
|
nvidia-nvjitlink-cu12==12.4.127
|
||||||
|
nvidia-nvtx-cu12==12.4.127
|
||||||
|
packaging==24.2
|
||||||
|
pillow==11.2.1
|
||||||
|
pygame==2.6.1
|
||||||
|
pyparsing==3.2.3
|
||||||
|
python-dateutil==2.9.0.post0
|
||||||
|
setuptools==78.1.0
|
||||||
|
six==1.17.0
|
||||||
|
sympy==1.13.1
|
||||||
|
torch==2.6.0
|
||||||
|
triton==3.2.0
|
||||||
|
typing_extensions==4.13.2
|
||||||
Reference in New Issue
Block a user