integracja projektu genetycznego generatora stolików

This commit is contained in:
s444417 2020-06-09 23:21:04 +02:00
parent 0bfeeb2486
commit f5e403b331
24 changed files with 807 additions and 154 deletions

View File

@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.8 (ProjektAI) (2)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -3,5 +3,5 @@
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6 (ProjektAI)" project-jdk-type="Python SDK" />
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8 (ProjektAI) (2)" project-jdk-type="Python SDK" />
</project>

View File

@ -0,0 +1,104 @@
class CrossingOverMethod:
FixedQuadrant = 0
SingleHorizontalDiv = 1
SingleVerticalDiv = 2
DoubleHorizontalDiv = 3
DoubleVerticalDiv = 4
RandomChoice = 5
class SelectionMethod:
Roulette = 0
Tournament = 1
class MutationMethod:
Flip = 0
Swap = 1
class GADefaults:
def __init__(self):
self.defSelectionMethods = [("ruletka", SelectionMethod.Roulette),
("turniej", SelectionMethod.Tournament)]
self.defCrossingOverMethods = [("pojedynczy poziomy", CrossingOverMethod.SingleHorizontalDiv),
("pojedynczy pionowy", CrossingOverMethod.SingleVerticalDiv),
("podwójny poziomy", CrossingOverMethod.DoubleHorizontalDiv),
("podwójny pionowy", CrossingOverMethod.DoubleVerticalDiv),
("ćwiartki", CrossingOverMethod.FixedQuadrant),
("losowe", CrossingOverMethod.RandomChoice)]
self.defMutationMethods = [("inwersja", MutationMethod.Flip),
("wymiana", MutationMethod.Swap)]
self.windowName = "Algorytm genetyczny - parametry"
self.windowGeometry = "400x400"
self.minTablesCount = 1
self.maxTablesCount = 100
self.defTablesCount = 20
self.runTablesCount = self.defTablesCount
self.sliderNameTablesCount = "stoliki"
self.minPopulationSize = 4
self.maxPopulationSize = 30
self.defPopulationSize = 5
self.runPopulationSize = self.defPopulationSize
self.sliderNamePopulationSize = "populacja"
self.minMutation = 0
self.maxMutation = 10
self.defMutation = 1
self.runMutation = self.defMutation
self.sliderNameMutation = "mutacje"
self.minGenerationsNumber = 1
self.maxGenerationsNumber = 1000
self.defGenerationsNumber = 20
self.runGenerationsNumber = self.defGenerationsNumber
self.sliderNameGenerationsNumber = "pokolenia"
self.defInfoFold = 5
self.runInfoFold = self.defInfoFold
self.sliderInfoFold = "co ile"
self.defSelectionMethod = SelectionMethod.Tournament
self.runSelectionMethod = self.defSelectionMethod
self.radioSelectionMethodName = "metoda selekcji"
self.defCrossingOverMethod = CrossingOverMethod.SingleHorizontalDiv
self.runCrossingOverMethod = self.defCrossingOverMethod
self.radioCrossingOverMethodName = "metoda krzyżowania"
self.defMutationMethod = MutationMethod.Swap
self.runMutationMethod = self.defMutationMethod
self.radioMutationMethodName = "metoda mutacji"
self.minElitism = 0
self.maxElitism = 50
self.defElitism = 0
self.runElitism = self.defElitism
self.sliderElitismName = "elitarność [%]"
self.buttonStartName = "generuj"
self.buttonDefaultsName = "przywróć"
self.__forbiddenPlaces = None
self.waiterPosition = None
self.kitchenPosition = None
def getForbiddenPlaces(self):
if self.__forbiddenPlaces is None:
self.__forbiddenPlaces = [self.waiterPosition, self.kitchenPosition]
return self.__forbiddenPlaces
def getInfo(self):
return "populacja: " + str(self.runPopulationSize) \
+ ", pokolenia: " + str(self.runGenerationsNumber) \
+ ", stoliki: " + str(self.runTablesCount) \
+ ", mutacje: " + str(self.runMutation)

View File

@ -0,0 +1,82 @@
import tkinter
from tkinter import *
from kelner.gui.GAdialog.GADefaults import CrossingOverMethod
class GADialog:
def __init__(self, defaults):
self.__defaults = defaults
self.window = tkinter.Tk()
self.window.attributes('-topmost', 'true')
self.window.title(defaults.windowName)
self.__sliderTablesCount = self.__getSlider(0, defaults.minTablesCount, defaults.maxTablesCount, defaults.runTablesCount, defaults.sliderNameTablesCount)
self.__sliderPopulationSize = self.__getSlider(1, defaults.minPopulationSize, defaults.maxPopulationSize, defaults.runPopulationSize, defaults.sliderNamePopulationSize)
self.__sliderMutation = self.__getSlider(2, defaults.minMutation, defaults.maxMutation, defaults.runMutation, defaults.sliderNameMutation)
self.__sliderGenerationsNumber = self.__getSlider(3, defaults.minGenerationsNumber, defaults.maxGenerationsNumber, defaults.runGenerationsNumber, defaults.sliderNameGenerationsNumber)
self.__sliderInfoFold = self.__getSlider(4, defaults.minGenerationsNumber, defaults.maxGenerationsNumber, defaults.runInfoFold, defaults.sliderInfoFold)
self.__sliderElitism = self.__getSlider(5, defaults.minElitism, defaults.maxElitism, defaults.runElitism, defaults.sliderElitismName)
self.__radioSelectionMethodValue = IntVar()
self.__radioSelectionMethodValue.set(self.__defaults.runSelectionMethod)
self.__getRadioButton(6, defaults.defSelectionMethods, self.__radioSelectionMethodValue, defaults.radioSelectionMethodName)
self.__radioCrossingOverMethodValue = IntVar()
self.__radioCrossingOverMethodValue.set(self.__defaults.runCrossingOverMethod)
self.__getRadioButton(8, defaults.defCrossingOverMethods, self.__radioCrossingOverMethodValue, defaults.radioCrossingOverMethodName)
self.__radioMutationMethodValue = IntVar()
self.__radioMutationMethodValue.set(self.__defaults.runMutationMethod)
self.__getRadioButton(12, defaults.defMutationMethods, self.__radioMutationMethodValue, defaults.radioMutationMethodName)
self.__buttonDefaults = self.__getButton(15, 0, W, defaults.buttonDefaultsName, self.__setDefaults)
self.__buttonStart = self.__getButton(15, 1, E, defaults.buttonStartName, self.__getAllValues)
self.window.mainloop()
def __getSlider(self, rowNum, minVal, maxVal, runVal, labText):
label = Label(self.window, text = labText)
label.grid(row = rowNum, column = 0, sticky = S + W, padx = 5, pady = 5)
slider = Scale(self.window, variable = IntVar(), from_ = minVal, to = maxVal, orient=HORIZONTAL, length = 200)
slider.grid(row = rowNum, column = 1, sticky = E, padx = 5, pady = 3)
slider.set(runVal)
return slider
def __getButton(self, rowNum, colNum, stickPos, btnText, action):
button = Button(self.window, text = btnText, command = action)
button.grid(row = rowNum, column = colNum, stick = stickPos, columnspan = 2, padx=60, pady=5)
return button
def __getRadioButton(self, rowNum, methods, variable, labText):
label = LabelFrame(self.window, text = labText)
label.grid(row=rowNum, column=0, columnspan=2, padx=5, pady=5, sticky=W)
rowNum += 1
iteration = 0
for text, mode in methods:
radio = Radiobutton(label, text = text, variable = variable, value = mode)
radio.grid(row = rowNum, column = iteration % 2, sticky = W, padx = 5, pady = 3)
rowNum += iteration % 2
iteration += 1
def __setDefaults(self):
self.__sliderTablesCount.set(self.__defaults.defTablesCount)
self.__sliderPopulationSize.set(self.__defaults.defPopulationSize)
self.__sliderMutation.set(self.__defaults.defMutation)
self.__sliderGenerationsNumber.set(self.__defaults.defGenerationsNumber)
self.__sliderInfoFold.set(self.__defaults.defInfoFold)
self.__sliderElitism.set(self.__defaults.defElitism)
self.__radioSelectionMethodValue.set(self.__defaults.defSelectionMethod)
self.__radioCrossingOverMethodValue.set(self.__defaults.defCrossingOverMethod)
self.__radioMutationMethodValue.set(self.__defaults.defMutationMethod)
def __getAllValues(self):
self.__defaults.runTablesCount = self.__sliderTablesCount.get()
self.__defaults.runPopulationSize = self.__sliderPopulationSize.get()
self.__defaults.runMutation = self.__sliderMutation.get()
self.__defaults.runGenerationsNumber = self.__sliderGenerationsNumber.get()
self.__defaults.runInfoFold = self.__sliderInfoFold.get()
self.__defaults.runElitism = self.__sliderElitism.get()
self.__defaults.runSelectionMethod = self.__radioSelectionMethodValue.get()
self.__defaults.runCrossingOverMethod = self.__radioCrossingOverMethodValue.get()
self.__defaults.runMutationMethod = self.__radioMutationMethodValue.get()
self.window.destroy()

22
kelner/gui/chart/Plots.py Normal file
View File

@ -0,0 +1,22 @@
import matplotlib.pyplot as plt
class Plots:
def __init__(self, generationNumber, bestFitnesses, bestTabless, worstFitnesses, worstTables, title):
self.__generationsNumber = generationNumber
self.__bestFitnesses = bestFitnesses
self.__bestTables = bestTabless
self.__worstTables = worstTables
self.__worstFitnesses = worstFitnesses
self.__title = title
def draw(self):
generations = [i for i in range(self.__generationsNumber + 1)]
plt.figure(num = self.__title)
plt.plot(generations, self.__bestTables, label = "stoliki najlepszego")
plt.plot(generations, self.__bestFitnesses, label="fitness najlepszego")
plt.plot(generations, self.__worstTables, label = "stoliki najgorszego")
plt.plot(generations, self.__worstFitnesses, label = "fitness najgorszego")
plt.legend()
plt.show()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 850 B

After

Width:  |  Height:  |  Size: 820 B

View File

