diff --git a/gameobjects.py b/gameobjects.py index 2c5abe4..d433158 100644 --- a/gameobjects.py +++ b/gameobjects.py @@ -46,7 +46,7 @@ class GameObject: self.image.set_colorkey(None) # Explicitly disable colorkey class Horse(GameObject): - default_vacceleration = 0.5 # Default acceleration value + default_vacceleration = 0.3 # Default acceleration value vspeed = 0 # Vertical speed vacceleration = 0 # Vertical acceleration stopped = False # Whether the horse is stopped @@ -106,6 +106,8 @@ class Horse(GameObject): def count_fitness(self): if self.stopped == False: self.fitness += 1 + if self.vacceleration == 0: + self.fitness -= 0.9 class Background(GameObject): def __init__(self, image_path, x, y): diff --git a/genetic_alg.py b/genetic_alg.py index 1ee727f..82bc4e0 100644 --- a/genetic_alg.py +++ b/genetic_alg.py @@ -5,9 +5,9 @@ import numpy as np class NeuralNetwork(nn.Module): def __init__(self, inputSize): super().__init__() - self.hidden1 = nn.Linear(inputSize, 32) - self.hidden2 = nn.Linear(32, 16) - self.output = nn.Linear(16, 3) + self.hidden1 = nn.Linear(inputSize, 128) + self.hidden2 = nn.Linear(128, 128) + self.output = nn.Linear(128, 3) def forward(self, x): x = torch.relu(self.hidden1(x)) @@ -16,20 +16,25 @@ class NeuralNetwork(nn.Module): return torch.argmax(x) class GeneticAlgorithm: - def __init__(self, populationSize: int, mutationRate: float, percentageBest: float, inputSize: int = 5): + def __init__(self, populationSize: int, mutationRate: float, percentageBest: float, percentageNew: float, inputSize: int = 5): self.populationSize = populationSize self.mutationRate = mutationRate self.percentageBest = percentageBest + self.percentageNew = percentageNew self.inputSize = inputSize self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + print(self.device) self.initialize_population() + def create_ind(self): + return NeuralNetwork(self.inputSize).to(self.device) + def initialize_population(self): - self.population = [NeuralNetwork(self.inputSize).to(self.device) for _ in range(self.populationSize)] + self.population = [self.create_ind() for _ in range(self.populationSize)] def crossover(self, parent1: NeuralNetwork, parent2: NeuralNetwork): - child1 = NeuralNetwork(self.inputSize).to(self.device) - child2 = NeuralNetwork(self.inputSize).to(self.device) + child1 = self.create_ind() + child2 = self.create_ind() point1 = len(child1.hidden1.weight.data) // 2 point2 = len(child1.hidden2.weight.data) // 2 child1.hidden1.weight.data = torch.cat((parent1.hidden1.weight.data[:point1], parent2.hidden1.weight.data[point1:]), dim=0) @@ -44,21 +49,23 @@ class GeneticAlgorithm: def mutate(self, model: NeuralNetwork): for param in model.parameters(): if torch.rand(1).item() < self.mutationRate: - param.data += torch.randn_like(param.data) * 0.1 + param.data += torch.randn_like(param.data) * 0.1 * (1 if torch.rand(1).item() >= 0.5 else -1) return model def learn(self, fitness: list): - self.population = [self.population[x] for x in np.argsort(fitness)] + self.population = [self.population[x] for x in np.argsort(fitness)[::-1]] numBest = int(self.populationSize * self.percentageBest) self.population = self.population[:numBest] - while len(self.population) < self.populationSize: + while len(self.population) < self.populationSize - self.populationSize * self.percentageNew: parent1, parent2 = np.random.choice(self.population), np.random.choice(self.population) child1, child2 = self.crossover(parent1, parent2) child1 = self.mutate(child1) child2 = self.mutate(child2) self.population.extend([child1, child2]) + while len(self.population) < self.populationSize: self.population.append(self.create_ind()) while len(self.population) > self.populationSize: self.population.pop() def predict(self, data: list, i): data = torch.tensor(data, requires_grad=False).float().to(self.device) - return self.population[i](data) \ No newline at end of file + return self.population[i](data) + \ No newline at end of file diff --git a/main.py b/main.py index d2f815a..8fccbef 100644 --- a/main.py +++ b/main.py @@ -3,10 +3,13 @@ import sys from gameobjects import * from genetic_alg import GeneticAlgorithm -POPULATION_SIZE = 20 +POPULATION_SIZE = 50 MUTATION_RATE = 0.5 +POPULATION_NEW = 0.1 POPULATION_BEST = 0.2 FRAME_RATE = 160 #TODO: FIX THE INCORRECT FRAME RATE CORRELATION +BARRIER_SPEED = 10 +BARRIER_DELAY = 100 pygame.init() @@ -32,8 +35,6 @@ def init_game(): grass = Background("images/Grass.jpg", 0, 0) grass.set_size(WIDTH, HEIGHT) - BARRIER_SPEED = 25 - gameobjects = [] barriers = [] @@ -46,7 +47,7 @@ def init_game(): horse.set_vspeed(0) horse.frame_counter = 0 horse.fitness = 0 - spawner = Spawner("images/Barrier.png", 125) + spawner = Spawner("images/Barrier.png", BARRIER_DELAY) new_barrier = spawner.spawn() barriers.append(new_barrier) gameobjects.extend(horses) @@ -65,12 +66,12 @@ def get_features(horse): last_barrier.rect.bottomright[0], last_barrier.rect.bottomright[1], horse.rect.topleft[0], horse.rect.topleft[1], horse.rect.bottomright[0], horse.rect.bottomright[1], - HEIGHT, BARRIER_SPEED] + 0, HEIGHT, BARRIER_SPEED] return features -genecticAlg = GeneticAlgorithm(POPULATION_SIZE, MUTATION_RATE, POPULATION_BEST, 10) horses = [Horse("images/Horse_1.png", 50, HEIGHT/2 + 0 * i) for i in range(POPULATION_SIZE)] init_game() +genecticAlg = GeneticAlgorithm(POPULATION_SIZE, MUTATION_RATE, POPULATION_BEST, POPULATION_NEW, len(get_features(horses[0]))) while True: for event in pygame.event.get(): if event.type == pygame.QUIT: # Handle window close event @@ -115,7 +116,7 @@ while True: if horse.rect.colliderect(upper_bound_rect) or horse.rect.colliderect(lower_bound_rect): horse.stop() horse.count_fitness() - print(str(horse.color) + ': ' + str(horse.fitness)) + # print(str(horse.color) + ': ' + str(horse.fitness)) for object in gameobjects: