-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 85cf4c0
Showing
4 changed files
with
261 additions
and
0 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
[NEAT] | ||
fitness_criterion = max | ||
fitness_threshold = 10000 | ||
pop_size = 50 | ||
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 | ||
|
||
# node add/remove rates | ||
node_add_prob = 0.2 | ||
node_delete_prob = 0.2 | ||
|
||
# network parameters | ||
num_hidden = 0 | ||
num_inputs = 5 | ||
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 = 30 | ||
weight_min_value = -30 | ||
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 = 20 | ||
species_elitism = 2 | ||
|
||
[DefaultReproduction] | ||
elitism = 2 | ||
survival_threshold = 0.2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
import pygame | ||
import os | ||
import random | ||
import math | ||
import sys | ||
import neat | ||
|
||
pygame.init() | ||
# Global Constants | ||
SCREEN_WIDTH = 1244 | ||
SCREEN_HEIGHT = 1016 | ||
|
||
SCREEN = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) | ||
TRACK = pygame.image.load(os.path.join("Assets", "track3.png")) | ||
|
||
|
||
class Car(pygame.sprite.Sprite): | ||
def __init__(self): | ||
super().__init__() | ||
self.original_image = pygame.image.load(os.path.join("Assets", "oldcar.png")) | ||
self.image = self.original_image | ||
self.rect = self.image.get_rect(center=(490, 820)) | ||
self.center = self.rect.center | ||
self.vel_vector = pygame.math.Vector2(0.8, 0) | ||
self.angle = 0 | ||
self.rotation_vel = 5 | ||
self.direction = 0 | ||
self.alive = True | ||
self.radars = [] | ||
|
||
def update(self, screen): | ||
self.radars.clear() | ||
self.rotate(screen) | ||
self.drive() | ||
for radar_angle in (-60, -30, 0, 30, 60): | ||
self.radar(SCREEN, radar_angle) | ||
self.data() | ||
self.collision(screen) | ||
|
||
|
||
#print(self.radar(SCREEN, 0)[0]) | ||
|
||
def drive(self): | ||
self.rect.center += self.vel_vector*6 | ||
|
||
def collision(self, screen): | ||
len = 40 | ||
collision_point_right = [int(self.rect.center[0] + math.cos(math.radians(360+18-self.angle))*len), | ||
int(self.rect.center[1] + math.sin(math.radians(360+18-self.angle))*len)] | ||
collision_point_left = [int(self.rect.center[0] + math.cos(math.radians(360-18-self.angle))*len), | ||
int(self.rect.center[1] + math.sin(math.radians(360-18-self.angle))*len)] | ||
|
||
# print(screen.get_at(collision_point_right)) | ||
if screen.get_at(collision_point_right) == pygame.Color(2, 105, 31, 255) \ | ||
or screen.get_at(collision_point_left) == pygame.Color(2, 105, 31, 255): | ||
self.alive = False | ||
print("collision") | ||
|
||
# Draw Collision Points - (draw this after getting the color(!) using get_at) | ||
pygame.draw.circle(screen, (0, 255, 255, 0), collision_point_right, 4) | ||
pygame.draw.circle(screen, (0, 255, 255, 0), collision_point_left, 4) | ||
|
||
def rotate(self, screen): | ||
if self.direction == 1: | ||
self.angle -= self.rotation_vel | ||
self.vel_vector.rotate_ip(self.rotation_vel) | ||
if self.direction == -1: | ||
self.angle += self.rotation_vel | ||
self.vel_vector.rotate_ip(-self.rotation_vel) | ||
|
||
self.image = pygame.transform.rotozoom(self.original_image, self.angle, 0.1) | ||
self.rect = self.image.get_rect(center=self.rect.center) | ||
# pygame.draw.rect(screen, (255, 255, 255, 255), self.rect) | ||
|
||
def radar(self, screen, radar_angle): | ||
length = 0 | ||
x = int(self.rect.center[0] + math.cos(math.radians(360-self.angle+radar_angle)) * length) | ||
y = int(self.rect.center[1] + math.sin(math.radians(360-self.angle+radar_angle)) * length) | ||
|
||
while not screen.get_at((x, y)) == pygame.Color(2, 105, 31, 255) and length < 200: | ||
length += 1 | ||
x = int(self.rect.center[0] + math.cos(math.radians(360-self.angle+radar_angle)) * length) | ||
y = int(self.rect.center[1] + math.sin(math.radians(360-self.angle+radar_angle)) * length) | ||
|
||
# Draw Radar | ||
pygame.draw.line(screen, (255, 255, 255, 255), self.rect.center, (x, y), 1) | ||
pygame.draw.circle(screen, (0, 255, 0, 0), (x, y), 3) | ||
|
||
dist = int(math.sqrt(math.pow(self.rect.center[0] - x, 2) | ||
+ math.pow(self.rect.center[1] - y, 2))) | ||
|
||
self.radars.append([radar_angle, dist]) | ||
|
||
def data(self): | ||
radars = self.radars | ||
input = [0, 0, 0, 0, 0] | ||
for i, radar in enumerate(radars): | ||
input[i] = int(radar[1]) | ||
return input | ||
|
||
|
||
def remove(index): | ||
cars.pop(index) | ||
ge.pop(index) | ||
nets.pop(index) | ||
|
||
def eval_genomes(genomes, config): | ||
global cars, ge, nets | ||
clock = pygame.time.Clock() | ||
|
||
cars = [] | ||
ge = [] | ||
nets = [] | ||
|
||
for genome_id, genome in genomes: | ||
cars.append(pygame.sprite.GroupSingle(Car())) | ||
ge.append(genome) | ||
net = neat.nn.FeedForwardNetwork.create(genome, config) | ||
nets.append(net) | ||
genome.fitness = 0 | ||
|
||
run = True | ||
while run: | ||
for event in pygame.event.get(): | ||
if event.type == pygame.QUIT: | ||
pygame.quit() | ||
sys.exit() | ||
|
||
SCREEN.blit(TRACK, (0, 0)) | ||
|
||
for car in cars: | ||
car.update(SCREEN) | ||
car.draw(SCREEN) | ||
|
||
if len(cars) == 0: | ||
break | ||
|
||
for i, car in enumerate(cars): | ||
ge[i].fitness += 1 | ||
if not car.sprite.alive: | ||
remove(i) | ||
|
||
for i, car in enumerate(cars): | ||
print(car.sprite.data()) | ||
output = nets[i].activate(car.sprite.data()) | ||
#print(output) | ||
if output[0] > 0.7: | ||
car.sprite.direction = -1 | ||
if output[1] > 0.7: | ||
car.sprite.direction = 1 | ||
if output[0] <= 0.7 and output[1] <= 0.7: | ||
car.sprite.direction = 0 | ||
|
||
clock.tick(30) | ||
pygame.display.update() | ||
|
||
|
||
# Setup the NEAT Neural Network | ||
def run(config_path): | ||
global pop | ||
config = neat.config.Config( | ||
neat.DefaultGenome, | ||
neat.DefaultReproduction, | ||
neat.DefaultSpeciesSet, | ||
neat.DefaultStagnation, | ||
config_path | ||
) | ||
|
||
pop = neat.Population(config) | ||
|
||
pop.add_reporter(neat.StdOutReporter(True)) | ||
stats = neat.StatisticsReporter() | ||
pop.add_reporter(stats) | ||
|
||
pop.run(eval_genomes, 50) | ||
|
||
|
||
if __name__ == '__main__': | ||
local_dir = os.path.dirname(__file__) | ||
config_path = os.path.join(local_dir, 'config.txt') | ||
run(config_path) | ||
|