Spaces:
Configuration error
Configuration error
| # 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) | |