@ -1,25 +1,24 @@
import pygame
from kelner.src.components.GridBoard import GridBoard
from kelner.src.components.Waiter import Waiter
from kelner.src.components.Table import Table
from kelner.src.components.Kitchen import Kitchen
from kelner.src.managers.DrawableCollection import DrawableCollection
# create screen consts
from kelner.src.managers.MenuManager import MenuManager
from kelner.src.managers.TableManager import TableManager
from kelner.src.managers.WaiterManager import WaiterManager
from kelner.src.managers.TableGenerator import TableGenerator
from kelner.src.algorithms.DecisionTree import Tree_Builder
from kelner.src.managers.KitchenManager import KitchenManager
from kelner.src.algorithms.CNN.PrepareData import LoadModelThread
import kelner.src.settings as settings
import time
# create screen consts
Scale = 2 # scale for all images used within project
CellSize = round(50 * Scale) # pixel size of 1 square cell in the grid
PaintOffset = CellSize # pixel size of paint offset for all drawables
GridCountX = 15 # number of columns in grid
GridCountY = 9 # number of rows in grid
ScreenWidth = CellSize * GridCountX + 2 * PaintOffset # screen width in pixels
Scale = 1.5 # scale for all images used within project
CellSize = round(50 * Scale) # pixel size of 1 square cell in the grid
PaintOffset = CellSize # pixel size of paint offset for all drawables
GridCountX = 15 # number of columns in grid
GridCountY = 9 # number of rows in grid
ScreenWidth = CellSize * GridCountX + 2 * PaintOffset # screen width in pixels
ScreenHeight = CellSize * GridCountY + 2 * PaintOffset # screen height in pixels
running_tasks = {'table': [], 'waiter': []}
@ -34,8 +33,10 @@ load_model_thread.start()
# joining this thread to main thread. Man thread will be started after this finish
load_model_thread.join()
kitchenManager = KitchenManager(gridBoard)
# initialize drawable objects manager
drawableManager = DrawableCollection()
drawableManager = DrawableCollection(kitchenManager)
# initialize menu manager
menuManager = MenuManager()
@ -75,48 +76,16 @@ else:
print("test4: fail")
# initialize waiter component
waiter1 = Waiter(0, 0, 0, GridCountX - 1, 0, GridCountY - 1, CellSize, PaintOffset)
# waiter2 = Waiter(0, GridCountY - 1, 0, GridCountX - 1, 0, GridCountY - 1, CellSize, PaintOffset)
# waiter3 = Waiter(GridCountX - 1, 0, 0, GridCountX - 1, 0, GridCountY - 1, CellSize, PaintOffset)
# waiter4 = Waiter(GridCountX - 1, GridCountY - 1, 0, GridCountX - 1, 0, GridCountY - 1, CellSize, PaintOffset)
waiter1 = Waiter(7, 4, 0, GridCountX - 1, 0, GridCountY - 1, CellSize, PaintOffset)
# adds waiter to drawable collection
drawableManager.add(waiter1)
# drawableManager.add(waiter2)
# drawableManager.add(waiter3)
# drawableManager.add(waiter4)
kitchen = Kitchen(5, GridCountX - 5, 5, GridCountY - 5, CellSize, PaintOffset)
kitchen = Kitchen(14, 0, 5, GridCountX - 5, 5, GridCountY - 5, CellSize, PaintOffset)
drawableManager.add(kitchen)
kitchenManager = KitchenManager(drawableManager, gridBoard)
# My comment
# initialize a number of tables given in range
for i in range(0, 40):
table = Table(1, GridCountX - 2, 1, GridCountY - 2, CellSize, PaintOffset)
if drawableManager.generatePosition(table):
drawableManager.add(table)
# new thread controlling tables
tableTask = TableManager(drawableManager, menuManager)
tableTask.start()
running_tasks['table'].append(tableTask)
# new thread controlling waiter
waiter1Task = WaiterManager(drawableManager, [waiter1], kitchenManager, menuManager, tableTask)
waiter1Task.start()
running_tasks['waiter'].append(tableTask)
# waiter2Task = WaiterManager(drawableManager, [waiter2])
# waiter2Task.start()
#
# waiter3Task = WaiterManager(drawableManager, [waiter3])
# waiter3Task.start()
#
# waiter4Task = WaiterManager(drawableManager, [waiter4])
# waiter4Task.start()
tableGenerator = TableGenerator(GridCountX, GridCountY, 1, GridCountX - 2, 1, GridCountY - 2, CellSize, PaintOffset, drawableManager)
tableGenerator.start()
# main loop
running = True
@ -124,56 +93,12 @@ while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
tableTask.stop()
waiter1Task.stop()
# waiter2Task.stop()
# waiter3Task.stop()
# waiter4Task.stop()
tableGenerator.stop()
drawableManager.stop()
running = False
# handles keyboard events
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
# checks if new waiter's position to the left is not occupied by other object
if drawableManager.isPositionAvailable(waiter1.getX() - 1, waiter1.getY()):
waiter1.moveLeft()
if event.key == pygame.K_RIGHT:
# checks if new waiter's position to the right is not occupied by other object
if drawableManager.isPositionAvailable(waiter1.getX() + 1, waiter1.getY()):
waiter1.moveRight()
if event.key == pygame.K_UP:
# checks if new waiter's position up is not occupied by other object
if drawableManager.isPositionAvailable(waiter1.getX(), waiter1.getY() - 1):
waiter1.moveUp()
if event.key == pygame.K_DOWN:
# checks if new waiter's position down is not occupied by other object
if drawableManager.isPositionAvailable(waiter1.getX(), waiter1.getY() + 1):
waiter1.moveDown()
if event.key == pygame.K_w:
# checks if new waiter's position to the left is not occupied by other object
if drawableManager.isPositionAvailable(waiter1.getX() - 1, waiter1.getY() - 1):
waiter1.moveLeft()
waiter1.moveUp()
if event.key == pygame.K_d:
# checks if new waiter's position to the right is not occupied by other object
if drawableManager.isPositionAvailable(waiter1.getX() + 1, waiter1.getY() - 1):
waiter1.moveRight()
waiter1.moveUp()
if event.key == pygame.K_s:
# checks if new waiter's position up is not occupied by other object
if drawableManager.isPositionAvailable(waiter1.getX() + 1, waiter1.getY() + 1):
waiter1.moveDown()
waiter1.moveRight()
if event.key == pygame.K_a:
# checks if new waiter's position down is not occupied by other object
if drawableManager.isPositionAvailable(waiter1.getX() - 1, waiter1.getY() + 1):
waiter1.moveDown()
waiter1.moveLeft()
drawableManager.forceRepaint()
# repaints all objects to the screen
# is set only on initial paint or after keyboard event or call to forceRepaint()
# is set only on initial paint or call to forceRepaint()
if drawableManager.mustRepaint():
if not kitchenManager.is_running():
gridBoard.reinitialize()

View File

