Add comments in the code.
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 34 KiB |
+68
-38
@@ -6,6 +6,10 @@ HUD_HEIGHT = 150
|
||||
BARRIER_SEED = 20
|
||||
|
||||
class GameObject:
|
||||
"""
|
||||
Base class for all game objects.
|
||||
Handles image loading, positioning, drawing, and child objects.
|
||||
"""
|
||||
x = 0
|
||||
y = 0
|
||||
|
||||
@@ -17,62 +21,70 @@ class GameObject:
|
||||
self.offset = offset
|
||||
|
||||
def draw(self, surface):
|
||||
# Draw self and all children recursively
|
||||
surface.blit(self.image, self.rect)
|
||||
if len(self.children) == 0:
|
||||
if not self.children:
|
||||
return
|
||||
for child in self.children:
|
||||
child.draw(surface)
|
||||
|
||||
def set_position(self, x, y):
|
||||
# Set position and update children positions with offsets
|
||||
self.rect.x = x
|
||||
self.rect.y = y
|
||||
self.x = x
|
||||
self.y = y
|
||||
if len(self.children) == 0:
|
||||
if not self.children:
|
||||
return
|
||||
for child in self.children:
|
||||
child.set_position(x + child.offset[0], y + child.offset[1])
|
||||
|
||||
def move(self, dx, dy):
|
||||
# Move object by delta and update children accordingly
|
||||
self.set_position(self.rect.x + dx, self.rect.y + dy)
|
||||
if len(self.children) == 0:
|
||||
if not self.children:
|
||||
return
|
||||
for child in self.children:
|
||||
self.set_position(self.rect.x + dx, self.rect.y + dy)
|
||||
|
||||
def set_size(self, width, height):
|
||||
# Resize image and update rect
|
||||
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):
|
||||
self.image = pygame.transform.scale(self.image, (self.image.get_width() * scale, self.image.get_height() * scale))
|
||||
# Scale image by a factor
|
||||
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):
|
||||
return (self.x, self.y)
|
||||
|
||||
def set_children(self, child):
|
||||
# Add a child GameObject
|
||||
self.children.append(child)
|
||||
|
||||
def recolor(self, color):
|
||||
# Apply color tint to image
|
||||
self.image.fill(color, special_flags=pygame.BLEND_MULT)
|
||||
self.image.set_colorkey(None)
|
||||
|
||||
class Horse(GameObject):
|
||||
"""
|
||||
Represents a horse character with vertical movement, animation, and fitness tracking.
|
||||
Supports acceleration, speed, and stopping.
|
||||
"""
|
||||
default_vacceleration = 0.2
|
||||
vspeed = 0
|
||||
vacceleration = 0
|
||||
stopped = False
|
||||
color = ()
|
||||
ribbon_fill = None
|
||||
ribbon_out = None
|
||||
|
||||
def __init__(self, image_path, x, y):
|
||||
super().__init__(image_path, x, y)
|
||||
self.images = []
|
||||
self.images.append(pygame.image.load("images/Horse_1.png"))
|
||||
self.images.append(pygame.image.load("images/Horse_2.png"))
|
||||
self.images.append(pygame.image.load("images/Horse_3.png"))
|
||||
# Load animation frames
|
||||
self.images = [
|
||||
pygame.image.load("images/Horse_1.png"),
|
||||
pygame.image.load("images/Horse_2.png"),
|
||||
pygame.image.load("images/Horse_3.png")
|
||||
]
|
||||
self.animation_speed = 25
|
||||
self.frame_counter = 0
|
||||
self.current_frame = 0
|
||||
@@ -80,7 +92,8 @@ class Horse(GameObject):
|
||||
self.vacceleration = 0
|
||||
self.stopped = False
|
||||
self.fitness = 0
|
||||
self.color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
|
||||
# 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.recolor(self.color)
|
||||
self.ribbon_out = GameObject("images/Ribbon_out.png", self.x, self.y, (14, -2))
|
||||
@@ -88,10 +101,11 @@ class Horse(GameObject):
|
||||
self.set_children(self.ribbon_out)
|
||||
|
||||
def apply_vspeed(self):
|
||||
pos = self.get_position()
|
||||
self.set_position(pos[0], pos[1] + self.vspeed)
|
||||
# Move vertically by current speed
|
||||
self.set_position(self.x, self.y + self.vspeed)
|
||||
|
||||
def apply_vacceleration(self):
|
||||
# Update speed by acceleration
|
||||
self.vspeed += self.vacceleration
|
||||
|
||||
def set_vspeed(self, speed):
|
||||
@@ -104,23 +118,25 @@ class Horse(GameObject):
|
||||
self.vacceleration = vacceleration
|
||||
|
||||
def up(self):
|
||||
# Accelerate upward
|
||||
self.set_vacceleration(-self.default_vacceleration)
|
||||
|
||||
def down(self):
|
||||
# Accelerate downward
|
||||
self.set_vacceleration(self.default_vacceleration)
|
||||
|
||||
def stay(self):
|
||||
# No vertical acceleration
|
||||
self.set_vacceleration(0)
|
||||
|
||||
def stop(self):
|
||||
# Stop all movement
|
||||
self.stopped = True
|
||||
self.set_vacceleration(0)
|
||||
self.set_vspeed(0)
|
||||
|
||||
def draw(self, surface):
|
||||
super().draw(surface)
|
||||
|
||||
def update_animation(self):
|
||||
# Animate horse by cycling frames
|
||||
self.frame_counter += 1
|
||||
if self.frame_counter >= self.animation_speed:
|
||||
self.frame_counter = 0
|
||||
@@ -128,21 +144,31 @@ class Horse(GameObject):
|
||||
self.image = self.images[self.current_frame]
|
||||
|
||||
def count_fitness(self):
|
||||
if self.stopped == False:
|
||||
# Increase fitness if moving; penalize if speed zero
|
||||
if not self.stopped:
|
||||
self.fitness += 1
|
||||
if self.vspeed == 0:
|
||||
self.fitness -= 0.9
|
||||
|
||||
class Background(GameObject):
|
||||
"""
|
||||
Background image for the game scene.
|
||||
"""
|
||||
def __init__(self, image_path, x, y):
|
||||
super().__init__(image_path, x, y)
|
||||
|
||||
class Barrier(GameObject):
|
||||
"""
|
||||
Obstacles that move horizontally and interact with horses.
|
||||
"""
|
||||
def __init__(self, image_path, x, y):
|
||||
super().__init__(image_path, x, y)
|
||||
self.scale_by(5)
|
||||
self.scale_by(5) # Make barrier larger for visibility
|
||||
|
||||
class UI:
|
||||
"""
|
||||
Handles HUD display and horse status markers.
|
||||
"""
|
||||
iteration_num = 0
|
||||
horses = []
|
||||
horizontal_offset = 25
|
||||
@@ -155,34 +181,38 @@ class UI:
|
||||
self.hud_rect = self.hud_image.get_rect(topleft=(x, y))
|
||||
|
||||
def add_horses(self, children):
|
||||
new_horses = [child for child in children if child not in self.horses]
|
||||
self.horses.extend(new_horses)
|
||||
# Add horses to UI tracking list
|
||||
self.horses.extend([child for child in children if child not in self.horses])
|
||||
|
||||
def draw_marks(self, screen):
|
||||
active_marks = [horse for horse in self.horses if not horse.stopped]
|
||||
unactive_marks = [horse for horse in self.horses if horse.stopped]
|
||||
width = 10
|
||||
xa = 0
|
||||
ya = HEIGHT + HUD_HEIGHT - self.mark_size
|
||||
for i, horse in enumerate(active_marks):
|
||||
# Draw colored squares representing active and stopped horses
|
||||
active = [h for h in self.horses if not h.stopped]
|
||||
inactive = [h for h in self.horses if h.stopped]
|
||||
|
||||
xa, ya = 0, HEIGHT + HUD_HEIGHT - self.mark_size
|
||||
for i, horse in enumerate(active):
|
||||
pygame.draw.rect(screen, horse.color, (xa, ya, self.mark_size, self.mark_size))
|
||||
xa += self.horizontal_offset
|
||||
if (i + 1) % width == 0:
|
||||
if (i+1) % 10 == 0:
|
||||
ya += self.vertical_offset
|
||||
xa = 0
|
||||
xu = WIDTH - self.mark_size
|
||||
yu = HEIGHT + HUD_HEIGHT - self.mark_size
|
||||
for i, horse in enumerate(unactive_marks):
|
||||
|
||||
xu, yu = WIDTH - self.mark_size, HEIGHT + HUD_HEIGHT - self.mark_size
|
||||
for i, horse in enumerate(inactive):
|
||||
pygame.draw.rect(screen, horse.color, (xu, yu, self.mark_size, self.mark_size))
|
||||
xu -= self.horizontal_offset
|
||||
if (i + 1) % width == 0:
|
||||
if (i+1) % 10 == 0:
|
||||
yu += self.vertical_offset
|
||||
xu = WIDTH - self.mark_size
|
||||
|
||||
def draw(self, surface):
|
||||
# Draw HUD image
|
||||
surface.blit(self.hud_image, self.hud_rect)
|
||||
|
||||
class Spawner:
|
||||
"""
|
||||
Spawns barriers at intervals to challenge horses.
|
||||
"""
|
||||
active = True
|
||||
delay = 500
|
||||
tick_counter = 0
|
||||
@@ -192,6 +222,7 @@ class Spawner:
|
||||
self.delay = delay
|
||||
|
||||
def handle(self):
|
||||
# Increment tick counter and spawn barrier when delay reached
|
||||
if self.active:
|
||||
self.tick_counter += 1
|
||||
if self.tick_counter >= self.delay:
|
||||
@@ -199,6 +230,5 @@ class Spawner:
|
||||
self.tick_counter = 0
|
||||
|
||||
def spawn(self):
|
||||
random_y = random.randint(0, HEIGHT - 100)
|
||||
new_barrier = Barrier(self.barrier_image_path, WIDTH, random_y)
|
||||
return new_barrier
|
||||
# Create a new barrier at a random vertical position on the right edge
|
||||
return Barrier(self.barrier_image_path, WIDTH, random.randint(0, HEIGHT - 100))
|
||||
|
||||
@@ -7,7 +7,7 @@ matplotlib.use('Agg')
|
||||
import matplotlib.pyplot as plt
|
||||
import random
|
||||
|
||||
POPULATION_SIZE = 50 # SET TO 500 IF YOU HAVE A GOOD PC
|
||||
POPULATION_SIZE = 50 # Number of horses in population
|
||||
MUTATION_RATE = 0.5
|
||||
POPULATION_NEW = 0.1
|
||||
POPULATION_BEST = 0.3
|
||||
@@ -42,6 +42,7 @@ last_barrier = None
|
||||
|
||||
def init_game():
|
||||
global gameobjects, horses, barriers, upper_bound_rect, lower_bound_rect, spawner, last_barrier, BARRIER_SPEED
|
||||
# Initialize background and game objects for a new iteration
|
||||
#random.seed(BARRIER_SEED) #SET SEED
|
||||
grass = Background("images/Grass.jpg", 0, 0)
|
||||
grass.set_size(WIDTH, HEIGHT)
|
||||
@@ -49,6 +50,7 @@ def init_game():
|
||||
gameobjects = [grass]
|
||||
barriers = []
|
||||
|
||||
# Reset horses to starting position and state
|
||||
for horse in horses:
|
||||
horse.set_position(50, HEIGHT / 2)
|
||||
horse.stopped = False
|
||||
@@ -64,6 +66,7 @@ def init_game():
|
||||
gameobjects.extend(horses)
|
||||
gameobjects.extend(barriers)
|
||||
|
||||
# Define upper and lower bounds for horse movement
|
||||
upper_bound_rect = pygame.Rect(0, 0, WIDTH, -10)
|
||||
lower_bound_rect = pygame.Rect(0, HEIGHT, WIDTH, 10)
|
||||
|
||||
@@ -73,6 +76,7 @@ def init_game():
|
||||
|
||||
|
||||
def get_features(horse: Horse):
|
||||
# Extract normalized features for neural network input
|
||||
features = [last_barrier.rect.topleft[0], last_barrier.rect.topleft[1],
|
||||
last_barrier.rect.bottomright[0], last_barrier.rect.bottomright[1],
|
||||
horse.rect.topleft[0], horse.rect.topleft[1],
|
||||
@@ -92,6 +96,7 @@ genecticAlg = GeneticAlgorithm(POPULATION_SIZE, MUTATION_RATE, POPULATION_BEST,
|
||||
while True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
# Save fitness plot on exit
|
||||
plt.plot(running_fitnesses)
|
||||
plt.xlabel("Iteration")
|
||||
plt.ylabel("Current Fitness")
|
||||
@@ -106,6 +111,7 @@ while True:
|
||||
if not horse.stopped:
|
||||
data = get_features(horse)
|
||||
res = genecticAlg.predict(data, i)
|
||||
# Control horse movement based on neural network output
|
||||
if res == 0:
|
||||
horse.up()
|
||||
elif res == 1:
|
||||
@@ -113,6 +119,7 @@ while True:
|
||||
else:
|
||||
horse.stay()
|
||||
else:
|
||||
# Move stopped horses left with barriers
|
||||
horse.move(-BARRIER_SPEED, 0)
|
||||
|
||||
horse.apply_vacceleration()
|
||||
@@ -121,6 +128,7 @@ while True:
|
||||
|
||||
spawner.handle()
|
||||
if spawner.tick_counter == 0:
|
||||
# Spawn new barrier periodically
|
||||
new_barrier = spawner.spawn()
|
||||
barriers.append(new_barrier)
|
||||
gameobjects.append(new_barrier)
|
||||
@@ -133,6 +141,7 @@ while True:
|
||||
horse.update_animation()
|
||||
|
||||
if not horse.stopped:
|
||||
# Check collisions with barriers and bounds
|
||||
for barrier in barriers:
|
||||
if horse.rect.colliderect(barrier.rect):
|
||||
horse.stop()
|
||||
@@ -146,8 +155,8 @@ while True:
|
||||
current_fitnesses = [horse.fitness for horse in horses]
|
||||
current_fitness = max(current_fitnesses)
|
||||
|
||||
|
||||
if all(horse.stopped for horse in horses):
|
||||
# All horses stopped: evolve population and start new iteration
|
||||
UI.iteration_num += 1
|
||||
genecticAlg.learn([x.fitness for x in horses])
|
||||
|
||||
@@ -160,6 +169,7 @@ while True:
|
||||
UI.draw(screen)
|
||||
UI.draw_marks(screen)
|
||||
|
||||
# Display iteration and fitness info
|
||||
iteration_num_txt = font.render(f"Iteration: {current_iteration}", True, BLACK)
|
||||
current_fitness_txt = font.render(
|
||||
f"Current Fitness: {current_fitness}", True, BLACK)
|
||||
|
||||
Reference in New Issue
Block a user