Merge pull request 'Created genetic algorithm. Added returned matrix of genetic algorithm into settings file. Replaced fields with map generated by algorithm.' (#14) from genetic_algorithm into main

Reviewed-on: s473634/TurboTraktor#14
This commit is contained in:
Jakub Paszke 2023-06-18 18:16:54 +02:00
commit 6cba5133a3
5 changed files with 176 additions and 4 deletions

Binary file not shown.

View File

@ -1,7 +1,7 @@
import pygame
import sys
import random
from settings import SIZE, directions, draw_lines_on_window
from settings import SIZE, directions, draw_lines_on_window, matrix_plants_type
from src.map import drawRoads, seedForFirstTime, return_fields_list, WORLD_MATRIX, get_type_by_position
from src.Tractor import Tractor
from src.bfs import Astar
@ -45,6 +45,7 @@ def recognize_plants(fields, destination):
print(pred)
return pred
# pygame initialization
pygame.init()
clock = pygame.time.Clock()

View File

@ -1,6 +1,11 @@
from cmath import sqrt
import pygame
import pickle
def load_matrix(filename):
with open(filename, 'rb') as file:
matrix = pickle.load(file)
return matrix
screen_width = 1368
screen_height = 936
@ -18,6 +23,7 @@ field_width = 4
field_height = 4
field_size = field_width*field_height
fields_amount = 25
matrix_plants_type = load_matrix("genetic_algorithm_matrix.pkl")
directions = {0: 'UP', 90: 'RIGHT', 180: 'DOWN', 270: 'LEFT'}
@ -29,4 +35,6 @@ def draw_lines_on_window(background):
pygame.draw.line(background, (0, 0, 0), (968, 285), (1336 , 285))
pygame.draw.line(background, (0, 0, 0), (968, 649), (1336 , 649))
pygame.draw.line(background, (0, 0, 0), (968, 285), (968, 649))
pygame.draw.line(background, (0, 0, 0), (1336, 285), (1336, 649))
pygame.draw.line(background, (0, 0, 0), (1336, 285), (1336, 649))

161
src/genetic_algorithm.py Normal file
View File

@ -0,0 +1,161 @@
import random
import copy
import pickle
import numpy
# Constants
GRID_SIZE = 5
PLANT_TYPES = [1, 2, 3]
POPULATION_SIZE = 1000
MAX_GENERATIONS = 100
MUTATION_RATE = 0.1
def generate_random_chromosome():
# Generate a random chromosome (random matrix)
chromosome = [[0] * GRID_SIZE for _ in range(GRID_SIZE)]
for row in range(GRID_SIZE):
for col in range(GRID_SIZE):
available_types = PLANT_TYPES.copy()
chromosome[row][col] = random.choice(available_types)
return chromosome
def calculate_fitness(chromosome):
# Calculate the fitness by counting the number of adjacent fields with the same plant type
fitness = 0
appearances = [0,0,0]
for row in range(GRID_SIZE):
for col in range(GRID_SIZE):
plant_type = chromosome[row][col]
# Check left neighbor
if col > 0 and chromosome[row][col - 1] == plant_type:
fitness += 1
# Check top neighbor
if row > 0 and chromosome[row - 1][col] == plant_type:
fitness += 1
for row in chromosome:
appearances[0] = appearances[0] + row.count(1)
appearances[1] = appearances[1] + row.count(2)
appearances[2] = appearances[2] + row.count(3)
for i in range(len(appearances)):
if appearances[i] < 7 and appearances[i] > 9:
fitness = fitness + abs(appearances[i]-8)
return fitness
def selection(population):
# Perform tournament selection to choose parents for reproduction
tournament_size = 5
parents = []
for _ in range(len(population)):
tournament = random.sample(population, tournament_size)
tournament.sort(key=lambda chromosome: calculate_fitness(chromosome))
parents.append(tournament[0])
return parents
def crossover(parent1, parent2):
# Perform single-point crossover to create two offspring
crossover_point = random.randint(0, (GRID_SIZE*GRID_SIZE)-1)
row = crossover_point // GRID_SIZE
column = crossover_point % GRID_SIZE
offspring1 = []
offspring2 = []
counter = 0
while counter<row:
offspring1.append(parent1[counter])
offspring2.append(parent2[counter])
counter += 1
# The place, where the parents are 'cutten' is here
offspring1.append(parent1[counter][:column] + parent2[counter][column:])
offspring2.append(parent2[counter][:column] + parent1[counter][column:])
while counter<GRID_SIZE-1:
offspring1.append(parent2[counter])
offspring2.append(parent1[counter])
counter += 1
return offspring1, offspring2
def genetic_algorithm():
results = []
population = [generate_random_chromosome() for _ in range(POPULATION_SIZE)]
# Main loop
for gen in range(MAX_GENERATIONS):
parents = selection(population)
offspring = []
# Crossover
for i in range(0, len(parents), 2):
parent1 = parents[i]
parent2 = parents[i + 1]
child1, child2 = crossover(parent1, parent2)
offspring.append(child1)
offspring.append(child2)
# Replacement
population = offspring
# Count best fitness of each population
best_chromosome = min(population, key=lambda chromosome: calculate_fitness(chromosome))
best_fitness = calculate_fitness(best_chromosome)
results.append((copy.deepcopy(best_chromosome), best_fitness))
if best_fitness == 0:
break
results.sort(key = lambda x: x[1])
best_tuple = results[0]
best_chromosome, best_fitness = best_tuple
return best_chromosome
# Replacing numbers into plants:
def replace_numbers(number_matrix):
return_matrix = number_matrix.copy()
for row in range(GRID_SIZE):
for col in range(GRID_SIZE):
if number_matrix[row][col] == 1:
return_matrix[row][col] = "carrot"
elif number_matrix[row][col] == 2:
return_matrix[row][col] = "potato"
elif number_matrix[row][col] == 3:
return_matrix[row][col] = "wheat"
else:
number_matrix[row][col] = "error"
return return_matrix
# Make the matrix as a list:
def list_of_matrix(plant_matrix):
return_list = []
for i in range(len(plant_matrix)):
return_list += plant_matrix[i]
return return_list
# Run the genetic algorithm
best_solution = genetic_algorithm()
# Print the best solution found
print("Best Solution:")
for row in best_solution:
print(row)
print("Best fitness: ", calculate_fitness(best_solution))
print("")
replaced_matrix = replace_numbers(best_solution)
print("Replaced Matrix:")
for row in replaced_matrix:
print(row)
return_list = list_of_matrix(replaced_matrix)
with open("genetic_algorithm_matrix.pkl", "wb") as file:
pickle.dump(return_list, file)

View File

@ -1,6 +1,6 @@
from cmath import sqrt
import pygame
from settings import screen_height, screen_width, SIZE, SPECIES, block_size, tile, road_coords, fields_amount, field_size, field_height, field_width
from settings import screen_height, screen_width, SIZE, SPECIES, block_size, tile, road_coords, fields_amount, field_size, field_height, field_width, matrix_plants_type
from src.Plant import Plant
import random
from src.Field import Field
@ -60,9 +60,11 @@ def drawRoads(screen):
return screen
def seedForFirstTime():
plants_type = matrix_plants_type
plant_group = pygame.sprite.Group()
for field in range(fields_amount):
plant_name = random.choice(SPECIES)
plant_name = matrix_plants_type[field]
blocks_seeded_in_field = 0
while (blocks_seeded_in_field < field_size):