@ -8,9 +8,9 @@ from tensorflow.keras.models import load_model
import kelner.src.settings as settings
# currently all images are not stored in repo because of big weight (5 GB)
data_dir = 'D:\\Nauka\\Studia\\4_sem\\SztucznaInteligencja\\A_star\\CNN\\foodRecognitionCNN\\food-101\\images'
folder_dir = 'D:\\Nauka\\Studia\\4_sem\\SztucznaInteligencja\\A_star\\CNN\\foodRecognitionCNN\\food-101'
foods_sorted = sorted(os.listdir(data_dir))
#data_dir = 'D:\\Nauka\\Studia\\4_sem\\SztucznaInteligencja\\A_star\\CNN\\foodRecognitionCNN\\food-101\\images'
#folder_dir = 'D:\\Nauka\\Studia\\4_sem\\SztucznaInteligencja\\A_star\\CNN\\foodRecognitionCNN\\food-101'
#foods_sorted = sorted(os.listdir(data_dir))
food_id = 0

View File

@ -0,0 +1,304 @@
import random
import math
from queue import PriorityQueue
from kelner.src.algorithms.GATableGenerator.Individual import Individual
from kelner.src.algorithms.AStar.Finder import Finder
from kelner.gui.GAdialog.GADefaults import GADefaults, CrossingOverMethod, MutationMethod, SelectionMethod
class GATableGenerator:
def __init__(self, gridCols, gridRows, params):
self.gridCols = gridCols
self.gridRows = gridRows
self.params = params
self.currentGenerationNumber = 0
self.population = None
self.bestFitnessScores = []
self.worstFitnessScores = []
self.bestTables = []
self.worstTables = []
def generateTables(self):
table = [[0] * self.gridCols for i in range(self.gridRows)]
for _ in range(self.params.runTablesCount):
isPositionUnique = False
while not isPositionUnique:
x = random.randint(0, self.gridCols - 1)
y = random.randint(0, self.gridRows - 1)
if (x, y) not in self.params.getForbiddenPlaces() and table[y][x] == 0:
isPositionUnique = True
table[y][x] = 1
table[self.params.kitchenPosition[1]][self.params.kitchenPosition[0]] = 1
return table
def getIndividual(self, table):
origin = self.params.waiterPosition
finder = Finder(table)
fitnessScore = 0
tablesCount = 0
for y in range(self.gridRows):
for x in range(self.gridCols):
if table[y][x] == 1:
tablesCount += 1
targets = finder.getNeighbours((x, y), False)
for target in targets:
if origin == target:
fitnessScore += 1
break
else:
path = finder.getPath(origin, target, True)
if path:
fitnessScore += 1
break
individual = Individual(table, fitnessScore - (tablesCount - fitnessScore), tablesCount)
return individual
def updateStats(self):
bestIndividual = self.getBestIndividual()
worstIndividual = self.getWorstIndividual()
self.bestFitnessScores.append(bestIndividual.getFitness())
self.bestTables.append(bestIndividual.getTablesCount())
self.worstFitnessScores.append(worstIndividual.getFitness())
self.worstTables.append(worstIndividual.getTablesCount())
def firstPopulation(self):
self.population = PriorityQueue()
for _ in range(self.params.runPopulationSize):
self.population.put(self.getIndividual(self.generateTables()))
self.currentGenerationNumber = 0
self.bestFitnessScores = []
self.worstFitnessScores = []
self.bestTables = []
self.worstTables = []
self.updateStats()
def getPopulationQueue(self):
queue = PriorityQueue()
for item in self.population.queue:
queue.put(item)
return queue
def getPopulationSortedArray(self):
queue = self.getPopulationQueue()
array = []
while not queue.empty():
array.append(queue.get())
return array
def arrayToQueue(self, array):
queue = PriorityQueue()
for item in array:
queue.put(item)
return queue
def selectParentsByRoulette(self):
populationArray = self.getPopulationSortedArray()
parents = []
delta = populationArray[len(populationArray) - 1].getFitness()
if delta > 0:
delta = 0
else:
delta = abs(delta) + 1
fitnessSum = 0
for individual in populationArray:
fitnessSum += individual.getFitness() + delta
for _ in range(2):
draw = random.uniform(0, 1)
accumulated = 0
for individual in populationArray:
probability = float(individual.getFitness() + delta) / fitnessSum
accumulated += probability
if draw <= accumulated:
parents.append(individual)
fitnessSum -= (individual.getFitness() + delta)
populationArray.remove(individual)
break
return parents
def selectParentsByTournament(self):
parents = []
competitorsNum = math.ceil(0.25 * self.params.runPopulationSize)
if competitorsNum < self.params.minPopulationSize:
competitorsNum = self.params.minPopulationSize - 1
population = self.getPopulationSortedArray()
parent = self.arrayToQueue(random.sample(population, competitorsNum)).get()
population.remove(parent)
parents.append(parent)
parents.append(self.arrayToQueue(random.sample(population, competitorsNum)).get())
return parents
def quadrant(self, parentL, parentR):
childTables = [[0] * self.gridCols for i in range(self.gridRows)]
for row in range(self.gridRows):
for col in range(self.gridCols):
if (row <= math.floor((self.gridRows - 1) / 2.0)) and (col <= math.ceil((self.gridCols - 1) / 2.0)) or \
(row > math.floor((self.gridRows - 1) / 2.0)) and (col > math.ceil((self.gridCols - 1) / 2.0)):
childTables[row][col] = parentL.getTables()[row][col]
else:
childTables[row][col] = parentR.getTables()[row][col]
return childTables
def singleVerticalDiv(self, parentL, parentR):
childTables = [[0] * self.gridCols for i in range(self.gridRows)]
divX = random.randint(1, self.gridCols - 2)
for row in range(self.gridRows):
for col in range(self.gridCols):
if col <= divX:
childTables[row][col] = parentL.getTables()[row][col]
else:
childTables[row][col] = parentR.getTables()[row][col]
return childTables
def singleHorizontalDiv(self, parentL, parentR):
childTables = [[0] * self.gridCols for i in range(self.gridRows)]
divY = random.randint(1, self.gridRows - 2)
for row in range(self.gridRows):
for col in range(self.gridCols):
if row <= divY:
childTables[row][col] = parentL.getTables()[row][col]
else:
childTables[row][col] = parentR.getTables()[row][col]
return childTables
def doubleVerticalDiv(self, parentL, parentR):
childTables = [[0] * self.gridCols for i in range(self.gridRows)]
divC = math.ceil((self.gridCols - 1) / 2)
divX1 = random.randint(1, divC - 1)
divX2 = random.randint(divC + 1, self.gridCols - 2)
for row in range(self.gridRows):
for col in range(self.gridCols):
if divX1 <= col < divX2:
childTables[row][col] = parentL.getTables()[row][col]
else:
childTables[row][col] = parentR.getTables()[row][col]
return childTables
def doubleHorizontalDiv(self, parentL, parentR):
childTables = [[0] * self.gridCols for i in range(self.gridRows)]
divC = math.ceil((self.gridRows - 1) / 2)
divY1 = random.randint(1, divC - 1)
divY2 = random.randint(divC + 1, self.gridRows - 2)
for row in range(self.gridRows):
for col in range(self.gridCols):
if divY1 <= row < divY2:
childTables[row][col] = parentL.getTables()[row][col]
else:
childTables[row][col] = parentR.getTables()[row][col]
return childTables
def randomChoice(self, parentL, parentR):
childTables = [[0] * self.gridCols for i in range(self.gridRows)]
parents = [parentL.getTables(), parentR.getTables()]
for row in range(self.gridRows):
for col in range(self.gridCols):
childTables[row][col] = random.choice(parents)[row][col]
return childTables
def procreate(self):
if self.params.runSelectionMethod == SelectionMethod.Roulette:
parentLeft, parentRight = self.selectParentsByRoulette()
elif self.params.runSelectionMethod == SelectionMethod.Tournament:
parentLeft, parentRight = self.selectParentsByTournament()
child = None
if self.params.runCrossingOverMethod == CrossingOverMethod.FixedQuadrant:
child = self.quadrant(parentLeft, parentRight)
elif self.params.runCrossingOverMethod == CrossingOverMethod.SingleVerticalDiv:
child = self.singleVerticalDiv(parentLeft, parentRight)
elif self.params.runCrossingOverMethod == CrossingOverMethod.SingleHorizontalDiv:
child = self.singleHorizontalDiv(parentLeft, parentRight)
elif self.params.runCrossingOverMethod == CrossingOverMethod.DoubleVerticalDiv:
child = self.doubleVerticalDiv(parentLeft, parentRight)
elif self.params.runCrossingOverMethod == CrossingOverMethod.DoubleHorizontalDiv:
child = self.doubleHorizontalDiv(parentLeft, parentRight)
elif self.params.runCrossingOverMethod == CrossingOverMethod.RandomChoice:
child = self.randomChoice(parentLeft, parentRight)
return child
def getRandTuple(self):
isPlaceAvailable = False
while not isPlaceAvailable:
randX = random.randint(0, self.gridCols - 1)
randY = random.randint(0, self.gridRows - 1)
isPlaceAvailable = (randX, randY) not in self.params.getForbiddenPlaces()
return randX, randY
def mutateFlip(self, tables):
mutatedTables = tables
numberOfMutation = random.randint(0, self.params.runMutation)
for _ in range(numberOfMutation):
randX, randY = self.getRandTuple()
if mutatedTables[randY][randX] == 1:
mutatedTables[randY][randX] = 0
else:
mutatedTables[randY][randX] = 1
return mutatedTables
def mutateSwap(self, tables):
mutatedTables = tables
numberOfMutation = random.randint(0, self.params.runMutation)
for _ in range(numberOfMutation):
randX1, randY1 = self.getRandTuple()
randX2, randY2 = self.getRandTuple()
value = mutatedTables[randY1][randX1]
mutatedTables[randY1][randX1] = mutatedTables[randY2][randX2]
mutatedTables[randY2][randX2] = value
return mutatedTables
def makeNextGeneration(self):
tablesArray = []
eliteSize = math.ceil((self.params.runPopulationSize * self.params.runElitism) / 100.0)
for _ in range(self.params.runPopulationSize - eliteSize):
if self.params.runMutationMethod == MutationMethod.Flip:
mutated = self.mutateFlip(self.procreate())
elif self.params.runMutationMethod == MutationMethod.Swap:
mutated = self.mutateSwap(self.procreate())
tablesArray.append(mutated)
eliteArray = []
for _ in range(eliteSize):
eliteArray.append(self.population.get())
self.population = PriorityQueue()
for individual in eliteArray:
self.population.put(individual)
for table in tablesArray:
self.population.put(self.getIndividual(table))
self.currentGenerationNumber += 1
self.updateStats()
def canGenerate(self):
return self.currentGenerationNumber < self.params.runGenerationsNumber
def getBestIndividual(self):
population = self.getPopulationQueue()
return population.get()
def getWorstIndividual(self):
population = self.getPopulationSortedArray()
return population[len(population) - 1]
def printBest(self):
populationBest = self.getBestIndividual()
populationBest.print(self.currentGenerationNumber)
def printPopulation(self):
queue = self.getPopulationQueue()
while not queue.empty():
individual = queue.get()
individual.print(self.currentGenerationNumber)
print('---------------------------------------')
def makeAllGenerations(self):
self.printBest()
while self.canGenerate():
self.makeNextGeneration()
self.printBest()
"""
params = GADefaults()
generator = GATableGenerator(15, 9, params)
generator.firstPopulation()
generator.printPopulation()
while generator.canGenerate():
generator.makeNextGeneration()
generator.printPopulation()
"""

View File

@ -0,0 +1,33 @@
class Individual:
def __init__(self, table, fitnessScore, tablesCount):
self.__table = table
self.__fitnessScore = fitnessScore
self.__tablesCount = tablesCount
# operator (>) for PriorityQueue comparison (determines the objects order)
def __gt__(self, other):
return self.__fitnessScore < other.__fitnessScore
def getTables(self):
return self.__table
def getTablesCount(self):
return self.__tablesCount
def getFitness(self):
return self.__fitnessScore
def getInfo(self, generation):
return f"GENERATION: {generation}, FITNESS: {self.__fitnessScore}, TABLES: {self.__tablesCount}"
def print(self, generation):
print(self.getInfo(generation))
cols = len(self.__table[0])
rows = len(self.__table)
for row in range(rows):
for col in range(cols):
v = self.__table[row][col]
v = ' ' if v == 0 else '#' if v == 1 else 'O'
print('|', v, sep='', end='')
print('|')

View File

@ -1,5 +1,3 @@
from kelner.src.managers.ImageCache import ImageCache, Images
class Drawable:
@ -62,15 +60,3 @@ class Drawable:
def drawAux(self, screen):
pass
def getImage(self, imageKind, img_path=None):
if imageKind in [Images.Guest1, Images.Guest2, Images.Guest3, Images.Plate]:
size = int(self.getCellSize() / 3)
elif imageKind in [Images.Kitchen, Images.Dishes]:
size = int(self.getCellSize())
else:
size = int(1.4 * self.getCellSize())
if img_path:
return ImageCache.getInstance().getImage(imageKind, size, size, img_path)
else:
return ImageCache.getInstance().getImage(imageKind, size, size)

