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()
|