Merge pull request 'Genetic-algorythm' (#29) from Genetic-algorythm into main
Reviewed-on: #29 Reviewed-by: Nastassia Zhuravel <naszhu@st.amu.edu.pl>
This commit is contained in:
commit
10ac917df3
3
.gitignore
vendored
3
.gitignore
vendored
@ -4,4 +4,5 @@
|
|||||||
__pycache__
|
__pycache__
|
||||||
|
|
||||||
#PyCharm
|
#PyCharm
|
||||||
.idea/
|
.idea/
|
||||||
|
AI_brain/model.h5
|
||||||
|
281
AI_brain/genetic_algorytm.py
Normal file
281
AI_brain/genetic_algorytm.py
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
import copy
|
||||||
|
import random
|
||||||
|
import configparser
|
||||||
|
import math
|
||||||
|
|
||||||
|
import pygame
|
||||||
|
from domain.entities.entity import Entity
|
||||||
|
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read("config.ini")
|
||||||
|
|
||||||
|
from domain.world import World
|
||||||
|
from AI_brain.rotate_and_go_aStar import RotateAndGoAStar, State
|
||||||
|
from random import randint
|
||||||
|
|
||||||
|
|
||||||
|
hits = 0
|
||||||
|
misses = 0
|
||||||
|
|
||||||
|
|
||||||
|
class Cashed_sub_paths(dict):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def __missing__(self, key):
|
||||||
|
self[key] = Cashed_sub_paths()
|
||||||
|
return self[key]
|
||||||
|
|
||||||
|
|
||||||
|
class Cashed_sub_path:
|
||||||
|
def __init__(self, sub_path: list[str] = [], distance: int = 0):
|
||||||
|
self.sub_path = sub_path
|
||||||
|
self.distance = distance
|
||||||
|
|
||||||
|
|
||||||
|
steps_distance_cashed: dict[tuple[int, int], Cashed_sub_path] = Cashed_sub_paths()
|
||||||
|
|
||||||
|
|
||||||
|
class Path:
|
||||||
|
def __init__(self):
|
||||||
|
self.walk = []
|
||||||
|
self.permutation = []
|
||||||
|
self.real_path = []
|
||||||
|
self.distance = 0
|
||||||
|
|
||||||
|
def random_walk(self, dusts: list[Entity]):
|
||||||
|
permutation = generate_random_permutation(len(dusts))
|
||||||
|
self.permutation = permutation
|
||||||
|
|
||||||
|
self.walk = addStartAndStation(permutation)
|
||||||
|
|
||||||
|
def calculate_distance(self, world: World):
|
||||||
|
distance = 0
|
||||||
|
for i in range(len(self.walk) - 1):
|
||||||
|
next_distance, next_real_path = self.step_distance(
|
||||||
|
self.walk[i], self.walk[i + 1], world
|
||||||
|
)
|
||||||
|
distance += next_distance
|
||||||
|
|
||||||
|
# BUG this part is not working and is not used, B.1 must be resolved
|
||||||
|
self.real_path = self.real_path + ["DEFAULT_ROTATION"] + next_real_path
|
||||||
|
|
||||||
|
self.distance = distance
|
||||||
|
|
||||||
|
def step_distance(
|
||||||
|
self, from_id: int, to_id: int, world: World
|
||||||
|
) -> tuple[int, list[str]]:
|
||||||
|
global hits, misses
|
||||||
|
if (from_id, to_id) in steps_distance_cashed:
|
||||||
|
hits += 1
|
||||||
|
distance = steps_distance_cashed[(from_id, to_id)].distance
|
||||||
|
sub_path = steps_distance_cashed[(from_id, to_id)].sub_path
|
||||||
|
return distance, sub_path
|
||||||
|
|
||||||
|
misses += 1
|
||||||
|
path_searcher = RotateAndGoAStar(
|
||||||
|
world,
|
||||||
|
self.getPosition(from_id, world.dustList),
|
||||||
|
self.getPosition(to_id, world.dustList),
|
||||||
|
)
|
||||||
|
path_searcher.search()
|
||||||
|
|
||||||
|
steps_distance_cashed[(from_id, to_id)] = Cashed_sub_path(
|
||||||
|
path_searcher.actions, path_searcher.cost
|
||||||
|
)
|
||||||
|
|
||||||
|
# BUG B.1 inverse path
|
||||||
|
inverse_sub_path = path_searcher.actions.copy()
|
||||||
|
steps_distance_cashed[(to_id, from_id)] = Cashed_sub_path(
|
||||||
|
inverse_sub_path, path_searcher.cost
|
||||||
|
)
|
||||||
|
return path_searcher.cost, path_searcher.actions
|
||||||
|
|
||||||
|
def inverse_sub_path(sub_path: list[str]) -> list[str]:
|
||||||
|
sub_path.reverse()
|
||||||
|
for command in sub_path:
|
||||||
|
command.replace("RL", "RR")
|
||||||
|
command.replace("RR", "RR")
|
||||||
|
|
||||||
|
def getPosition(
|
||||||
|
self,
|
||||||
|
number: int,
|
||||||
|
dustList: list[Entity],
|
||||||
|
) -> State:
|
||||||
|
if number == -1:
|
||||||
|
dock_start_x, dock_start_y = config.get(
|
||||||
|
"CONSTANT", "DockStationStartPosition"
|
||||||
|
).split(",")
|
||||||
|
dock_start_x, dock_start_y = int(dock_start_x), int(dock_start_y)
|
||||||
|
|
||||||
|
return State(dock_start_x, dock_start_y)
|
||||||
|
|
||||||
|
if number == -2:
|
||||||
|
vacuum_start_x, vacuum_start_y = config.get(
|
||||||
|
"CONSTANT", "RobotStartPosition"
|
||||||
|
).split(",")
|
||||||
|
|
||||||
|
vacuum_start_x, vacuum_start_y = int(vacuum_start_x), int(vacuum_start_y)
|
||||||
|
return State(vacuum_start_x, vacuum_start_y)
|
||||||
|
|
||||||
|
return State(dustList[number].x, dustList[number].y)
|
||||||
|
|
||||||
|
def get_real_path(self, world: World):
|
||||||
|
full_path = []
|
||||||
|
|
||||||
|
for index_place in range(len(self.walk) - 1):
|
||||||
|
path_searcher = RotateAndGoAStar(
|
||||||
|
world,
|
||||||
|
self.getPosition(self.walk[index_place], world.dustList),
|
||||||
|
self.getPosition(self.walk[index_place + 1], world.dustList),
|
||||||
|
)
|
||||||
|
path_searcher.search()
|
||||||
|
full_path = full_path + ["DEFAULT_ROTATION"] + path_searcher.actions
|
||||||
|
|
||||||
|
self.real_path = full_path
|
||||||
|
|
||||||
|
|
||||||
|
def generate_random_permutation(n):
|
||||||
|
# Create a list of numbers from 1 to n
|
||||||
|
numbers = list(range(0, n))
|
||||||
|
|
||||||
|
# Shuffle the list using the random.shuffle function
|
||||||
|
random.shuffle(numbers)
|
||||||
|
|
||||||
|
return numbers
|
||||||
|
|
||||||
|
|
||||||
|
# BUG solution: inverse direction at the last step
|
||||||
|
def addStartAndStation(permutation: list[int]):
|
||||||
|
frequency = math.ceil(100 / config.getint("CONSTANT", "BananaFilling"))
|
||||||
|
numer_of_stops = math.ceil(len(permutation) / frequency)
|
||||||
|
walk = permutation.copy()
|
||||||
|
|
||||||
|
for i in range(1, numer_of_stops):
|
||||||
|
walk.insert((frequency + 1) * i - 1, -1)
|
||||||
|
walk.insert(len(walk), -1)
|
||||||
|
walk.insert(0, -2)
|
||||||
|
|
||||||
|
return walk
|
||||||
|
|
||||||
|
|
||||||
|
class GeneticAlgorytm:
|
||||||
|
def __init__(self, world: World):
|
||||||
|
self.world = world
|
||||||
|
self.population_size = config.getint("GENETIC_ALGORITHM", "PopulationSize")
|
||||||
|
self.mutation_probability = config.getfloat(
|
||||||
|
"GENETIC_ALGORITHM", "MutationProbability"
|
||||||
|
)
|
||||||
|
self.iteration_number = config.getint("GENETIC_ALGORITHM", "IterationNumber")
|
||||||
|
self.descendants_number = config.getint(
|
||||||
|
"GENETIC_ALGORITHM", "DescendantsNumber"
|
||||||
|
)
|
||||||
|
self.dusts = world.dustList
|
||||||
|
self.doc_station = world.doc_station
|
||||||
|
self.paths: list[Path] = []
|
||||||
|
self.checked_permutations = {}
|
||||||
|
|
||||||
|
self.best_path = None
|
||||||
|
self.best_distance = math.inf
|
||||||
|
self.best_real_path = []
|
||||||
|
|
||||||
|
def generate_population(self):
|
||||||
|
for i in range(self.population_size):
|
||||||
|
path = Path()
|
||||||
|
path.random_walk(self.dusts)
|
||||||
|
self.checked_permutations[tuple(path.permutation)] = True
|
||||||
|
path.calculate_distance(self.world)
|
||||||
|
self.paths.append(path)
|
||||||
|
|
||||||
|
def print_top(self):
|
||||||
|
print(
|
||||||
|
"Best path: ",
|
||||||
|
self.best_path.walk,
|
||||||
|
"Distance: ",
|
||||||
|
self.best_path.distance,
|
||||||
|
)
|
||||||
|
for path in self.paths[1:]:
|
||||||
|
print(path.walk, path.distance)
|
||||||
|
|
||||||
|
def evaluate_population(self):
|
||||||
|
self.paths.sort(key=lambda x: x.distance, reverse=False)
|
||||||
|
|
||||||
|
self.best_distance = self.paths[0].distance
|
||||||
|
self.best_path = self.paths[0]
|
||||||
|
|
||||||
|
for path in self.paths[self.population_size :]:
|
||||||
|
del self.checked_permutations[tuple(path.permutation)]
|
||||||
|
|
||||||
|
self.paths = self.paths[: self.population_size]
|
||||||
|
|
||||||
|
def create_child(self, parent1: Path, parent2: Path) -> Path:
|
||||||
|
child = Path()
|
||||||
|
|
||||||
|
child.permutation = parent1.permutation[: len(parent1.permutation) // 2]
|
||||||
|
|
||||||
|
# Add missing items from parent2 in the order they appear
|
||||||
|
for item in parent2.permutation:
|
||||||
|
if item not in child.permutation:
|
||||||
|
child.permutation.append(item)
|
||||||
|
|
||||||
|
child.walk = addStartAndStation(child.permutation)
|
||||||
|
|
||||||
|
child.calculate_distance(self.world)
|
||||||
|
|
||||||
|
return child
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.generate_population()
|
||||||
|
|
||||||
|
for i in range(self.iteration_number):
|
||||||
|
self.crossover()
|
||||||
|
|
||||||
|
self.evaluate_population()
|
||||||
|
self.best_real_path = self.paths[0].get_real_path(self.world)
|
||||||
|
|
||||||
|
print(hits, (misses + hits))
|
||||||
|
|
||||||
|
print(hits / (misses + hits))
|
||||||
|
|
||||||
|
def mutate(self, mutant: Path) -> Path:
|
||||||
|
random_number = randint(0, len(mutant.permutation) - 1)
|
||||||
|
random_number2 = random_number
|
||||||
|
while random_number == random_number2:
|
||||||
|
random_number2 = randint(0, len(mutant.permutation) - 1)
|
||||||
|
|
||||||
|
mutant.permutation[random_number], mutant.permutation[random_number2] = (
|
||||||
|
mutant.permutation[random_number2],
|
||||||
|
mutant.permutation[random_number],
|
||||||
|
)
|
||||||
|
|
||||||
|
if tuple(mutant.permutation) in self.checked_permutations:
|
||||||
|
return self.mutate(mutant)
|
||||||
|
|
||||||
|
mutant.walk = addStartAndStation(mutant.permutation)
|
||||||
|
|
||||||
|
mutant.calculate_distance(self.world)
|
||||||
|
return mutant
|
||||||
|
|
||||||
|
def crossover(self):
|
||||||
|
for i in range(self.descendants_number):
|
||||||
|
parent1 = self.paths[random.randint(0, self.population_size - 1)]
|
||||||
|
|
||||||
|
parent2 = self.paths[random.randint(0, self.population_size - 1)]
|
||||||
|
|
||||||
|
child = self.create_child(parent1, parent2)
|
||||||
|
while tuple(child.permutation) in self.checked_permutations:
|
||||||
|
parent1 = self.paths[random.randint(0, self.population_size - 1)]
|
||||||
|
parent2 = self.paths[random.randint(0, self.population_size - 1)]
|
||||||
|
child = self.create_child(parent1, parent2)
|
||||||
|
|
||||||
|
self.checked_permutations[tuple(child.permutation)] = True
|
||||||
|
self.paths.append(child)
|
||||||
|
|
||||||
|
mutant = Path()
|
||||||
|
mutant.permutation = child.permutation.copy()
|
||||||
|
mutant = self.mutate(mutant)
|
||||||
|
self.checked_permutations[tuple(mutant.permutation)] = True
|
||||||
|
self.paths.append(mutant)
|
||||||
|
|
||||||
|
self.evaluate_population()
|
@ -5,18 +5,19 @@ from tensorflow import keras
|
|||||||
import cv2
|
import cv2
|
||||||
import random
|
import random
|
||||||
|
|
||||||
#You can download model from https://uam-my.sharepoint.com/:f:/g/personal/pavbia_st_amu_edu_pl/EmBHjnETuk5LiCZS6xk7AnIBNsnffR3Sygf8EX2bhR1w4A
|
# You can download model from https://uam-my.sharepoint.com/:f:/g/personal/pavbia_st_amu_edu_pl/EmBHjnETuk5LiCZS6xk7AnIBNsnffR3Sygf8EX2bhR1w4A
|
||||||
#Change the path to model + to datasets (string 12 + strings 35,41,47,53)
|
# Change the path to model + to datasets (string 12 + strings 35,41,47,53)
|
||||||
|
|
||||||
|
|
||||||
class VacuumRecognizer:
|
class VacuumRecognizer:
|
||||||
model = keras.models.load_model('AI_brain\model.h5') #Neuron model path
|
model = keras.models.load_model("AI_brain\model.h5") # Neuron model path
|
||||||
|
|
||||||
def recognize(self, image_path) -> str:
|
def recognize(self, image_path) -> str:
|
||||||
class_names = ['Banana', 'Cat', 'Earings', 'Plant']
|
class_names = ["Banana", "Cat", "Earings", "Plant"]
|
||||||
|
|
||||||
img = cv2.imread(image_path, flags=cv2.IMREAD_GRAYSCALE)
|
img = cv2.imread(image_path, flags=cv2.IMREAD_GRAYSCALE)
|
||||||
cv2.waitKey(0)
|
cv2.waitKey(0)
|
||||||
img = (np.expand_dims(img, 0))
|
img = np.expand_dims(img, 0)
|
||||||
|
|
||||||
predictions = self.model.predict(img)[0].tolist()
|
predictions = self.model.predict(img)[0].tolist()
|
||||||
|
|
||||||
@ -31,31 +32,43 @@ class VacuumRecognizer:
|
|||||||
return class_names[predictions.index(max(predictions))]
|
return class_names[predictions.index(max(predictions))]
|
||||||
|
|
||||||
def get_random_dir(self, type) -> str:
|
def get_random_dir(self, type) -> str:
|
||||||
if type == 'Plant':
|
if type == "Plant":
|
||||||
plant_image_paths = 'C:\\Users\\Pavel\\Desktop\\AI\\Machine_learning_2023\\AI_brain\\Image_datasetJPGnewBnW\\check\\Plant' #Plant dataset path
|
plant_image_paths = "C:\\Users\\Pavel\\Desktop\\AI\\Machine_learning_2023\\AI_brain\\Image_datasetJPGnewBnW\\check\\Plant" # Plant dataset path
|
||||||
plant_dirs = os.listdir(plant_image_paths)
|
plant_dirs = os.listdir(plant_image_paths)
|
||||||
full_path = plant_image_paths + '\\' + plant_dirs[random.randint(0, len(plant_dirs)-1)]
|
full_path = (
|
||||||
|
plant_image_paths
|
||||||
|
+ "\\"
|
||||||
|
+ plant_dirs[random.randint(0, len(plant_dirs) - 1)]
|
||||||
|
)
|
||||||
print(full_path)
|
print(full_path)
|
||||||
return full_path
|
return full_path
|
||||||
elif type == 'Earings':
|
elif type == "Earings":
|
||||||
earnings_image_paths = 'C:\\Users\\Pavel\\Desktop\\AI\\Machine_learning_2023\\AI_brain\\Image_datasetJPGnewBnW\\check\\Earings' #Earings dataset path
|
earnings_image_paths = "C:\\Users\\Pavel\\Desktop\\AI\\Machine_learning_2023\\AI_brain\\Image_datasetJPGnewBnW\\check\\Earings" # Earings dataset path
|
||||||
earning_dirs = os.listdir(earnings_image_paths)
|
earning_dirs = os.listdir(earnings_image_paths)
|
||||||
full_path = earnings_image_paths + '\\' + earning_dirs[random.randint(0, len(earning_dirs)-1)]
|
full_path = (
|
||||||
|
earnings_image_paths
|
||||||
|
+ "\\"
|
||||||
|
+ earning_dirs[random.randint(0, len(earning_dirs) - 1)]
|
||||||
|
)
|
||||||
print(full_path)
|
print(full_path)
|
||||||
return full_path
|
return full_path
|
||||||
elif type == 'Banana':
|
elif type == "Banana":
|
||||||
banana_image_paths = 'C:\\Users\\Pavel\\Desktop\\AI\\Machine_learning_2023\\AI_brain\\Image_datasetJPGnewBnW\\check\\Banana' #Banana dataset path
|
banana_image_paths = "C:\\Users\\Pavel\\Desktop\\AI\\Machine_learning_2023\\AI_brain\\Image_datasetJPGnewBnW\\check\\Banana" # Banana dataset path
|
||||||
banana_dirs = os.listdir(banana_image_paths)
|
banana_dirs = os.listdir(banana_image_paths)
|
||||||
full_path = banana_image_paths + '\\' + banana_dirs[random.randint(0, len(banana_dirs)-1)]
|
full_path = (
|
||||||
|
banana_image_paths
|
||||||
|
+ "\\"
|
||||||
|
+ banana_dirs[random.randint(0, len(banana_dirs) - 1)]
|
||||||
|
)
|
||||||
print(full_path)
|
print(full_path)
|
||||||
return full_path
|
return full_path
|
||||||
elif type == 'Cat':
|
elif type == "Cat":
|
||||||
cat_image_paths = 'C:\\Users\\Pavel\\Desktop\\AI\\Machine_learning_2023\\AI_brain\\Image_datasetJPGnewBnW\\check\\Cat' #Cat dataset path
|
cat_image_paths = "C:\\Users\\Pavel\\Desktop\\AI\\Machine_learning_2023\\AI_brain\\Image_datasetJPGnewBnW\\check\\Cat" # Cat dataset path
|
||||||
cat_dir = os.listdir(cat_image_paths)
|
cat_dir = os.listdir(cat_image_paths)
|
||||||
|
|
||||||
|
|
||||||
#For testing the neuron model
|
# For testing the neuron model
|
||||||
'''image_paths = []
|
"""image_paths = []
|
||||||
image_paths.append('C:\\Users\\Pavel\\Desktop\\AI\\Machine_learning_2023\\AI_brain\\Image_datasetJPGnewBnW\\check\\Banana')
|
image_paths.append('C:\\Users\\Pavel\\Desktop\\AI\\Machine_learning_2023\\AI_brain\\Image_datasetJPGnewBnW\\check\\Banana')
|
||||||
image_paths.append('C:\\Users\\Pavel\\Desktop\\AI\\Machine_learning_2023\\AI_brain\\Image_datasetJPGnewBnW\\check\\Cat')
|
image_paths.append('C:\\Users\\Pavel\\Desktop\\AI\\Machine_learning_2023\\AI_brain\\Image_datasetJPGnewBnW\\check\\Cat')
|
||||||
image_paths.append('C:\\Users\\Pavel\\Desktop\\AI\\Machine_learning_2023\\AI_brain\\Image_datasetJPGnewBnW\\check\\Earings')
|
image_paths.append('C:\\Users\\Pavel\\Desktop\\AI\\Machine_learning_2023\\AI_brain\\Image_datasetJPGnewBnW\\check\\Earings')
|
||||||
@ -65,4 +78,4 @@ uio = VacuumRecognizer()
|
|||||||
for image_path in image_paths:
|
for image_path in image_paths:
|
||||||
dirs = os.listdir(image_path)
|
dirs = os.listdir(image_path)
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
print(uio.recognize(image_path + '\\' + dirs[random.randint(0, len(dirs)-1)]))'''
|
print(uio.recognize(image_path + '\\' + dirs[random.randint(0, len(dirs)-1)]))"""
|
||||||
|
@ -3,11 +3,10 @@ from domain.world import World
|
|||||||
|
|
||||||
|
|
||||||
class State:
|
class State:
|
||||||
def __init__(self, x, y, direction=(1, 0), entity=None):
|
def __init__(self, x: int, y: int, direction=(1, 0), entity=None):
|
||||||
self.x = x
|
self.x = x
|
||||||
self.y = y
|
self.y = y
|
||||||
self.direction = direction
|
self.direction = direction
|
||||||
|
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash((self.x, self.y))
|
return hash((self.x, self.y))
|
||||||
@ -19,7 +18,7 @@ class State:
|
|||||||
and self.direction == other.direction
|
and self.direction == other.direction
|
||||||
)
|
)
|
||||||
|
|
||||||
def heuristic(self, goal_state):
|
def heuristic(self, goal_state) -> int:
|
||||||
return abs(self.x - goal_state.x) + abs(self.y - goal_state.y)
|
return abs(self.x - goal_state.x) + abs(self.y - goal_state.y)
|
||||||
|
|
||||||
|
|
||||||
@ -53,19 +52,19 @@ class RotateAndGoAStar:
|
|||||||
self.enqueued_states = set()
|
self.enqueued_states = set()
|
||||||
self.explored = set()
|
self.explored = set()
|
||||||
self.actions = []
|
self.actions = []
|
||||||
|
self.cost = 0
|
||||||
|
|
||||||
def get_g_score(self, state):
|
def get_g_score(self, state) -> int:
|
||||||
return self.world.get_cost(state.x, state.y)
|
return self.world.get_cost(state.x, state.y)
|
||||||
|
|
||||||
def search(self):
|
def search(self):
|
||||||
heapq.heappush(
|
heapq.heappush(self.fringe, Node(self.start_state, 0, self.goal_state))
|
||||||
self.fringe, Node(self.start_state, 0, self.goal_state)
|
|
||||||
)
|
|
||||||
|
|
||||||
while self.fringe:
|
while self.fringe:
|
||||||
elem = heapq.heappop(self.fringe)
|
elem: Node = heapq.heappop(self.fringe)
|
||||||
if self.is_goal(elem.state):
|
if self.is_goal(elem.state):
|
||||||
self.actions = action_sequence(elem)
|
self.actions = action_sequence(elem)
|
||||||
|
self.cost = elem.g_score
|
||||||
return True
|
return True
|
||||||
self.explored.add(elem.state)
|
self.explored.add(elem.state)
|
||||||
|
|
||||||
@ -73,7 +72,7 @@ class RotateAndGoAStar:
|
|||||||
if state in self.explored:
|
if state in self.explored:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
new_g_score = new_g_score = elem.g_score + self.world.get_cost(state.x, state.y)
|
new_g_score = elem.g_score + self.world.get_cost(state.x, state.y)
|
||||||
if state not in self.enqueued_states:
|
if state not in self.enqueued_states:
|
||||||
next_node = Node(state, new_g_score, self.goal_state)
|
next_node = Node(state, new_g_score, self.goal_state)
|
||||||
next_node.action = action
|
next_node.action = action
|
||||||
@ -84,12 +83,12 @@ class RotateAndGoAStar:
|
|||||||
for node in self.fringe:
|
for node in self.fringe:
|
||||||
if node.state == state:
|
if node.state == state:
|
||||||
node.g_score = new_g_score
|
node.g_score = new_g_score
|
||||||
node.f_score = (
|
node.f_score = new_g_score + node.state.heuristic(
|
||||||
new_g_score + node.state.heuristic(self.goal_state)
|
self.goal_state
|
||||||
)
|
)
|
||||||
node.parent = elem
|
node.parent = elem
|
||||||
node.action = action
|
node.action = action
|
||||||
heapq.heapify(self.fringe)
|
heapq.heapify(self.fringe)
|
||||||
break
|
break
|
||||||
|
|
||||||
return False
|
return False
|
||||||
@ -102,12 +101,12 @@ class RotateAndGoAStar:
|
|||||||
next_x = state.x + state.direction[0]
|
next_x = state.x + state.direction[0]
|
||||||
next_y = state.y + state.direction[1]
|
next_y = state.y + state.direction[1]
|
||||||
if self.world.accepted_move(next_x, next_y):
|
if self.world.accepted_move(next_x, next_y):
|
||||||
new_successors.append(
|
new_successors.append(("GO", State(next_x, next_y, state.direction)))
|
||||||
("GO", State(next_x, next_y, state.direction))
|
|
||||||
)
|
|
||||||
return new_successors
|
return new_successors
|
||||||
|
|
||||||
def is_goal(self, state: State) -> bool:
|
def is_goal(self, state: State) -> bool:
|
||||||
return (
|
return state.x == self.goal_state.x and state.y == self.goal_state.y
|
||||||
state.x == self.goal_state.x
|
|
||||||
and state.y == self.goal_state.y )
|
def number_of_moves_forward(self) -> int:
|
||||||
|
go_count = self.actions.count("GO")
|
||||||
|
return go_count
|
||||||
|
20
config.ini
20
config.ini
@ -4,9 +4,23 @@ movement = robot
|
|||||||
#accept: human, robot
|
#accept: human, robot
|
||||||
|
|
||||||
[CONSTANT]
|
[CONSTANT]
|
||||||
NumberOfBananas = 10
|
NumberOfBananas = 15
|
||||||
NumberOfEarrings = 3
|
NumberOfEarrings = 0
|
||||||
NumberOfPlants = 5
|
NumberOfPlants = 5
|
||||||
|
BananaFilling = 25
|
||||||
|
RobotStartPosition = 5, 5
|
||||||
|
DockStationStartPosition = 5, 6
|
||||||
|
#9,8
|
||||||
|
|
||||||
[NEURAL_NETWORK]
|
[NEURAL_NETWORK]
|
||||||
is_nural_network_off = True
|
is_neural_network_off = True
|
||||||
|
|
||||||
|
[AI_BRAIN]
|
||||||
|
mode = full_clean
|
||||||
|
#accept: full_clean, to_station
|
||||||
|
|
||||||
|
[GENETIC_ALGORITHM]
|
||||||
|
PopulationSize = 20
|
||||||
|
DescendantsNumber = 6
|
||||||
|
MutationProbability = 0.3
|
||||||
|
IterationNumber = 1_000
|
||||||
|
@ -21,12 +21,13 @@ class VacuumMoveCommand(Command):
|
|||||||
if not self.world.accepted_move(end_x, end_y):
|
if not self.world.accepted_move(end_x, end_y):
|
||||||
return
|
return
|
||||||
|
|
||||||
tmp = self.world.is_garbage_at(end_x, end_y)
|
garbage = self.world.garbage_at(end_x, end_y)
|
||||||
if len(tmp) > 0:
|
if len(garbage) > 0:
|
||||||
for t in tmp:
|
for item in garbage:
|
||||||
if self.vacuum.get_container_filling() < 100:
|
if self.vacuum.get_container_filling() < 100:
|
||||||
self.vacuum.increase_container_filling()
|
self.vacuum.increase_container_filling()
|
||||||
self.world.dust[end_x][end_y].remove(t)
|
self.world.delete_entities_at_Of_type(item.x, item.y, item.type)
|
||||||
|
self.world.dust[end_x][end_y].remove(item)
|
||||||
|
|
||||||
if self.world.is_docking_station_at(end_x, end_y):
|
if self.world.is_docking_station_at(end_x, end_y):
|
||||||
self.vacuum.dump_trash()
|
self.vacuum.dump_trash()
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
from domain.entities.entity import Entity
|
from domain.entities.entity import Entity
|
||||||
from domain.world import World
|
from domain.world import World
|
||||||
|
import configparser
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read("config.ini")
|
||||||
|
|
||||||
|
|
||||||
class Vacuum(Entity):
|
class Vacuum(Entity):
|
||||||
@ -11,7 +15,7 @@ class Vacuum(Entity):
|
|||||||
self.container_filling = 0
|
self.container_filling = 0
|
||||||
|
|
||||||
def increase_container_filling(self) -> None:
|
def increase_container_filling(self) -> None:
|
||||||
self.container_filling += 5
|
self.container_filling += config.getint("CONSTANT", "BananaFilling")
|
||||||
|
|
||||||
def dump_trash(self) -> None:
|
def dump_trash(self) -> None:
|
||||||
self.container_filling = 0
|
self.container_filling = 0
|
||||||
|
@ -8,7 +8,9 @@ class World:
|
|||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
self.dust = [[[] for j in range(height)] for i in range(width)]
|
self.dust = [[[] for j in range(height)] for i in range(width)]
|
||||||
|
self.dustList: list[Entity] = []
|
||||||
self.obstacles = [[[] for j in range(height)] for i in range(width)]
|
self.obstacles = [[[] for j in range(height)] for i in range(width)]
|
||||||
|
self.entity = [[[] for j in range(height)] for i in range(width)]
|
||||||
|
|
||||||
self.vacuum = None
|
self.vacuum = None
|
||||||
self.cat = None
|
self.cat = None
|
||||||
@ -19,8 +21,10 @@ class World:
|
|||||||
self.doc_station = entity
|
self.doc_station = entity
|
||||||
elif entity.type == "PEEL":
|
elif entity.type == "PEEL":
|
||||||
self.dust[entity.x][entity.y].append(entity)
|
self.dust[entity.x][entity.y].append(entity)
|
||||||
|
self.dustList.append(Entity(entity.x, entity.y, "PEEL"))
|
||||||
elif entity.type == "EARRING":
|
elif entity.type == "EARRING":
|
||||||
self.dust[entity.x][entity.y].append(entity)
|
self.dust[entity.x][entity.y].append(entity)
|
||||||
|
self.dustList.append(Entity(entity.x, entity.y, "EARRING"))
|
||||||
elif entity.type == "VACUUM":
|
elif entity.type == "VACUUM":
|
||||||
self.vacuum = entity
|
self.vacuum = entity
|
||||||
elif entity.type == "CAT":
|
elif entity.type == "CAT":
|
||||||
@ -29,10 +33,27 @@ class World:
|
|||||||
else:
|
else:
|
||||||
self.obstacles[entity.x][entity.y].append(entity)
|
self.obstacles[entity.x][entity.y].append(entity)
|
||||||
|
|
||||||
|
self.entity[entity.x][entity.y].append(entity)
|
||||||
|
|
||||||
|
def is_entity_at(
|
||||||
|
self,
|
||||||
|
x: int,
|
||||||
|
y: int,
|
||||||
|
) -> bool:
|
||||||
|
if len(self.entity[x][y]) > 0:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def delete_entities_at_Of_type(self, x: int, y: int, type: str):
|
||||||
|
entities = self.entity[x][y]
|
||||||
|
for entity in entities:
|
||||||
|
if entity.type == type:
|
||||||
|
entities.remove(entity)
|
||||||
|
|
||||||
def is_obstacle_at(self, x: int, y: int) -> bool:
|
def is_obstacle_at(self, x: int, y: int) -> bool:
|
||||||
return bool(self.obstacles[x][y])
|
return bool(self.obstacles[x][y])
|
||||||
|
|
||||||
def is_garbage_at(self, x: int, y: int):
|
def garbage_at(self, x: int, y: int) -> list[Entity]:
|
||||||
if len(self.dust[x][y]) == 0:
|
if len(self.dust[x][y]) == 0:
|
||||||
return []
|
return []
|
||||||
return [i for i in self.dust[x][y] if evaluate([i.properties])[0] == 1]
|
return [i for i in self.dust[x][y] if evaluate([i.properties])[0] == 1]
|
||||||
@ -54,5 +75,5 @@ class World:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_cost(self, x, y):
|
def get_cost(self, x, y) -> float:
|
||||||
return self.costs[x][y]
|
return self.costs[x][y]
|
||||||
|
113
main.py
113
main.py
@ -3,6 +3,9 @@ from random import randint
|
|||||||
import pygame
|
import pygame
|
||||||
import configparser
|
import configparser
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read("config.ini")
|
||||||
|
|
||||||
from domain.commands.random_cat_move_command import RandomCatMoveCommand
|
from domain.commands.random_cat_move_command import RandomCatMoveCommand
|
||||||
from domain.commands.vacuum_move_command import VacuumMoveCommand
|
from domain.commands.vacuum_move_command import VacuumMoveCommand
|
||||||
from domain.entities.cat import Cat
|
from domain.entities.cat import Cat
|
||||||
@ -13,17 +16,16 @@ from domain.entities.earring import Earring
|
|||||||
from domain.entities.docking_station import Doc_Station
|
from domain.entities.docking_station import Doc_Station
|
||||||
from domain.world import World
|
from domain.world import World
|
||||||
from view.renderer import Renderer
|
from view.renderer import Renderer
|
||||||
from AI_brain.image_recognition import VacuumRecognizer
|
from AI_brain.genetic_algorytm import GeneticAlgorytm, Path
|
||||||
|
|
||||||
|
if not config.getboolean("NEURAL_NETWORK", "is_neural_network_off"):
|
||||||
|
from AI_brain.image_recognition import VacuumRecognizer
|
||||||
|
|
||||||
# from AI_brain.movement import GoAnyDirectionBFS, State
|
# from AI_brain.movement import GoAnyDirectionBFS, State
|
||||||
# from AI_brain.rotate_and_go_bfs import RotateAndGoBFS, State
|
# from AI_brain.rotate_and_go_bfs import RotateAndGoBFS, State
|
||||||
from AI_brain.rotate_and_go_aStar import RotateAndGoAStar, State
|
from AI_brain.rotate_and_go_aStar import RotateAndGoAStar, State
|
||||||
|
|
||||||
|
|
||||||
config = configparser.ConfigParser()
|
|
||||||
config.read("config.ini")
|
|
||||||
|
|
||||||
|
|
||||||
class Main:
|
class Main:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
tiles_x = 10
|
tiles_x = 10
|
||||||
@ -51,24 +53,38 @@ class Main:
|
|||||||
def run_robot(self):
|
def run_robot(self):
|
||||||
self.renderer.render(self.world)
|
self.renderer.render(self.world)
|
||||||
|
|
||||||
start_state = State(self.world.vacuum.x, self.world.vacuum.y)
|
if config["AI_BRAIN"]["mode"] == "to_station":
|
||||||
end_state = State(self.world.doc_station.x, self.world.doc_station.y)
|
start_state = State(self.world.vacuum.x, self.world.vacuum.y)
|
||||||
|
end_state = State(self.world.doc_station.x, self.world.doc_station.y)
|
||||||
|
|
||||||
# path_searcher = GoAnyDirectionBFS(self.world, start_state, end_state)
|
path_searcher = RotateAndGoAStar(self.world, start_state, end_state)
|
||||||
# path_searcher = RotateAndGoBFS(self.world, start_state, end_state)
|
|
||||||
path_searcher = RotateAndGoAStar(self.world, start_state, end_state)
|
if not path_searcher.search():
|
||||||
if not path_searcher.search():
|
print("No solution")
|
||||||
print("No solution")
|
exit(0)
|
||||||
|
print(path_searcher.actions)
|
||||||
|
print(path_searcher.cost)
|
||||||
|
robot_actions = path_searcher.actions
|
||||||
|
|
||||||
|
elif config["AI_BRAIN"]["mode"] == "full_clean":
|
||||||
|
genetic_searcher = GeneticAlgorytm(self.world)
|
||||||
|
genetic_searcher.run()
|
||||||
|
|
||||||
|
genetic_searcher.print_top()
|
||||||
|
|
||||||
|
robot_actions = genetic_searcher.best_path.real_path
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("Wrong mode")
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
path_searcher.actions.reverse()
|
|
||||||
while self.running:
|
while self.running:
|
||||||
for event in pygame.event.get():
|
for event in pygame.event.get():
|
||||||
if event.type == pygame.QUIT:
|
if event.type == pygame.QUIT:
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
||||||
if len(path_searcher.actions) > 0:
|
if len(robot_actions) > 0:
|
||||||
action_direction = path_searcher.actions.pop()
|
action_direction = robot_actions.pop(0)
|
||||||
# self.handle_action1(action_direction)
|
# self.handle_action1(action_direction)
|
||||||
self.handle_action2(action_direction)
|
self.handle_action2(action_direction)
|
||||||
|
|
||||||
@ -113,6 +129,8 @@ class Main:
|
|||||||
self.world.vacuum.direction[1],
|
self.world.vacuum.direction[1],
|
||||||
-self.world.vacuum.direction[0],
|
-self.world.vacuum.direction[0],
|
||||||
)
|
)
|
||||||
|
elif action == "DEFAULT_ROTATION":
|
||||||
|
self.world.vacuum.direction = (1, 0)
|
||||||
|
|
||||||
def process_input(self):
|
def process_input(self):
|
||||||
for event in pygame.event.get():
|
for event in pygame.event.get():
|
||||||
@ -145,35 +163,59 @@ class Main:
|
|||||||
|
|
||||||
|
|
||||||
def generate_world(tiles_x: int, tiles_y: int) -> World:
|
def generate_world(tiles_x: int, tiles_y: int) -> World:
|
||||||
if config.getboolean("NEURAL_NETWORK", "is_nural_network_off"):
|
if config.getboolean("NEURAL_NETWORK", "is_neural_network_off"):
|
||||||
world = World(tiles_x, tiles_y)
|
world = World(tiles_x, tiles_y)
|
||||||
for _ in range(config.getint("CONSTANT", "NumberOfBananas")):
|
|
||||||
temp_x = randint(0, tiles_x - 1)
|
x, y = config.get("CONSTANT", "RobotStartPosition").split(",")
|
||||||
temp_y = randint(0, tiles_y - 1)
|
x, y = int(x), int(y)
|
||||||
world.add_entity(Garbage(temp_x, temp_y))
|
world.vacuum = Vacuum(x, y)
|
||||||
world.vacuum = Vacuum(1, 1)
|
|
||||||
world.doc_station = Doc_Station(9, 8)
|
x, y = config.get("CONSTANT", "DockStationStartPosition").split(",")
|
||||||
|
x, y = int(x), int(y)
|
||||||
|
world.doc_station = Doc_Station(x, y)
|
||||||
if config.getboolean("APP", "cat"):
|
if config.getboolean("APP", "cat"):
|
||||||
world.cat = Cat(7, 8)
|
world.cat = Cat(7, 8)
|
||||||
world.add_entity(world.cat)
|
world.add_entity(world.cat)
|
||||||
|
|
||||||
|
world.add_entity(world.doc_station)
|
||||||
|
world.add_entity(world.vacuum)
|
||||||
world.add_entity(Entity(2, 8, "PLANT1"))
|
world.add_entity(Entity(2, 8, "PLANT1"))
|
||||||
world.add_entity(Entity(4, 1, "PLANT1"))
|
world.add_entity(Entity(4, 1, "PLANT1"))
|
||||||
world.add_entity(Entity(3, 4, "PLANT2"))
|
world.add_entity(Entity(3, 4, "PLANT2"))
|
||||||
world.add_entity(Entity(8, 8, "PLANT2"))
|
world.add_entity(Entity(8, 8, "PLANT2"))
|
||||||
world.add_entity(Entity(9, 3, "PLANT3"))
|
world.add_entity(Entity(9, 3, "PLANT3"))
|
||||||
world.add_entity(Earring(9, 7))
|
|
||||||
world.add_entity(Earring(5, 5))
|
numberOfEarrings = config.getint("CONSTANT", "NumberOfEarrings")
|
||||||
world.add_entity(Earring(4, 6))
|
for _ in range(numberOfEarrings):
|
||||||
|
temp_x = randint(0, tiles_x - 1)
|
||||||
|
temp_y = randint(0, tiles_y - 1)
|
||||||
|
|
||||||
|
while world.is_entity_at(temp_x, temp_y):
|
||||||
|
temp_x = randint(0, tiles_x - 1)
|
||||||
|
temp_y = randint(0, tiles_y - 1)
|
||||||
|
|
||||||
|
world.add_entity(Earring(temp_x, temp_y))
|
||||||
|
|
||||||
|
for _ in range(config.getint("CONSTANT", "NumberOfBananas")):
|
||||||
|
temp_x = randint(0, tiles_x - 1)
|
||||||
|
temp_y = randint(0, tiles_y - 1)
|
||||||
|
|
||||||
|
while world.is_entity_at(temp_x, temp_y):
|
||||||
|
temp_x = randint(0, tiles_x - 1)
|
||||||
|
temp_y = randint(0, tiles_y - 1)
|
||||||
|
|
||||||
|
world.add_entity(Garbage(temp_x, temp_y))
|
||||||
else:
|
else:
|
||||||
def world_adder(x,y,object,style=None):
|
|
||||||
|
def world_adder(x, y, object, style=None):
|
||||||
print(object)
|
print(object)
|
||||||
if object == 'Plant':
|
if object == "Plant":
|
||||||
world.add_entity(Entity(x, y, f"PLANT{randint(1, 3)}"))
|
world.add_entity(Entity(x, y, f"PLANT{randint(1, 3)}"))
|
||||||
if object == 'Earings':
|
if object == "Earings":
|
||||||
world.add_entity(Earring(x, y))
|
world.add_entity(Earring(x, y))
|
||||||
if object == 'Banana':
|
if object == "Banana":
|
||||||
world.add_entity(Garbage(temp_x, temp_y))
|
world.add_entity(Garbage(temp_x, temp_y))
|
||||||
if object == 'Cat' and config.getboolean("APP", "cat"):
|
if object == "Cat" and config.getboolean("APP", "cat"):
|
||||||
world.add_entity(Cat(x, y))
|
world.add_entity(Cat(x, y))
|
||||||
|
|
||||||
neural_network = VacuumRecognizer()
|
neural_network = VacuumRecognizer()
|
||||||
@ -188,28 +230,27 @@ def generate_world(tiles_x: int, tiles_y: int) -> World:
|
|||||||
for _ in range(config.getint("CONSTANT", "NumberOfPlants")):
|
for _ in range(config.getint("CONSTANT", "NumberOfPlants")):
|
||||||
temp_x = randint(0, tiles_x - 1)
|
temp_x = randint(0, tiles_x - 1)
|
||||||
temp_y = randint(0, tiles_y - 1)
|
temp_y = randint(0, tiles_y - 1)
|
||||||
path = VacuumRecognizer.get_random_dir(neural_network,'Plant')
|
path = VacuumRecognizer.get_random_dir(neural_network, "Plant")
|
||||||
world_adder(temp_x, temp_y, neural_network.recognize(path))
|
world_adder(temp_x, temp_y, neural_network.recognize(path))
|
||||||
|
|
||||||
for _ in range(config.getint("CONSTANT", "NumberOfEarrings")):
|
for _ in range(config.getint("CONSTANT", "NumberOfEarrings")):
|
||||||
temp_x = randint(0, tiles_x - 1)
|
temp_x = randint(0, tiles_x - 1)
|
||||||
temp_y = randint(0, tiles_y - 1)
|
temp_y = randint(0, tiles_y - 1)
|
||||||
path = VacuumRecognizer.get_random_dir(neural_network,'Earings')
|
path = VacuumRecognizer.get_random_dir(neural_network, "Earings")
|
||||||
world_adder(temp_x, temp_y, neural_network.recognize(path))
|
world_adder(temp_x, temp_y, neural_network.recognize(path))
|
||||||
|
|
||||||
for _ in range(config.getint("CONSTANT", "NumberOfBananas")):
|
for _ in range(config.getint("CONSTANT", "NumberOfBananas")):
|
||||||
temp_x = randint(0, tiles_x - 1)
|
temp_x = randint(0, tiles_x - 1)
|
||||||
temp_y = randint(0, tiles_y - 1)
|
temp_y = randint(0, tiles_y - 1)
|
||||||
path = VacuumRecognizer.get_random_dir(neural_network,'Banana')
|
path = VacuumRecognizer.get_random_dir(neural_network, "Banana")
|
||||||
world_adder(temp_x, temp_y, neural_network.recognize(path))
|
world_adder(temp_x, temp_y, neural_network.recognize(path))
|
||||||
|
|
||||||
|
|
||||||
for x in range(world.width):
|
for x in range(world.width):
|
||||||
for y in range(world.height):
|
for y in range(world.height):
|
||||||
if world.is_garbage_at(x, y):
|
if world.garbage_at(x, y):
|
||||||
world.costs[x][y] = 1
|
world.costs[x][y] = 1
|
||||||
else:
|
else:
|
||||||
world.costs[x][y] = 10
|
world.costs[x][y] = 2
|
||||||
return world
|
return world
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ class Renderer:
|
|||||||
self.tile_height + self.tile_height / 4,
|
self.tile_height + self.tile_height / 4,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
"EARRING": pygame.transform.scale(
|
"EARRING": pygame.transform.scale(
|
||||||
pygame.image.load("media/sprites/earrings.webp"),
|
pygame.image.load("media/sprites/earrings.webp"),
|
||||||
(
|
(
|
||||||
self.tile_width + self.tile_width / 4,
|
self.tile_width + self.tile_width / 4,
|
||||||
@ -115,16 +115,8 @@ class Renderer:
|
|||||||
self.render_board()
|
self.render_board()
|
||||||
for x in range(world.width):
|
for x in range(world.width):
|
||||||
for y in range(world.height):
|
for y in range(world.height):
|
||||||
for entity in world.dust[x][y]:
|
for entity in world.entity[x][y]:
|
||||||
self.draw_entity(entity)
|
self.draw_entity(entity)
|
||||||
for x in range(world.width):
|
|
||||||
for y in range(world.height):
|
|
||||||
for entity in world.obstacles[x][y]:
|
|
||||||
self.draw_entity(entity)
|
|
||||||
self.draw_entity(world.vacuum)
|
|
||||||
self.draw_entity(world.doc_station)
|
|
||||||
if config.getboolean("APP", "cat"):
|
|
||||||
self.draw_entity(world.cat)
|
|
||||||
pygame.display.update()
|
pygame.display.update()
|
||||||
|
|
||||||
def line(self, x_1, y_1, x_2, y_2, color=None):
|
def line(self, x_1, y_1, x_2, y_2, color=None):
|
||||||
|
Loading…
Reference in New Issue
Block a user