View File

@ -1,18 +1,15 @@
import random
from enum import Enum
from threading import Lock
from kelner.src.components.Drawable import Drawable
from kelner.src.managers.ImageCache import ImageCache, Images
from kelner.src.algorithms.DecisionTree import Tree_Builder
import os
import copy
import time
import pygame
class Kitchen(Drawable):
def __init__(self, minX, maxX, minY, maxY, cellSize, offset):
def __init__(self, x, y, minX, maxX, minY, maxY, cellSize, offset):
# call base class constructor
super().__init__(14, 0, minX, maxX, minY, maxY, cellSize, offset)
super().__init__(x, y, minX, maxX, minY, maxY, cellSize, offset)
self._preparing_orders = None
self._ready_orders = None
self._photos_path = os.path.join(os.getcwd(), 'foodImages')
@ -92,7 +89,17 @@ class Kitchen(Drawable):
# print("Image drawing: {}".format(img_path))
return img_paths
def getImage(self, imageKind, img_path=None):
if imageKind in [Images.Guest1, Images.Guest2, Images.Guest3, Images.Plate]:
size = int(self.getCellSize() / 3)
elif imageKind in [Images.Kitchen, Images.Dishes]:
size = int(self.getCellSize())
else:
size = int(1.4 * self.getCellSize())
if img_path:
return ImageCache.getInstance().getImage(imageKind, size, size, img_path)
else:
return ImageCache.getInstance().getImage(imageKind, size, size)
def draw(self, screen):
xBase = self.getX() * self.getCellSize() + self.getOffset()

View File

@ -111,7 +111,7 @@ class Table(Drawable):
self.__status = status
def __getImage(self, imageKind):
if imageKind in [Images.Guest1, Images.Guest2, Images.Guest3, Images.Plate]:
if imageKind in [Images.Guest1, Images.Guest2, Images.Guest3, Images.Plate, Images.Chicken]:
size = int(self.getCellSize() / 3)
else:
size = int(1.4 * self.getCellSize())
@ -164,3 +164,15 @@ class Table(Drawable):
screen.blit(imagePlate, (xBase + guest1XOffset, yBase + platesYOffset))
screen.blit(imagePlate, (xBase + guest2XOffset, yBase + platesYOffset))
screen.blit(imagePlate, (xBase + guest3XOffset, yBase + platesYOffset))
elif self.isStatus(Status.Served):
platesYOffset = int(0.3 * self.getCellSize())
imageChicken = self.__getImage(Images.Chicken)
if len(self.__guests) == 1:
screen.blit(imageChicken, (xBase + guest2XOffset, yBase + platesYOffset))
elif len(self.__guests) == 2:
screen.blit(imageChicken, (xBase + guest4XOffset, yBase + platesYOffset))
screen.blit(imageChicken, (xBase + guest5XOffset, yBase + platesYOffset))
elif len(self.__guests) == 3:
screen.blit(imageChicken, (xBase + guest1XOffset, yBase + platesYOffset))
screen.blit(imageChicken, (xBase + guest2XOffset, yBase + platesYOffset))
screen.blit(imageChicken, (xBase + guest3XOffset, yBase + platesYOffset))

View File

