Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import pygame
- import random
- import threading
- import math
- import time
- from pygame import gfxdraw
- GREY = (54, 57, 63)
- RED = (255, 0, 0)
- DARK_RED = (165, 42, 42)
- GREEN = (124,252,0)
- DARK_GREEN = (0,128,0)
- BLUE = (0,0,128)
- PINK = (211, 54, 130)
- class Main:
- window_width = 700
- window_height = 700
- running = True
- exit_flag = False
- def __init__(self):
- pygame.init()
- self.counter = 0
- self.finished = 0
- self.pop_size = 200
- self.lifespan = 400
- self.fps = 60
- self.window_width = Main.window_width
- self.window_height = Main.window_height
- self.target = (300, 100)
- self.reached_goal = False
- self.clock = pygame.time.Clock()
- self.gen_count = 0
- self.display = pygame.display.set_mode((self.window_width, self.window_height))
- self.font = pygame.font.SysFont('Segoe UI', 20)
- rect_width = 200
- self.obstacle = pygame.Rect((int(self.window_width/2-rect_width/2), int(self.window_height*0.4)) , (rect_width, 50))
- self.rocket_pop = Population()
- def constructor(self):
- self.counter = 0
- self.finished = 0
- self.pop_size = 200
- self.lifespan = 400
- self.fps = 60
- self.window_width = Main.window_width
- self.window_height = Main.window_height
- self.target = (300, 100)
- self.reached_goal = False
- self.display = pygame.display.set_mode((self.window_width, self.window_height))
- self.clock = pygame.time.Clock()
- self.gen_count = 0
- self.font = pygame.font.SysFont('Segoe UI', 20)
- rect_width = 200
- self.obstacle = pygame.Rect((int(self.window_width/2-rect_width/2), int(self.window_height*0.4)) , (rect_width, 50))
- def draw_text(self):
- gen_count_text = self.font.render("generation: "+str(self.gen_count), True, (255, 255, 255))
- finished_text = self.font.render("reached the goal: "+str(self.finished), True, (255, 255, 255))
- lifespan_text = self.font.render("lifespan: "+str(self.counter+1)+"/"+str(self.lifespan), True, (255, 255, 255))
- self.display.blit(gen_count_text, (50, 50))
- self.display.blit(finished_text, (50, 90))
- self.display.blit(lifespan_text, (50, 130))
- def draw_obstacles(self):
- pygame.draw.rect(self.display, (0,0,0), self.obstacle)
- @staticmethod
- def pause_simulation():
- if Main.running:
- Main.running = False
- else:
- Main.resume_simulation()
- @staticmethod
- def resume_simulation():
- Main.running = True
- @staticmethod
- def pygame_event_check():
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- pygame.quit()
- quit()
- def run(self):
- while not self.exit_flag:
- self.display.fill(GREY)
- self.draw_text()
- self.draw_obstacles()
- self.pygame_event_check()
- if self.running:
- if self.counter >= self.lifespan:
- self.counter = 0
- new_generation, finished = self.rocket_pop.selection()
- self.rocket_pop = Population(new_generation)
- time.sleep(1)
- self.gen_count += 1
- if finished > 0 and self.reached_goal == False:
- print(f"Reached the goal in {self.gen_count} generations")
- self.reached_goal = True
- for rocket in self.rocket_pop.population:
- rocket.update(self.counter)
- self.counter += 1
- self.clock.tick(self.fps)
- pygame.draw.circle(self.display, PINK, self.target, 20)
- pygame.display.update()
- while not self.exit_flag:
- self.pygame_event_check()
- print("exit")
- class DNA():
- def __init__(self, lifespan, max_vel, genes=[]):
- self.max_vel = max_vel
- self.mutation_chance = 0.05
- if genes == []:
- self.genes = [(random.uniform(-self.max_vel, self.max_vel), random.uniform(-self.max_vel, self.max_vel)) for i in range(lifespan)]
- else:
- self.genes = genes
- def crossover(self, partner):
- new_genes = []
- mid = random.randint(1, len(self.genes))
- for i in range(len(self.genes)):
- if i < mid:
- new_genes.append(self.genes[i])
- else:
- new_genes.append(partner.dna.genes[i])
- return new_genes
- def mutate(self):
- for gene in self.genes:
- chance_to_mutate = random.random()
- if chance_to_mutate < self.mutation_chance:
- gene = (random.uniform(-self.max_vel, self.max_vel), random.uniform(-self.max_vel, self.max_vel))
- class Rocket(Main):
- def __init__(self, starting_pos, genes=[]):
- Main.constructor(self)
- self.path = []
- self.position = starting_pos
- self.starting_pos = starting_pos
- self.arrow_size = 5
- Rocket.max_vel = 0.2
- self.acceleration = (0,0)
- self.velocity = (0,0)
- self.crashed = False
- self.finished = False
- self.dna = DNA(self.lifespan, self.max_vel, genes)
- def update(self, counter):
- #self.acceleration = self.normalise(self.dna.genes[counter])*max_vel
- if not self.crashed and not self.finished:
- self.acceleration = self.dna.genes[counter]
- self.velocity = (self.velocity[0]+self.acceleration[0], self.velocity[1]+self.acceleration[1])
- self.position = (self.velocity[0]+self.position[0], self.velocity[1]+self.position[1])
- self.acceleration *= 0
- self.check_pos()
- self.draw(counter)
- def draw(self, counter):
- arrow_x = self.normalise(self.velocity)[0]*self.arrow_size
- arrow_y = self.normalise(self.velocity)[1]*self.arrow_size
- pygame.gfxdraw.aatrigon(self.display,int(self.position[0]+arrow_x*self.arrow_size), int(self.position[1]+arrow_y*self.arrow_size),
- int(self.position[0]-arrow_y*self.arrow_size/3), int(self.position[1]+arrow_x*self.arrow_size/3),
- int(self.position[0]+arrow_y*self.arrow_size/3), int(self.position[1]-arrow_x*self.arrow_size/3),
- (GREEN))
- #if counter % 10 == 0:
- # self.path.append((int(self.position[0]), int(self.position[1])))
- #for marker in self.path:
- #pygame.draw.circle(display, GREEN, marker, 2)
- distance = self.get_distance(self.starting_pos, self.position)
- #if distance > 710:
- # index = 710
- #else:
- # index = int(distance)
- #marker = {"colour": colour, "pos":(int(self.position[0]), int(self.position[1]))}
- #colour = hex_to_rgb(rb_gradient[index].hex)
- #noise_val = math.sqrt(n.world[int(self.position[0])][int(self.position[1])]**2)
- def check_pos(self):
- arrow_x = self.normalise(self.velocity)[0]*self.arrow_size
- arrow_y = self.normalise(self.velocity)[1]*self.arrow_size
- d = self.get_distance(self.position, self.target)
- if self.position[0]+arrow_x*self.arrow_size > self.window_width:
- self.crashed = True
- elif self.position[0]+arrow_x*self.arrow_size < 0:
- self.crashed = True
- elif self.position[1]+arrow_y*self.arrow_size > self.window_height:
- self.crashed = True
- elif self.position[1]+arrow_y*self.arrow_size < 0:
- self.crashed = True
- if self.obstacle.collidepoint((self.position[0]+arrow_x*self.arrow_size, self.position[1]+arrow_y*self.arrow_size)):
- self.crashed = True
- if d < 20:
- self.finished = True
- @staticmethod
- def normalise(vector):
- magnitude = math.sqrt(vector[0]**2+vector[1]**2)
- if magnitude != 0:
- return (vector[0]/magnitude, vector[1]/magnitude)
- return (0,0)
- @staticmethod
- def get_distance(coord1, coord2):
- return math.sqrt(((coord2[1]-coord1[1])**2)+((coord2[0]-coord1[0])**2))
- @staticmethod
- def round_to_1(x):
- return round(x, -int(math.floor(math.log10(abs(x)))))
- class Population(Main):
- def __init__(self, population=[]):
- Main.constructor(self)
- self.finished = 0
- self.starting_pos = (int(self.window_width/2), self.window_height)
- self.calulate_min_mates()
- if population == []:
- self.population = [Rocket(self.starting_pos) for i in range(self.pop_size)]
- else:
- self.population = population
- def selection(self):
- max_fitness = 0
- total_fitness = 0
- list_of_fitnesses = []
- new_population = []
- mating_pool = []
- mates = []
- d_target = Rocket.get_distance(self.starting_pos, self.target)
- for rocket in self.population:
- d = Rocket.get_distance(rocket.position, self.target)
- rocket.fitness = 100/d
- if rocket.crashed:
- rocket.fitness *= 0.5
- if rocket.finished:
- self.finished += 1
- rocket.fitness *= 2
- list_of_fitnesses.append(rocket.fitness)
- while len(mating_pool) < self.min_mates-1:
- mate = random.choices(self.population, list_of_fitnesses, k=1)
- self.diversity_update(mate[0])
- mating_pool.append(mate[0])
- while len(new_population) < self.pop_size:
- for rocket in mating_pool:
- if len(new_population) > self.pop_size:
- break
- mate = random.choice(mating_pool)
- if (rocket, mate) not in mates:
- child_dna = rocket.dna.crossover(mate)
- child_dna = DNA(self.lifespan, Rocket.max_vel, child_dna)
- child_dna.mutate()
- new_population.append(Rocket(self.starting_pos, child_dna.genes))
- mates.append((rocket, mate))
- return new_population, self.finished
- def diversity_update(self, mate):
- max_distance = 0
- mate_with_max = mate
- for rocket in self.population:
- if rocket is mate:
- continue
- d = Rocket.get_distance(rocket.position, mate.position)
- if d > max_distance:
- max_distance = d
- mate_with_max = rocket
- mate_with_max.fitness *= 2
- def calulate_min_mates(self):
- self.min_mates = 2
- no_children = 1
- while no_children < self.pop_size:
- no_children = int(math.factorial(self.min_mates)/(2*(math.factorial(self.min_mates-2))))
- self.min_mates += 1
- def run():
- main = Main()
- mThread = threading.Thread(target=main.run)
- threads.append(mThread)
- mThread.start()
- mThread.join()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement