-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
153 lines (122 loc) · 5.15 KB
/
Copy pathapp.py
File metadata and controls
153 lines (122 loc) · 5.15 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
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import pygame
import random
from sty import fg
from functions import (
generate_cities, generate_individuals,
get_route_from_individual, get_cities_ids_from_individual,
create_child, calculate_score
)
from settings import (
SCREEN_SIZE, BACKGROUND_COLOR,
FONT_STYLE, FONT_SIZE, FONT_COLOR,
START_POINT_POS, START_POINT_COLOR, START_POINT_RADIUS
)
pygame.font.init()
class App:
def __init__(self, cities_num, population_size, next_round_passers_num, mutation_chance):
if not cities_num >= 2:
raise ValueError('Wrong value for cities_num (cities_num >= 2)')
if not population_size > 2:
raise ValueError('Wrong value for population_size (population_size > 2)')
if not 0 < next_round_passers_num < population_size:
raise ValueError('Wrong value for next_round_passers_num (0 < val < population_size)')
if not 0 <= mutation_chance <= 1:
raise ValueError('Wrong value for mutation_chance (0 < val < 1)')
self.screen = pygame.display.set_mode(SCREEN_SIZE)
self.screen.fill(BACKGROUND_COLOR)
self.font = pygame.font.SysFont(FONT_STYLE, FONT_SIZE)
pygame.display.set_caption('Genetic algo')
self.cities = generate_cities(cities_num)
self.generation = Generation(population_size, next_round_passers_num, mutation_chance, self.cities)
self.routes_to_be_displayed = []
def run(self):
running = True
while running:
generation_winner = self.generation.get_generation_winners()[0]
self.add_route( # display the best route from generation
get_route_from_individual(generation_winner),
{
'route': get_cities_ids_from_individual(generation_winner),
'score': generation_winner['score']
}
)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
self.refresh_screen()
self.generation.make_new_generation()
def draw_cities(self):
for city in self.cities:
text_surface = self.font.render(
str(city['id']), False, FONT_COLOR
)
self.screen.blit(text_surface, city['pos'])
def draw_start_point(self):
pygame.draw.circle(
self.screen, START_POINT_COLOR, START_POINT_POS, START_POINT_RADIUS
)
def draw_routes(self):
for route_obj in self.routes_to_be_displayed:
pygame.draw.lines(
self.screen, route_obj['color'], True,
[START_POINT_POS] + route_obj['route'], 2
)
def add_route(self, route, log_info=None):
# if there is no route like the given one
if route not in [route_obj['route'] for route_obj in self.routes_to_be_displayed]:
color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
route_obj = {
'route': route,
'color': color,
}
self.routes_to_be_displayed.append(route_obj)
if log_info:
log = fg(*color) + str(log_info) + fg.rs
print(log)
def refresh_screen(self):
self.screen.fill(BACKGROUND_COLOR)
self.draw_start_point()
self.draw_routes()
self.draw_cities()
pygame.display.flip()
class Generation:
def __init__(self, population_size, next_round_passers_num, mutation_chance, cities):
self.generation_number = 0
self.mutation_chance = mutation_chance
self.population_size = population_size
self.individuals = generate_individuals(self.population_size, cities)
self.next_round_passers_num = next_round_passers_num
def calc_individuals_score(self):
for individual in self.individuals:
individual['score'] = calculate_score(
get_route_from_individual(individual)
)
def get_generation_winners(self):
self.calc_individuals_score()
self.individuals = sorted(
self.individuals, key=lambda i: i['score']
)[:self.next_round_passers_num]
return self.individuals
def make_new_generation(self):
if random.uniform(0, 1) <= self.mutation_chance:
self.mutate()
current_population_size = len(self.individuals)
parents = self.individuals[:]
while current_population_size != self.population_size:
parent1, parent2 = random.choices(parents, k=2)
self.individuals.append(
create_child(parent1, parent2)
)
current_population_size += 1
self.generation_number += 1
def mutate(self):
individual = random.choice(self.individuals)
genome = individual['genome']
gene1, gene2 = random.choices(genome, k=2)
i1 = genome.index(gene1)
i2 = genome.index(gene2)
genome[i1], genome[i2] = genome[i2], genome[i1]
def log_individuals_info(self):
for individual in self.individuals:
print(individual['genome'], individual['score'])
print('------------------------')