@ -7,16 +7,43 @@ from kelner.src.components.Kitchen import Kitchen
# drawable objects manager
from kelner.src.managers.MenuManager import MenuManager
from kelner.src.managers.TableManager import TableManager
from kelner.src.managers.WaiterManager import WaiterManager
class DrawableCollection:
# const, minimal distance between objects
__MinDistanceX = 1
__MinDistanceY = 0
def __init__(self):
def __init__(self, kitchenManager):
# collection that holds all drawable objects
self.__mustRepaint = True
self.__drawables = []
self.__waiterLock = Lock()
self.__tasks = []
self.__kitchenManager = kitchenManager
def start(self):
# initialize menu manager
menuManager = MenuManager()
# new thread controlling tables
tableTask = TableManager(self, menuManager)
self.__tasks.append(tableTask)
waiters = self.getWaiters()
for waiter in waiters:
# new thread controlling waiter
waiterTask = WaiterManager(self, [waiter], self.__kitchenManager, menuManager, tableTask)
self.__tasks.append(waiterTask)
for task in self.__tasks:
task.start()
def stop(self):
for task in self.__tasks:
task.stop()
# adds drawable objects to the collection
def add(self, drawable):
@ -52,6 +79,10 @@ class DrawableCollection:
break
return isPositionAvailable
# deletes all tables
def delTables(self):
self.__drawables = [drawable for drawable in self.__drawables if not isinstance(drawable, Table)]
# gets all tables by status from collection
def getTables(self, status):
result = []
@ -61,11 +92,10 @@ class DrawableCollection:
return result
def get_kitchen(self):
kitchen = None
for item in self.__drawables:
if isinstance(item, Kitchen):
kitchen = item
return kitchen
return item
return None
def lock(self):
self.__waiterLock.acquire()
@ -101,8 +131,8 @@ class DrawableCollection:
nearestTables = []
tables = self.getTables(tableStatus)
for table in tables:
if (table.getX() == waiter.getX() and abs(table.getY() - waiter.getY()) == 1) or \
(table.getY() == waiter.getY() and abs(table.getX() - waiter.getX()) == 1):
if (table.getX() == waiter.getX() and abs(table.getY() - waiter.getY()) == 1) or\
(table.getY() == waiter.getY() and abs(table.getX() - waiter.getX()) == 1):
nearestTables.append(table)
return nearestTables

View File

@ -23,6 +23,7 @@ class Images(Enum):
ToolTip = 16
Kitchen = 17
Dishes = 18
Chicken = 19
class ImageCache:
@ -60,7 +61,8 @@ class ImageCache:
Images.Guest3: './images/wiking_rudy2.png',
Images.ToolTip: './images/tooltip.png',
Images.Kitchen: './images/kitchen.png',
Images.Dishes: './images/testDishes/'
Images.Dishes: './images/testDishes/',
Images.Chicken: './images/kurczak.png'
}
def __getFont(self):

View File

@ -6,9 +6,8 @@ import sys
# creates new thread
class KitchenManager(threading.Thread):
def __init__(self, drawable_manager, gridboard):
def __init__(self, gridboard):
super().__init__()
self._drawable_manager = drawable_manager
self._gridboard = gridboard
self.__runThread = False

View File

@ -0,0 +1,120 @@
import threading
from tkinter import messagebox, Tk
from kelner.src.components.Table import Table
from kelner.src.algorithms.GATableGenerator.GATableGenerator import GATableGenerator
from kelner.gui.GAdialog.GADialog import GADialog
from kelner.gui.GAdialog.GADefaults import GADefaults
from kelner.gui.chart.Plots import Plots
# creates new thread
class TableGenerator (threading.Thread):
def __init__(self, gridCols, gridRows, minX, maxX, minY, maxY, cellSize, paintOffset, drawableManager):
super().__init__()
self.__minX = minX
self.__maxX = maxX
self.__minY = minY
self.__maxY = maxY
self.__cellSize = cellSize
self.__paintOffset = paintOffset
self.__drawableManager = drawableManager
self.__runThread = True
self.__defaults = GADefaults()
self.__defaults.waiterPosition = self.__getDefaultWaiterPosition()
self.__defaults.kitchenPosition = self.__getDefaultKitchenPosition()
self.__gaTableGenerator = GATableGenerator(gridCols, gridRows, self.__defaults)
def __getDefaultWaiterPosition(self):
waiters = self.__drawableManager.getWaiters()
if waiters:
return waiters[0].getX(), waiters[0].getY()
return None
def __getDefaultKitchenPosition(self):
kitchen = self.__drawableManager.get_kitchen()
if kitchen is not None:
return kitchen.getX(), kitchen.getY()
return None
def __randomTables(self):
# initialize a number of tables given in range
for i in range(0, 40):
table = Table(self.__minX, self.__maxX, self.__minY, self.__maxY, self.__cellSize, self.__paintOffset)
if self.__drawableManager.generatePosition(table):
self.__drawableManager.add(table)
def __geneticTables(self):
bestIndividual = self.__gaTableGenerator.getBestIndividual()
for row in range(self.__gaTableGenerator.gridRows):
for col in range(self.__gaTableGenerator.gridCols):
if bestIndividual.getTables()[row][col] == 1 and \
(row != self.__defaults.kitchenPosition[1] or col != self.__defaults.kitchenPosition[0]):
table = Table(0, self.__gaTableGenerator.gridCols - 1, 0, self.__gaTableGenerator.gridRows - 1, self.__cellSize, self.__paintOffset)
table.setX(col)
table.setY(row)
self.__drawableManager.add(table)
def __askIfGA(self):
window = Tk()
window.attributes('-topmost', 'true')
window.wm_withdraw()
result = messagebox.askquestion("Rodzaj algorytmu", "Czy uruchomić algorytm genetyczny?", parent=window)
window.destroy()
return result == 'yes'
def __ask(self, info):
window = Tk()
window.attributes('-topmost', 'true')
window.wm_withdraw()
result = messagebox.askquestion(info + '\n', "Czy nowa generacja?", parent=window)
window.destroy()
return result == 'yes'
def __askIfAgain(self):
window = Tk()
window.attributes('-topmost', 'true')
window.wm_withdraw()
result = messagebox.askquestion("Potwierdź", "Czy powtórzyć proces?", parent=window)
window.destroy()
return result == 'yes'
def __plot(self):
plot = Plots(self.__gaTableGenerator.currentGenerationNumber,
self.__gaTableGenerator.bestFitnessScores, self.__gaTableGenerator.bestTables,
self.__gaTableGenerator.worstFitnessScores, self.__gaTableGenerator.worstTables,
self.__defaults.getInfo())
plot.draw()
def run(self):
if self.__askIfGA():
while self.__runThread:
again = True
GADialog(self.__defaults)
self.__gaTableGenerator.firstPopulation()
while again:
self.__drawableManager.delTables()
self.__geneticTables()
self.__drawableManager.forceRepaint()
if self.__gaTableGenerator.canGenerate():
if self.__gaTableGenerator.currentGenerationNumber % self.__defaults.runInfoFold != 0 or\
self.__ask(self.__gaTableGenerator.getBestIndividual().getInfo(self.__gaTableGenerator.currentGenerationNumber)):
self.__gaTableGenerator.makeNextGeneration()
else:
self.__drawableManager.start()
self.stop()
again = False
self.__plot()
else:
self.__plot()
if not self.__askIfAgain():
self.__drawableManager.start()
self.stop()
again = False
else:
self.__randomTables()
self.__drawableManager.forceRepaint()
self.__drawableManager.start()
def stop(self):
self.__runThread = False

