summaryrefslogtreecommitdiffstats
path: root/Weekly Challenges/Week 4/genetic algorithm.py
blob: 4e6657f5bd0d8c5acfc882260f3f00d4a1b5abec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import pygame
from random import random, choices
from math import hypot


SCREEN_WIDTH = 800
SCREEN_HEIGHT = 800
STEPS_PER_GENERATION = 2000

class Rocket:
    acceleration_rate = 0.005
    radius = 10
    fitness_cutoff = 400

    def __init__(self, dna = None):
        self.position = pygame.Vector2(SCREEN_WIDTH / 2, SCREEN_HEIGHT)
        self.velocity = pygame.Vector2()
        if dna == None:
            self.dna = []
            for i in range(STEPS_PER_GENERATION):
                self.dna.append(pygame.Vector2(random()*2-1,random()*2-1))
                self.dna[i].scale_to_length(self.acceleration_rate)
        else:
            self.dna = dna

    def update(self, step):
        self.position += self.velocity
        self.velocity += self.dna[step]

    def show(self, screen):
        pygame.draw.circle(screen, (255, 255, 255), self.position, 10)

class Population:
    population_size = 30
    max_acceptable_distance = SCREEN_HEIGHT
    mutation_rate = 0.0005


    def __init__(self):
        self.rockets = [Rocket() for _ in range(self.population_size)]
        self.step = 0
        self.top_fitness = 0
        
    def show(self, screen):
        for rocket in self.rockets:
            rocket.show(screen)

    def update(self, target):
        for rocket in self.rockets:
            rocket.update(self.step)
        self.step += 1
        if self.step == STEPS_PER_GENERATION:
            self.step = 0
            self.get_next_generation(target)

    def get_next_generation(self, target):

        # Rank the performance of the rockets; "calculate the fitness"
        fitnesses = []
        best_fitness = 0
        for rocket in self.rockets:
            distance_from_target = hypot(rocket.position.x - target.x, rocket.position.y - target.y)

            f = max(0.01, self.max_acceptable_distance - distance_from_target)
            best_fitness = max(f, best_fitness)
            fitnesses.append(f)
        self.top_fitness = max(self.top_fitness, best_fitness)
        print(f"{best_fitness=} {self.top_fitness=}")
        # Using these rankings, find "parents" to use for the next generation of rockets
        parentsA = choices(self.rockets, fitnesses, k = self.population_size)
        parentsB = choices(self.rockets, fitnesses, k = self.population_size)

        # Create the children, save them into self.rockets
        children = []

        for i in range(self.population_size):
            children.append(self.get_child(parentsA[i], parentsB[i]))
        self.rockets = children

    def get_child(self, parentA, parentB):
        new_dna = []
        for i in range(STEPS_PER_GENERATION):
            if random() < 0.5:
                new_dna.append(parentA.dna[i])
            else:
                new_dna.append(parentB.dna[i])

            # Mutate
            if random() < self.mutation_rate:
                new_dna[i] = pygame.Vector2(random()*2-1,random()*2-1)
                new_dna[i].scale_to_length(Rocket.acceleration_rate)

        return Rocket(new_dna) # <-- we need to pass dna into the new rocket

screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))

population = Population()

is_running = True

target = pygame.Vector2(SCREEN_WIDTH/2, SCREEN_HEIGHT/2)

while is_running:
    screen.fill((0, 0, 0))

    pygame.draw.circle(screen, (255,0,0), target, 10)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            is_running = False

    population.update(target)
    population.show(screen)

    pygame.display.update()

pygame.quit()