# Welcome to the Self_Driven_Vehicle Project!! # NEAT(NeuroEvolution of Augmenting Topologies) is an evolutionary algorithm that creates artificial neural networks. # The "config-feedforward.txt" file contains all the hyper-parameters for adjusting Vehicle Behaviour. # import pygame import os import math import sys import random import neat screen_width = 1500 screen_height = 800 generation = 0 class Car: def __init__(self): self.surface = pygame.image.load("car.png") self.surface = pygame.transform.scale(self.surface, (100, 100)) self.rotate_surface = self.surface self.pos = [700, 650] self.angle = 0 self.speed = 0 self.center = [self.pos[0] + 50, self.pos[1] + 50] self.radars = [] self.radars_for_draw = [] self.is_alive = True self.goal = False self.distance = 0 self.time_spent = 0 def draw(self, screen): screen.blit(self.rotate_surface, self.pos) self.draw_radar(screen) def draw_radar(self, screen): for r in self.radars: pos, dist = r pygame.draw.line(screen, (0, 255, 0), self.center, pos, 1) pygame.draw.circle(screen, (0, 255, 0), pos, 5) def check_collision(self, map): self.is_alive = True for p in self.four_points: if map.get_at((int(p[0]), int(p[1]))) == (255, 255, 255, 255): self.is_alive = False break def check_radar(self, degree, map): len = 0 x = int(self.center[0] + math.cos(math.radians(360 - (self.angle + degree))) * len) y = int(self.center[1] + math.sin(math.radians(360 - (self.angle + degree))) * len) while not map.get_at((x, y)) == (255, 255, 255, 255) and len < 300: len = len + 1 x = int(self.center[0] + math.cos(math.radians(360 - (self.angle + degree))) * len) y = int(self.center[1] + math.sin(math.radians(360 - (self.angle + degree))) * len) dist = int(math.sqrt(math.pow(x - self.center[0], 2) + math.pow(y - self.center[1], 2))) self.radars.append([(x, y), dist]) def update(self, map): #check speed self.speed = 15 #check position self.rotate_surface = self.rot_center(self.surface, self.angle) self.pos[0] += math.cos(math.radians(360 - self.angle)) * self.speed if self.pos[0] < 20: self.pos[0] = 20 elif self.pos[0] > screen_width - 120: self.pos[0] = screen_width - 120 self.distance += self.speed self.time_spent += 1 self.pos[1] += math.sin(math.radians(360 - self.angle)) * self.speed if self.pos[1] < 20: self.pos[1] = 20 elif self.pos[1] > screen_height - 120: self.pos[1] = screen_height - 120 # here, we're calculating 4 collision points self.center = [int(self.pos[0]) + 50, int(self.pos[1]) + 50] len = 40 left_top = [self.center[0] + math.cos(math.radians(360 - (self.angle + 30))) * len, self.center[1] + math.sin(math.radians(360 - (self.angle + 30))) * len] right_top = [self.center[0] + math.cos(math.radians(360 - (self.angle + 150))) * len, self.center[1] + math.sin(math.radians(360 - (self.angle + 150))) * len] left_bottom = [self.center[0] + math.cos(math.radians(360 - (self.angle + 210))) * len, self.center[1] + math.sin(math.radians(360 - (self.angle + 210))) * len] right_bottom = [self.center[0] + math.cos(math.radians(360 - (self.angle + 330))) * len, self.center[1] + math.sin(math.radians(360 - (self.angle + 330))) * len] self.four_points = [left_top, right_top, left_bottom, right_bottom] self.check_collision(map) self.radars.clear() for d in range(-90, 120, 45): self.check_radar(d, map) def get_data(self): radars = self.radars ret = [0, 0, 0, 0, 0] for i, r in enumerate(radars): ret[i] = int(r[1] / 30) return ret def get_alive(self): return self.is_alive def get_reward(self): return self.distance / 50.0 def rot_center(self, image, angle): orig_rect = image.get_rect() rot_image = pygame.transform.rotate(image, angle) rot_rect = orig_rect.copy() rot_rect.center = rot_image.get_rect().center rot_image = rot_image.subsurface(rot_rect).copy() return rot_image def run_car(genomes, config): # Init NEAT nets = [] cars = [] for id, g in genomes: net = neat.nn.FeedForwardNetwork.create(g, config) nets.append(net) g.fitness = 0 # Init my cars cars.append(Car()) # Init my game pygame.init() screen = pygame.display.set_mode((screen_width, screen_height)) clock = pygame.time.Clock() generation_font = pygame.font.SysFont("Arial", 70) font = pygame.font.SysFont("Arial", 30) map = pygame.image.load('map.png') # Main loop global generation generation += 1 while True: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit(0) # Different inputs result in different Vehicle behaviour. for index, car in enumerate(cars): output = nets[index].activate(car.get_data()) i = output.index(max(output)) if i == 0: car.angle += 20 else: car.angle -= 20 # Update car and fitness remain_cars = 0 for i, car in enumerate(cars): if car.get_alive(): remain_cars += 1 car.update(map) genomes[i][1].fitness += car.get_reward() # check if remain_cars == 0: break # Drawing screen.blit(map, (0, 0)) for car in cars: if car.get_alive(): car.draw(screen) text = generation_font.render("Generation : " + str(generation), True, (255, 255, 0)) text_rect = text.get_rect() text_rect.center = (screen_width/2, 100) screen.blit(text, text_rect) text = font.render("remain cars : " + str(remain_cars), True, (0, 0, 0)) text_rect = text.get_rect() text_rect.center = (screen_width/2, 200) screen.blit(text, text_rect) pygame.display.flip() clock.tick(0) if __name__ == "__main__": # Set configuration file config_path = "./config-feedforward.txt" config = neat.config.Config(neat.DefaultGenome, neat.DefaultReproduction, neat.DefaultSpeciesSet, neat.DefaultStagnation, config_path) # Create core evolution algorithm class p = neat.Population(config) # Add reporter for fancy statistical result p.add_reporter(neat.StdOutReporter(True)) stats = neat.StatisticsReporter() p.add_reporter(stats) # Run NEAT p.run(run_car, 1000)