View File

@ -1,6 +1,7 @@
import threading
import time
import sys
from math import sqrt
from kelner.src.components.Table import Status
from kelner.src.algorithms.AStar.Finder import Finder
from kelner.src.algorithms.BFS.BFS import BFS
@ -20,43 +21,65 @@ class WaiterManager(threading.Thread):
self._menu_manager = menu_manager
self._table_manager = table_manager
def __getNearestTargetPath(self, waiter, target):
distance = sys.maxsize
nearestTargetPath = None
reservedPlaces = self.__drawableManager.getReservedPlaces(waiter)
def __getDistance(self, waiter, tupleXY):
dx = waiter.getX() - tupleXY[0]
dy = waiter.getY() - tupleXY[1]
return sqrt(dx * dx + dy * dy)
def __sortTargets(self, waiter, targets):
return sorted(targets, key=lambda target: self.__getDistance(waiter, (target[0], target[1])))
def __getTargets(self, waiter, finder):
found = []
tables = self.__drawableManager.getTables(Status.Ready)
finder = Finder(reservedPlaces)
origin = (waiter.getX(), waiter.getY())
if target == 'kitchen':
path = finder.getPath(origin, (14, 1), True)
path2 = finder.getPath(origin, (13, 0), True)
path = path2 if len(path) > len(path2) else path
return path
if tables:
origin = (waiter.getX(), waiter.getY())
for table in tables:
if table.hasOrder():
targets = finder.getNeighbours((table.getX(), table.getY()), False)
for target in targets:
if target is not None:
path = finder.getPath(origin, target, True)
if path:
result = len(path)
if result < distance:
distance = result
nearestTargetPath = path
if target == origin:
return []
else:
found.append(target)
return self.__sortTargets(waiter, found)
def __getNearestTargetPath(self, waiter, targets=None):
distance = sys.maxsize
nearestTargetPath = None
reservedPlaces = self.__drawableManager.getReservedPlaces(waiter)
finder = Finder(reservedPlaces)
origin = (waiter.getX(), waiter.getY())
if targets is None:
targets = self.__getTargets(waiter, finder)
for target in targets:
if distance > self.__getDistance(waiter, target):
path = finder.getPath(origin, target, True)
if path:
result = len(path)
if result < distance:
distance = result
nearestTargetPath = path
return nearestTargetPath
def get_specified_path(self, waiter, table):
distance = sys.maxsize
nearestTargetPath = None
reserved_places = self.__drawableManager.getReservedPlaces(waiter)
finder = Finder(reserved_places)
origin = (waiter.getX(), waiter.getY())
targets = finder.getNeighbours((table[0], table[1]), False)
for target in targets:
if target is not None:
if distance > self.__getDistance(waiter, target):
path = finder.getPath(origin, target, True)
if path:
return path
result = len(path)
if result < distance:
distance = result
nearestTargetPath = path
return nearestTargetPath
def __changeWaiterDirection(self, waiter, x, y):
targetDirection = x - waiter.getX(), y - waiter.getY()
@ -172,13 +195,14 @@ class WaiterManager(threading.Thread):
if len(waiter.getAcceptedOrders()) > 1:
waiter.set_target('kitchen')
path = self.__getNearestTargetPath(waiter, target=waiter.get_target())
path = self.__getNearestTargetPath(waiter, [(14, 1), (13, 0)])
waiter.make_busy()
else:
if waiter.get_target() == 'return_order' and waiter.isPathEmpty():
print("Order returned")
table = self._table_manager.get_table(waiter.get_remaining_positions()[0])
table.setStatus(Status.Served)
self.__changeWaiterDirection(waiter, table.getX(), table.getY())
waiter.get_remaining_positions().pop(0)
time.sleep(2)
@ -187,7 +211,7 @@ class WaiterManager(threading.Thread):
path = self.get_specified_path(waiter, waiter.get_remaining_positions()[0])
else:
waiter.set_target('table')
path = self.__getNearestTargetPath(waiter, target='table')
path = self.__getNearestTargetPath(waiter)
waiter.setPath([] if path is None else path)
@ -201,6 +225,7 @@ class WaiterManager(threading.Thread):
if waiter.get_target() == 'kitchen':
self._table_manager.pause()
kitchen = self.__drawableManager.get_kitchen()
self.__changeWaiterDirection(waiter, kitchen.getX(), kitchen.getY())
waiter_orders = waiter.getAcceptedOrders()
# print("Waiter near kitchen. Collected orders: {}".format(waiter_orders))
kitchen.add_orders(waiter_orders)