I've created this game and coded an AI using NEAT library to learn the game and play by it self, although it learned a few techniques to collect the points, it's not as optmized as it should and i'm not being able to make it converge into direct collecting the points.
How can i optimize the inputs on the main and config code in order to the mob converge into directly go for the points?
HERE IS THE MAIN CODE
import random
import pygame
import os
import neat
ai_play = True
pygame.init()
white = (255, 255, 255)
black = (0, 0, 0)
gray = (128, 128, 128)
background = gray
WIDTH = 400
HEIGHT = 500
generation = 0
score = 0
fps = 60
font = pygame.font.Font('freesansbold.ttf', 16)
end_font = pygame.font.Font('freesansbold.ttf', 25)
timer = pygame.time.Clock()
# Janela do jogo
screen = pygame.display.set_mode([WIDTH, HEIGHT])
pygame.display.set_caption('AI Jump')
Boneco = pygame.transform.scale(pygame.image.load('char.png'), (90, 70))
Imagem_Feno = pygame.transform.scale(pygame.image.load('hay (1).png'), (15, 20))
class Alpaca:
IMG = Boneco
Velocidade_pulo = 10
Velocidade_movimento = 3
def __init__(self):
self.img = self.IMG
self.x = 150
self.y = 360
self.vel_x = 0
self.vel_y = 0
self.velocity = 10
self.tempo = 0
self.height = self.y
self.alpaca_jump = False
self.jump_height = 10
self.gravity = .4
self.left_pressed = False
self.right_pressed = False
self.speed = self.Velocidade_movimento
self.score = 0
self.jump_points = 10
def left(self):
self.left_pressed = True
def right(self):
self.right_pressed = True
def stop(self):
self.left_pressed = False
self.right_pressed = False
def update_horizontal(self):
#movimento horizontal
self.vel_x = 0
if self.left_pressed and not self.right_pressed:
self.vel_x = -self.speed
if self.right_pressed and not self.left_pressed:
self.vel_x = self.speed
self.x += self.vel_x
def update_vertical(self):
#movimento vertical
if self.alpaca_jump and self.jump_points > 0 and self.vel_y >= 0:
self.vel_y = -self.jump_height
self.alpaca_jump = False
self.jump_points -= 1
self.y += self.vel_y
self.vel_y += self.gravity
def draw(self,tela):
tela.blit(self.img, (self.x, self.y))
def score_count(self, feno):
if feno.collide(self):
self.score += 1
else:
pass
class Feno:
Fenos = []
def __init__(self,x,y):
self.img = Imagem_Feno
self.x = x
self.y = y
self.size_x = 10
self.size_y = 10
self.Fenos = [[self.x, self.y]]
def desenhar(self,tela):
for i,feno in enumerate(self.Fenos):
self.ponto = pygame.draw.rect(tela, black, [feno[0],feno[1], 10,10], 0, 3)
def collide(self,alpaca):
if self.ponto.colliderect([alpaca.x,alpaca.y, 70, 70]):
self.Fenos.pop()
self.Fenos.append([random.randint(10, 320),random.randint(15, 480)])
return True
else:
return False
def draw_screen(screen,alpacas_fenos,generation):
global ai_play
for i, alpaca_feno in enumerate(alpacas_fenos):
alpaca_feno[0].draw(screen)
alpaca_feno[1].desenhar(screen)
score_text = font.render('Score: ' + str(alpaca_feno[0].score), True, black)
screen.blit(score_text, (320, 20))
if ai_play:
generation_text = font.render('Generation: ' + str(generation), True, black)
screen.blit(generation_text, (15, 20))
def main(genomas, config):
global score
global generation
generation += 1
redes = []
lista_genomas = []
alpacas_fenos = []
if ai_play:
for _,genoma in genomas:
rede = neat.nn.FeedForwardNetwork.create(genoma, config)
redes.append(rede)
genoma.fitness = 0
lista_genomas.append(genoma)
alpacas_fenos.append((Alpaca(), Feno(random.randint(10, 320),random.randint(15, 450))))
else:
alpacas_fenos = [(Alpaca(), Feno(random.randint(10, 320), random.randint(15, 450)))]
running = True
while running == True:
timer.tick(fps)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.QUIT()
quit()
if not ai_play:
for i in alpacas_fenos:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
i[0].left()
if event.key == pygame.K_d:
i[0].right()
if event.key == pygame.K_k:
i[0].alpaca_jump = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_a:
i[0].stop()
if event.key == pygame.K_d:
i[0].stop()
for i in alpacas_fenos:
if i[0].x < -40:
i[0].x = 350
elif i[0].x > 350:
i[0].x = -40
if len(alpacas_fenos) == 0:
running = False
for j,i in enumerate(alpacas_fenos):
i[0].update_horizontal()
i[0].update_vertical()
if ai_play:
lista_genomas[j].fitness += 0.1
output = redes[j].activate((i[0].y, ((i[1].Fenos[0][0] - i[0].x)**2 + (i[1].Fenos[0][1] - i[0].y)**2)**0.5))
if output[0] <= -0.5:
i[0].left()
elif output[0] > 0 and output[0] < 0.25:
i[0].alpaca_jump = True
elif output[0] >= 0.5:
i[0].right()
#else:
# i[0].stop()
screen.fill(background)
draw_screen(screen, alpacas_fenos, generation)
for j,i in enumerate(alpacas_fenos):
i[0].score_count(i[1])
if i[1].collide(i[0]) and ai_play:
lista_genomas[j].fitness += 5
i[0].jump_points += 10
if i[1].collide(i[0]) and not ai_play:
i[0].jump_points += 10
for j,i in enumerate(alpacas_fenos):
if i[0].y > 490 or i[0].y < -250:
alpacas_fenos.pop(j)
if ai_play:
lista_genomas[j].fitness -= 1
lista_genomas.pop(j)
redes.pop(j)
else:
pass
pygame.display.flip()
def run(path_config):
config = neat.config.Config(neat.DefaultGenome, neat.DefaultReproduction, neat.DefaultSpeciesSet, neat.DefaultStagnation, path_config)
population = neat.Population(config)
population.add_reporter(neat.StdOutReporter(True))
stats = neat.StatisticsReporter()
population.add_reporter(stats)
if ai_play:
population.run(main, 50)
else:
main(None, None)
if __name__ == '__main__':
local_dir = os.path.dirname(__file__)
path_config = os.path.join(local_dir, 'config.txt')
run(path_config)
HERE IS THE CONFIG
[NEAT]
fitness_criterion = max
fitness_threshold = 400
pop_size = 100
reset_on_extinction = False
[DefaultGenome]
# node activation options
activation_default = tanh
activation_mutate_rate = 0.0
activation_options = tanh
# node aggregation options
aggregation_default = sum
aggregation_mutate_rate = 0.0
aggregation_options = sum
# node bias options
bias_init_mean = 0.0
bias_init_stdev = 1.0
bias_max_value = 30.0
bias_min_value = -30.0
bias_mutate_power = 0.5
bias_mutate_rate = 0.7
bias_replace_rate = 0.1
# genome compatibility options
compatibility_disjoint_coefficient = 1.0
compatibility_weight_coefficient = 0.5
# connection add/remove rates
conn_add_prob = 0.5
conn_delete_prob = 0.5
# connection enable options
enabled_default = True
enabled_mutate_rate = 0.01
feed_forward = True
initial_connection = full_nodirect
# node add/remove rates
node_add_prob = 0.5
node_delete_prob = 0.2
# network parameters
num_hidden = 4
num_inputs = 2
num_outputs = 2
# node response options
response_init_mean = 1.0
response_init_stdev = 0.0
response_max_value = 30.0
response_min_value = -30.0
response_mutate_power = 0.0
response_mutate_rate = 0.0
response_replace_rate = 0.0
# connection weight options
weight_init_mean = 0.0
weight_init_stdev = 1.0
weight_max_value = 50
weight_min_value = -50
weight_mutate_power = 0.5
weight_mutate_rate = 0.8
weight_replace_rate = 0.1
[DefaultSpeciesSet]
compatibility_threshold = 3.0
[DefaultStagnation]
species_fitness_func = max
max_stagnation = 5
species_elitism = 0
[DefaultReproduction]
elitism = 2
survival_threshold = 0.2
I tried to change the genomes activation functions, different values of fitness gains and losses, the nodes connections, numbers of inputs, outputs and hidden nodes and the stagnation and elitsm of species but still couldn't find the best fit for the code.