-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathpopulation.py
More file actions
77 lines (62 loc) · 2.73 KB
/
population.py
File metadata and controls
77 lines (62 loc) · 2.73 KB
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
# PY5 IMPORTED MODE CODE
from dna import DNA
from rocket import Rocket
class Population:
# Needs "lifespan", "xy" parameter because Python modules have isolated namespaces.
# (p5.js sketches share a single global scope)
def __init__(
self, mutation: float, length: int, lifespan: int, xy: tuple[float, float]
):
"""Population has variables to keep track of the mutation rate, current
population list, and number of generations."""
self.mutation_rate = mutation # Mutation rate.
self.generations = 0 # Number of generations
self.x, self.y = xy
# List to hold the current population.
self.population = [
Rocket(self.x, self.y, DNA(lifespan)) for _ in range(length)
]
def live(self) -> None:
"""The run() method takes care of the simulation, updates the rocket's
position, and draws it to the canvas."""
for rocket in self.population:
rocket.run()
# Needs "target" parameter because Python modules have isolated namespaces.
def fitness(self, target: Py5Vector2D) -> None:
"""Calculate the fitness for each rocket."""
for rocket in self.population:
rocket.calculate_fitness(target)
def selection(self) -> None:
"""The selection method normalizes all the fitness values."""
# Sum all the fitness values.
total_fitness = sum(rocket.fitness for rocket in self.population)
# Divide by the total to normalize the fitness values.
for rocket in self.population:
rocket.fitness /= total_fitness
def reproduction(self) -> None:
new_population = [] # Separate the list for the next generation.
for _ in range(len(self.population)):
# Now use the weighted selection algorithm.
parent_a = self.weighted_selection()
parent_b = self.weighted_selection()
child = parent_a.crossover(parent_b)
child.mutate(self.mutation_rate)
# Rocket goes in the new population.
new_population.append(Rocket(self.x, self.y, child))
# Now the new population is the current one.
self.population = new_population
self.generations += 1
def weighted_selection(self) -> DNA:
# Start with the first element.
index = 0
# Pick a starting point.
start = random()
# At the finish line?
while start > 0:
# Move a distance according to fitness.
start -= self.population[index].fitness
# Pass the baton to the next element.
index += 1
# Undo moving to the next element since the finish has been reached.
index -= 1
return self.population[index].dna