adding genetic algorithm implementation that chooses which plants to farm #3
@ -26,3 +26,7 @@ class Constants:
|
||||
CACTUS = 'kaktus'
|
||||
POTATO = 'ziemniak'
|
||||
WEATH = 'pszenica'
|
||||
|
||||
# Genetic algorithm points average
|
||||
|
||||
POINTS_AVERAGE = 20
|
||||
|
52
src/utils/GeneticAlgorithm.py
Normal file
52
src/utils/GeneticAlgorithm.py
Normal file
@ -0,0 +1,52 @@
|
||||
from Plants import *
|
||||
|
||||
from random import choice, random
|
||||
|
||||
|
||||
class GeneticAlgorithm:
|
||||
|
||||
def __init__(self):
|
||||
self.mutation_probability = 0.1
|
||||
self.stop_condition = stop_condition
|
||||
|
||||
def selection_strategy(self, generation: BaseField):
|
||||
maximum_selected_items = int(len(generation) / 10)
|
||||
sorted_elements = sorted(generation, key=lambda x: x.evaluation)
|
||||
return sorted[:maximum_selected_items]
|
||||
|
||||
def _generate_random_plants(self):
|
||||
plant_names = [choice(BaseField.possibilities) for _ in range(9)]
|
||||
return [plant_selector(plant_name) for plant_name in plant_names]
|
||||
|
||||
def _generate_first_population(self):
|
||||
return [BaseField(self._generate_random_plants()) for _ in range(100)]
|
||||
|
||||
def run(self):
|
||||
for population in self._generate_first_population():
|
||||
print(population)
|
||||
first_population = self._generate_first_population()
|
||||
first_population.sort(key=lambda x: x.evaluation)
|
||||
population_length = len(first_population)
|
||||
i = 0
|
||||
while True:
|
||||
selected = self.selection_strategy(first_population)
|
||||
new_population = selected.copy()
|
||||
while len(new_population) != population_length:
|
||||
child = choice(first_population).crossover(choice(first_population))
|
||||
if random() <= self.mutation_probability:
|
||||
child.mutate()
|
||||
new_population.append(child)
|
||||
|
||||
first_population = new_population
|
||||
best_match = min(first_population, key=lambda x: x.evaluation)
|
||||
i += 1
|
||||
if self.stop_condition(best_match):
|
||||
break
|
||||
|
||||
|
||||
def main():
|
||||
GeneticAlgorithm().run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
80
src/utils/Plants.py
Normal file
80
src/utils/Plants.py
Normal file
@ -0,0 +1,80 @@
|
||||
from constants import Constants
|
||||
from random import randint, choice
|
||||
|
||||
|
||||
class BasePlant:
|
||||
|
||||
def __init__(self):
|
||||
self.appearance_points = 0
|
||||
self.difficulty_points = 0
|
||||
self.profit_points = 0
|
||||
|
||||
def first_population_generator(self):
|
||||
pass
|
||||
|
||||
def __int__(self):
|
||||
return sum([self.appearance_points, self.difficulty_points, self.profit_points])
|
||||
|
||||
def __float__(self):
|
||||
return self.__int__()
|
||||
|
||||
|
||||
def stop_condition(average):
|
||||
return average == Constants.POINTS_AVERAGE
|
||||
|
||||
|
||||
def plant_selector(plant_name: str) -> BasePlant:
|
||||
if plant_name == Constants.POTATO:
|
||||
return Potato()
|
||||
elif plant_name == Constants.WEATH:
|
||||
return Weath()
|
||||
elif plant_name == Constants.CACTUS:
|
||||
return Cactus()
|
||||
|
||||
|
||||
class BaseField:
|
||||
"""Class that represents what plants grow on a certain field divided into 9 tiles"""
|
||||
|
||||
possibilities = [Constants.WEATH, Constants.POTATO, Constants.CACTUS]
|
||||
|
||||
def __init__(self, plants):
|
||||
self.plants: list(BasePlant) = plants
|
||||
self.evaluation = self.evaluate_function()
|
||||
|
||||
def mutate(self):
|
||||
self._perform_mutation()
|
||||
self.evaluation = self.evaluate_function()
|
||||
|
||||
def _perform_mutation(self):
|
||||
random_index = randint(0, 8)
|
||||
self.plants[random_index] = plant_selector(choice(self.possibilities))
|
||||
|
||||
def crossover(self, other_field):
|
||||
length = int(randint(0, 8))
|
||||
new_plants = self.plants[:length] + other_field.plants[length:]
|
||||
return BaseField(new_plants)
|
||||
|
||||
def evaluate_function(self):
|
||||
current_fields_average = sum([int(plant) for plant in self.plants]) / 9
|
||||
return Constants.POINTS_AVERAGE - current_fields_average
|
||||
|
||||
def __str__(self):
|
||||
return ''.join([str(plant) + ' ' for plant in self.plants])
|
||||
|
||||
|
||||
class Potato(BasePlant):
|
||||
|
||||
def __str__(self):
|
||||
return Constants.POTATO
|
||||
|
||||
|
||||
class Cactus(BasePlant):
|
||||
|
||||
def __str__(self):
|
||||
return Constants.CACTUS
|
||||
|
||||
|
||||
class Weath(BasePlant):
|
||||
|
||||
def __str__(self):
|
||||
return Constants.WEATH
|
Loading…
Reference in New Issue
Block a user