Obracanie kelnera (algorytm i grafika). Kilku kelnerów może chodzić równocześnie. Tooltip z ilością zebranych zamówień.

This commit is contained in:
s444417 2020-04-27 12:22:19 +02:00
parent 760f2607e2
commit dba30bae63
25 changed files with 652 additions and 393 deletions

View File

@ -2,7 +2,7 @@
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.7 (WaiterMaster)" jdkType="Python SDK" />
<orderEntry type="jdk" jdkName="Python 3.7 (venv)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TestRunnerService">

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.7 (WaiterMaster)" project-jdk-type="Python SDK" />
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.7 (venv)" project-jdk-type="Python SDK" />
</project>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

BIN
kelner/images/tooltip.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@ -26,24 +26,39 @@ drawableManager = DrawableCollection()
menuManager = MenuManager()
# initialize waiter component
waiter = Waiter(0, 0, 0, GridCountX - 1, 0, GridCountY - 1, CellSize, PaintOffset)
waiter1 = Waiter(0, 0, 0, GridCountX - 1, 0, GridCountY - 1, CellSize, PaintOffset)
waiter2 = Waiter(0, 1, 0, GridCountX - 1, 0, GridCountY - 1, CellSize, PaintOffset)
waiter3 = Waiter(0, 2, 0, GridCountX - 1, 0, GridCountY - 1, CellSize, PaintOffset)
waiter4 = Waiter(0, 3, 0, GridCountX - 1, 0, GridCountY - 1, CellSize, PaintOffset)
# adds waiter to drawable collection
drawableManager.add(waiter)
drawableManager.add(waiter1)
drawableManager.add(waiter2)
drawableManager.add(waiter3)
drawableManager.add(waiter4)
# initialize a number of tables given in range
for i in range(1, 45):
for i in range(1, 40):
table = Table(0, GridCountX - 1, 0, GridCountY - 1, CellSize, PaintOffset)
drawableManager.generatePosition(table)
drawableManager.add(table)
if drawableManager.generatePosition(table):
drawableManager.add(table)
# new thread controlling tables
tableTask = TableManager(drawableManager, menuManager)
tableTask.start()
# new thread controlling waiter
waiterTask = WaiterManager(drawableManager)
waiterTask.start()
waiter1Task = WaiterManager(drawableManager, [waiter1])
waiter1Task.start()
waiter2Task = WaiterManager(drawableManager, [waiter2])
waiter2Task.start()
waiter3Task = WaiterManager(drawableManager, [waiter3])
waiter3Task.start()
waiter4Task = WaiterManager(drawableManager, [waiter4])
waiter4Task.start()
# main loop
running = True
@ -52,27 +67,51 @@ while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
tableTask.stop()
waiterTask.stop()
waiter1Task.stop()
waiter2Task.stop()
waiter3Task.stop()
waiter4Task.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(waiter.getX() - 1, waiter.getY()):
waiter.moveLeft()
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(waiter.getX() + 1, waiter.getY()):
waiter.moveRight()
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(waiter.getX(), waiter.getY() - 1):
waiter.moveUp()
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(waiter.getX(), waiter.getY() + 1):
waiter.moveDown()
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

View File

@ -1,125 +1,140 @@
from queue import PriorityQueue
from kelner.src.algorithms.AStar.Node import Node
class Finder:
def __init__(self, table):
self._table = table
self._xMin = 0
self._yMin = 0
self._xMax = len(table[0]) - 1
self._yMax = len(table) - 1
self.__isDiagonal = False
def _set(self, x, y, v):
self._table[y][x] = v
def _get(self, x, y):
return self._table[y][x]
# returns right position relative to the node
def __incXnopY(self, node, neighbours):
if node.x < self._xMax and self._get(node.x + 1, node.y) == 0:
neighbours.append(Node(node, node.x + 1, node.y, node.distance + 1))
# returns left position relative to the node
def __decXnopY(self, node, neighbours):
if self._xMin < node.x and self._get(node.x - 1, node.y) == 0:
neighbours.append(Node(node, node.x - 1, node.y, node.distance + 1))
# returns top position relative to the node
def __nopXincY(self, node, neighbours):
if node.y < self._yMax and self._get(node.x, node.y + 1) == 0:
neighbours.append(Node(node, node.x, node.y + 1, node.distance + 1))
# returns bottom position relative to the node
def __nopXdecY(self, node, neighbours):
if self._yMin < node.y and self._get(node.x, node.y - 1) == 0:
neighbours.append(Node(node, node.x, node.y - 1, node.distance + 1))
# returns left top position relative to the node
def __decXdecY(self, node, neighbours):
if (self._xMin < node.x and self._yMin < node.y and
self._get(node.x - 1, node.y - 1) == 0 and
self._get(node.x - 1, node.y) == 0 and
self._get(node.x, node.y - 1) == 0):
neighbours.append(Node(node, node.x - 1, node.y - 1, node.distance + 2))
# returns left bottom position relative to the node
def __decXincY(self, node, neighbours):
if (self._xMin < node.x and node.y < self._yMax and
self._get(node.x - 1, node.y + 1) == 0 and
self._get(node.x - 1, node.y) == 0 and
self._get(node.x, node.y + 1) == 0):
neighbours.append(Node(node, node.x - 1, node.y + 1, node.distance + 2))
# returns right bottom position relative to the node
def __incXincY(self, node, neighbours):
if (node.x < self._xMax and node.y < self._yMax and
self._get(node.x + 1, node.y + 1) == 0 and
self._get(node.x + 1, node.y) == 0 and
self._get(node.x, node.y + 1) == 0):
neighbours.append(Node(node, node.x + 1, node.y + 1, node.distance + 2))
# returns right top position relative to the node
def __incXdecY(self, node, neighbours):
if (node.x < self._xMax and self._yMin < node.y and
self._get(node.x + 1, node.y - 1) == 0 and
self._get(node.x + 1, node.y) == 0 and
self._get(node.x, node.y - 1) == 0):
neighbours.append(Node(node, node.x + 1, node.y - 1, node.distance + 2))
# returns all plausible positions relative to the node
def __getNeighbours(self, node):
neighbours = []
self.__nopXincY(node, neighbours)
self.__incXnopY(node, neighbours)
self.__decXnopY(node, neighbours)
self.__nopXdecY(node, neighbours)
if self.__isDiagonal:
self.__decXdecY(node, neighbours)
self.__decXincY(node, neighbours)
self.__incXincY(node, neighbours)
self.__incXdecY(node, neighbours)
return neighbours
# main algorithm - simplification of well known A*
def __getPath(self, origin, target):
Q = PriorityQueue()
V = set()
Q.put(origin)
while not Q.empty():
head = Q.get()
if head == target:
return head
V.add(head)
for node in self.__getNeighbours(head):
if node not in V:
node.estimated = node.distance + node.getDistanceTo(target)
Q.put(node)
V.add(node)
return None
# returns neighbours for locationXY-tuple as list of tuple(x,y)
def getNeighbours(self, locationXY, isDiagonal):
neighboursXY = []
self.__isDiagonal = isDiagonal
location = Node(None, locationXY[0], locationXY[1], 0)
neighbours = self.__getNeighbours(location)
for neighbour in neighbours:
neighboursXY.append((neighbour.x, neighbour.y))
return neighboursXY
# returns the shortest path as list of tuple(x,y) from originXY-tuple to targetXY-tuple
def getPath(self, originXY, targetXY, isDiagonal):
self.__isDiagonal = isDiagonal
origin = Node(None, originXY[0], originXY[1], 0)
target = Node(None, targetXY[0], targetXY[1], 0)
result = self.__getPath(origin, target)
path = []
while result is not None:
if result.parent is not None:
path.insert(0, (result.x, result.y))
result = result.parent
return path
from queue import PriorityQueue
from kelner.src.algorithms.AStar.Node import Node
class Finder:
def __init__(self, table):
self._table = table
self._xMin = 0
self._yMin = 0
self._xMax = len(table[0]) - 1
self._yMax = len(table) - 1
def _set(self, x, y, v):
self._table[y][x] = v
def _get(self, x, y):
return self._table[y][x]
# returns right position relative to the node
def __incXnopY(self, node, direction, isDiagonal, neighbours):
if node.x < self._xMax and self._get(node.x + 1, node.y) == 0:
turnsCount = self.getTurnsCount(direction, (1, 0), isDiagonal)
neighbours.append(Node(node, node.x + 1, node.y, node.distance + turnsCount + 1))
# returns left position relative to the node
def __decXnopY(self, node, direction, isDiagonal, neighbours):
if self._xMin < node.x and self._get(node.x - 1, node.y) == 0:
turnsCount = self.getTurnsCount(direction, (-1, 0), isDiagonal)
neighbours.append(Node(node, node.x - 1, node.y, node.distance + turnsCount + 1))
# returns top position relative to the node
def __nopXincY(self, node, direction, isDiagonal, neighbours):
if node.y < self._yMax and self._get(node.x, node.y + 1) == 0:
turnsCount = self.getTurnsCount(direction, (0, 1), isDiagonal)
neighbours.append(Node(node, node.x, node.y + 1, node.distance + turnsCount + 1))
# returns bottom position relative to the node
def __nopXdecY(self, node, direction, isDiagonal, neighbours):
if self._yMin < node.y and self._get(node.x, node.y - 1) == 0:
turnsCount = self.getTurnsCount(direction, (0, -1), isDiagonal)
neighbours.append(Node(node, node.x, node.y - 1, node.distance + turnsCount + 1))
# returns left top position relative to the node
def __decXdecY(self, node, direction, isDiagonal, neighbours):
if (self._xMin < node.x and self._yMin < node.y and
self._get(node.x - 1, node.y - 1) == 0 and
self._get(node.x - 1, node.y) == 0 and
self._get(node.x, node.y - 1) == 0):
turnsCount = self.getTurnsCount(direction, (-1, -1), isDiagonal)
neighbours.append(Node(node, node.x - 1, node.y - 1, node.distance + turnsCount + 2))
# returns left bottom position relative to the node
def __decXincY(self, node, direction, isDiagonal, neighbours):
if (self._xMin < node.x and node.y < self._yMax and
self._get(node.x - 1, node.y + 1) == 0 and
self._get(node.x - 1, node.y) == 0 and
self._get(node.x, node.y + 1) == 0):
turnsCount = self.getTurnsCount(direction, (-1, 1), isDiagonal)
neighbours.append(Node(node, node.x - 1, node.y + 1, node.distance + turnsCount + 2))
# returns right bottom position relative to the node
def __incXincY(self, node, direction, isDiagonal, neighbours):
if (node.x < self._xMax and node.y < self._yMax and
self._get(node.x + 1, node.y + 1) == 0 and
self._get(node.x + 1, node.y) == 0 and
self._get(node.x, node.y + 1) == 0):
turnsCount = self.getTurnsCount(direction, (1, 1), isDiagonal)
neighbours.append(Node(node, node.x + 1, node.y + 1, node.distance + turnsCount + 2))
# returns right top position relative to the node
def __incXdecY(self, node, direction, isDiagonal, neighbours):
if (node.x < self._xMax and self._yMin < node.y and
self._get(node.x + 1, node.y - 1) == 0 and
self._get(node.x + 1, node.y) == 0 and
self._get(node.x, node.y - 1) == 0):
turnsCount = self.getTurnsCount(direction, (1, -1), isDiagonal)
neighbours.append(Node(node, node.x + 1, node.y - 1, node.distance + turnsCount + 2))
# returns all plausible positions relative to the node
def __getNeighbours(self, node, isDiagonal):
direction = node.getDirection()
neighbours = []
self.__nopXincY(node, direction, isDiagonal, neighbours)
self.__incXnopY(node, direction, isDiagonal, neighbours)
self.__decXnopY(node, direction, isDiagonal, neighbours)
self.__nopXdecY(node, direction, isDiagonal, neighbours)
if isDiagonal:
self.__decXdecY(node, direction, isDiagonal, neighbours)
self.__decXincY(node, direction, isDiagonal, neighbours)
self.__incXincY(node, direction, isDiagonal, neighbours)
self.__incXdecY(node, direction, isDiagonal, neighbours)
return neighbours
# main algorithm - simplification of well known A*
def __getPath(self, origin, target, isDiagonal):
Q = PriorityQueue()
V = set()
Q.put(origin)
while not Q.empty():
head = Q.get()
if head == target:
return head
V.add(head)
for node in self.__getNeighbours(head, isDiagonal):
if node not in V:
node.estimated = node.distance + node.getDistanceTo(target)
Q.put(node)
V.add(node)
return None
# returns the number of turns to change direction from old to new
@staticmethod
def getTurnsCount(oldDirection, newDirection, isDiagonal):
if oldDirection == (0, 0) or oldDirection == newDirection:
return 0
if 0 == oldDirection[0] + newDirection[0] and 0 == oldDirection[1] + newDirection[1]:
return 4 if isDiagonal else 2
return abs(newDirection[0] - oldDirection[0]) + abs(newDirection[1] - oldDirection[1]) if isDiagonal else 1
# returns neighbours for locationXY-tuple as list of tuple(x,y)
def getNeighbours(self, locationXY, isDiagonal):
neighboursXY = []
location = Node(None, locationXY[0], locationXY[1], 0)
neighbours = self.__getNeighbours(location, isDiagonal)
for neighbour in neighbours:
neighboursXY.append((neighbour.x, neighbour.y))
return neighboursXY
# returns the shortest path as list of tuple(x,y) from originXY-tuple to targetXY-tuple
def getPath(self, originXY, targetXY, isDiagonal):
origin = Node(None, originXY[0], originXY[1], 0)
target = Node(None, targetXY[0], targetXY[1], 0)
result = self.__getPath(origin, target, isDiagonal)
path = []
while result is not None:
if result.parent is not None:
path.insert(0, (result.x, result.y))
result = result.parent
return path

View File

@ -1,60 +1,60 @@
from random import randint
from kelner.src.algorithms.AStar.Finder import Finder
class FinderTest(Finder):
def __init__(self, table):
super().__init__(table)
def __setValues(self, xys, v):
if xys is not None:
for xy in xys:
self._set(xy[0], xy[1], v)
def print(self, xys):
self.__setValues(xys, 2)
for row in self._table:
for col in row:
v = ' ' if col == 0 else '#' if col == 1 else 'O'
print('|', v, sep='', end='')
print('|')
self.__setValues(xys, 0)
def getRandomTuple(self):
while True:
x = randint(self._xMin, self._xMax)
y = randint(self._yMin, self._yMax)
if self._get(x, y) == 0:
break
return x, y
def getRandomBorderTuple(self):
xSet = [self._xMin, self._xMax]
ySet = [self._yMin, self._yMax]
while True:
x = randint(self._xMin, self._xMax)
y = randint(self._yMin, self._yMax)
if (x in xSet or y in ySet) and self._get(x, y) == 0:
break
return x, y
def fillRandom(self):
for _ in range(120):
while True:
x = randint(self._xMin, self._xMax)
y = randint(self._yMin, self._yMax)
if self._get(x, y) == 0:
break
self._set(x, y, 1)
cols = 20
rows = 20
table = [[0] * cols for i in range(rows)]
finder = FinderTest(table)
finder.fillRandom()
originXY = finder.getRandomBorderTuple()
targetXY = finder.getRandomBorderTuple()
result = finder.getPath(originXY, targetXY, True)
finder.print(result)
import random
from kelner.src.algorithms.AStar.Finder import Finder
class FinderTest(Finder):
def __init__(self, table):
super().__init__(table)
def __setValues(self, xys, v):
if xys is not None:
for xy in xys:
self._set(xy[0], xy[1], v)
def print(self, xys):
self.__setValues(xys, 2)
for row in self._table:
for col in row:
v = ' ' if col == 0 else '#' if col == 1 else 'O'
print('|', v, sep='', end='')
print('|')
self.__setValues(xys, 0)
def getRandomTuple(self):
while True:
x = random.randint(self._xMin, self._xMax)
y = random.randint(self._yMin, self._yMax)
if self._get(x, y) == 0:
break
return x, y
def getRandomBorderTuple(self):
xSet = [self._xMin, self._xMax]
ySet = [self._yMin, self._yMax]
while True:
x = random.randint(self._xMin, self._xMax)
y = random.randint(self._yMin, self._yMax)
if (x in xSet or y in ySet) and self._get(x, y) == 0:
break
return x, y
def fillRandom(self):
for _ in range(120):
while True:
x = random.randint(self._xMin, self._xMax)
y = random.randint(self._yMin, self._yMax)
if self._get(x, y) == 0:
break
self._set(x, y, 1)
cols = 20
rows = 20
table = [[0] * cols for i in range(rows)]
finder = FinderTest(table)
finder.fillRandom()
originXY = finder.getRandomBorderTuple()
targetXY = finder.getRandomBorderTuple()
result = finder.getPath(originXY, targetXY, True)
finder.print(result)

View File

@ -1,34 +1,40 @@
from math import sqrt
class Node:
def __init__(self, parent, x, y, distance):
self.parent = parent
self.x = x
self.y = y
self.distance = distance
self.estimated = distance
# returns distance from the object to other node
def getDistanceTo(self, other):
dx = self.x - other.x
dy = self.y - other.y
return sqrt(dx * dx + dy * dy)
# abs(dx) + abs(dy)
# used by str() method to represent the object
def __repr__(self):
return "%s:%s" % (self.x, self.y)
# generates hash key for Set
def __hash__(self):
return hash(str(self))
# operator (==) for Set (determines if the object equals other node)
def __eq__(self, other):
return (self.x == other.x) and (self.y == other.y)
# operator (>) for PriorityQueue comparison (determines the objects order)
def __gt__(self, other):
return self.estimated > other.estimated
from math import sqrt
class Node:
def __init__(self, parent, x, y, distance):
self.parent = parent
self.x = x
self.y = y
self.distance = distance
self.estimated = distance
# returns distance from the object to other node
def getDistanceTo(self, other):
dx = self.x - other.x
dy = self.y - other.y
return sqrt(dx * dx + dy * dy)
# abs(dx) + abs(dy)
def getDirection(self):
if self.parent is None:
return 0, 0
else:
return self.x - self.parent.x, self.y - self.parent.y
# used by str() method to represent the object
def __repr__(self):
return "%s:%s" % (self.x, self.y)
# generates hash key for Set
def __hash__(self):
return hash(str(self))
# operator (==) for Set (determines if the object equals other node)
def __eq__(self, other):
return (self.x == other.x) and (self.y == other.y)
# operator (>) for PriorityQueue comparison (determines the objects order)
def __gt__(self, other):
return self.estimated > other.estimated

View File

@ -10,10 +10,10 @@ class Drawable:
self.__maxX = maxX
self.__minY = minY
self.__maxY = maxY
self.setX(x)
self.setY(y)
self.__cellSize = cellSize # cell size in pixels
self.__offset = offset # paint offset in pixels
self.__x = x
self.__y = y
self.__cellSize = cellSize # cell size in pixels
self.__offset = offset # paint offset in pixels
def setX(self, x):
if x < self.__minX or self.__maxX < x:

View File

@ -1,18 +1,35 @@
import random
import pygame
from kelner.src.components.Drawable import Drawable
from kelner.src.managers.ImageCache import ImageCache, Images
class Direction:
LeftUp = (-1,-1)
Up = ( 0,-1)
RightUp = ( 1,-1)
Right = ( 1, 0)
RightDown = ( 1, 1)
Down = ( 0, 1)
LeftDown = (-1, 1)
Left = (-1, 0)
class Waiter(Drawable):
def __init__(self, x, y, minX, maxX, minY, maxY, ratio, offset):
# call base class constructor
super().__init__(x, y, minX, maxX, minY, maxY, ratio, offset)
self.__dx = Direction.Down[0]
self.__dy = Direction.Down[1]
self.__acceptedOrders = []
self.__currentPath = []
def moveUp(self):
if self.getY() > self.getMinY():
self.setY(self.getY() - 1)
self.setX(self.getX())
return True
else:
return False
@ -20,6 +37,7 @@ class Waiter(Drawable):
def moveDown(self):
if self.getY() < self.getMaxY():
self.setY(self.getY() + 1)
self.setX(self.getX())
return True
else:
return False
@ -27,6 +45,7 @@ class Waiter(Drawable):
def moveLeft(self):
if self.getX() > self.getMinX():
self.setX(self.getX() - 1)
self.setY(self.getY())
return True
else:
return False
@ -34,10 +53,56 @@ class Waiter(Drawable):
def moveRight(self):
if self.getX() < self.getMaxX():
self.setX(self.getX() + 1)
self.setY(self.getY())
return True
else:
return False
def setX(self, x):
oldX = self.getX()
if super().setX(x):
self.__dx = x - oldX
return True
else:
return False
def setY(self, y):
oldY = self.getY()
if super().setY(y):
self.__dy = y - oldY
return True
else:
return False
def getDirection(self):
return self.__dx, self.__dy
def setDirection(self, dx, dy):
self.__dx = dx
self.__dy = dy
def getNextDirection(self, oldDirectionXY, newDirectionXY, isDiagonal):
if oldDirectionXY == (0, 0) or oldDirectionXY == newDirectionXY:
return None
if 0 == oldDirectionXY[0]:
if 0 == newDirectionXY[0]:
return random.choice([-1, 1]), oldDirectionXY[1] if isDiagonal else 0
else:
dx = newDirectionXY[0] - oldDirectionXY[0]
return +1 if dx > 0 else -1, oldDirectionXY[1] if isDiagonal else 0
if 0 == oldDirectionXY[1]:
if 0 == newDirectionXY[1]:
return oldDirectionXY[0] if isDiagonal else 0, random.choice([-1, 1])
else:
dy = newDirectionXY[1] - oldDirectionXY[1]
return oldDirectionXY[0] if isDiagonal else 0, +1 if dy > 0 else -1
if 0 == oldDirectionXY[0] + newDirectionXY[0] and 0 == oldDirectionXY[1] + newDirectionXY[1]:
return random.choice([(oldDirectionXY[0], 0), (0, oldDirectionXY[1])])
else:
dx = newDirectionXY[0] - oldDirectionXY[0]
dy = newDirectionXY[1] - oldDirectionXY[1]
return (0, oldDirectionXY[1]) if abs(dx) > abs(dy) else (oldDirectionXY[0], 0)
# accepts orders from the table and stores them in queue
def addOrder(self, table):
self.__acceptedOrders += [(table, table.getOrder())]
@ -52,7 +117,47 @@ class Waiter(Drawable):
return self.__currentPath.pop(0)
def draw(self, screen):
imageWaiter = ImageCache.getInstance().getImage(Images.Waiter, self.getCellSize(), self.getCellSize())
xBase = self.getX() * self.getCellSize() + self.getOffset()
yBase = self.getY() * self.getCellSize() + self.getOffset()
screen.blit(imageWaiter, (xBase, yBase))
direction = self.getDirection()
imageKind = None
if direction == Direction.LeftUp:
imageKind = Images.WaiterLeftUp
elif direction == Direction.Up:
imageKind = Images.WaiterUp
elif direction == Direction.RightUp:
imageKind = Images.WaiterRightUp
elif direction == Direction.Right:
imageKind = Images.WaiterRight
elif direction == Direction.RightDown:
imageKind = Images.WaiterRightDown
elif direction == Direction.Down:
imageKind = Images.WaiterDown
elif direction == Direction.LeftDown:
imageKind = Images.WaiterLeftDown
elif direction == Direction.Left:
imageKind = Images.WaiterLeft
imageWaiter = ImageCache.getInstance().getImage(imageKind, self.getCellSize(), self.getCellSize())
self.__xBase = self.getX() * self.getCellSize() + self.getOffset()
self.__yBase = self.getY() * self.getCellSize() + self.getOffset()
screen.blit(imageWaiter, (self.__xBase, self.__yBase))
def drawAux(self, screen):
toolTipWidth = int(0.4 * self.getCellSize())
toolTipHeight = int(0.2 * self.getCellSize())
toolTipXOffset = int(0.6 * self.getCellSize())
toolTipYOffset = - int(0.1 * self.getCellSize())
imageToolTip = ImageCache.getInstance().getImage(Images.ToolTip, toolTipWidth, toolTipHeight)
screen.blit(imageToolTip, (self.__xBase + toolTipXOffset, self.__yBase + toolTipYOffset))
font = pygame.font.SysFont('comicsansms', 24, True)
imageText = font.render(str(len(self.__acceptedOrders)), True, (204, 0, 0))
size = imageText.get_size()
ratio = 0.9 * toolTipHeight
textWidth = int((ratio / size[1]) * size[0])
textHeight = int(ratio)
imageText = pygame.transform.scale(imageText, (textWidth, textHeight))
textXOffset = toolTipXOffset + int((toolTipWidth - textWidth) / 2)
textYOffset = toolTipYOffset + int(0.05 * toolTipHeight)
screen.blit(imageText, (self.__xBase + textXOffset, self.__yBase + textYOffset))

View File

@ -1,4 +1,6 @@
import random
from threading import Lock
from kelner.src.components.Table import Table, Status
from kelner.src.components.Waiter import Waiter
@ -6,13 +8,15 @@ from kelner.src.components.Waiter import Waiter
# drawable objects manager
class DrawableCollection:
# const, minimal distance between objects
__MinDistanceX = 0
__MinDistanceX = 1
__MinDistanceY = 0
def __init__(self):
# collection that holds all drawable objects
self.__mustRepaint = True
self.__drawables = []
self.__waiterLock = Lock()
self.__tableLock = Lock()
# adds drawable objects to the collection
def add(self, drawable):
@ -21,6 +25,7 @@ class DrawableCollection:
# generates and sets random (x, y) and cheks if it's not occupied by other object
def generatePosition(self, drawable):
isPositionUnique = False
attempt = 0
while not isPositionUnique:
x = random.randint(drawable.getMinX() + 1, drawable.getMaxX() - 1)
y = random.randint(drawable.getMinY() + 1, drawable.getMaxY() - 1)
@ -32,6 +37,11 @@ class DrawableCollection:
if isPositionUnique:
drawable.setX(x)
drawable.setY(y)
return True
else:
attempt += 1
if attempt > 40:
return False
# checks if position (x,y) is not occupied by other object
def isPositionAvailable(self, x, y):
@ -58,17 +68,6 @@ class DrawableCollection:
result += [item]
return result
# waiter collects orders from all nearest tables
def collectOrders(self):
waiters = self.getWaiters()
for waiter in waiters:
tables = self.getTables(Status.Ready)
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):
table.setStatus(Status.Waiting)
waiter.addOrder(table)
table.delOrder()
# returns table: 0 - position is available, 1 - position is occupied
def getReservedPlaces(self, waiter):
cols = waiter.getMaxX() - waiter.getMinX() + 1
@ -78,8 +77,51 @@ class DrawableCollection:
if tables:
for table in tables:
reservedPlaces[table.getY()][table.getX()] = 1
waiters = self.getWaiters()
if waiters:
for other in waiters:
if other is not waiter:
reservedPlaces[other.getY()][other.getX()] = 1
return reservedPlaces
def getNearestTables(self, waiter, tableStatus):
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):
nearestTables.append(table)
return nearestTables
# waiter collects orders from table
def collectOrder(self, waiter, table):
result = False
self.__tableLock.acquire()
try:
if table.isStatus(Status.Ready) and table.getOrder() != []:
waiter.addOrder(table)
table.delOrder()
result = True
finally:
self.__tableLock.release()
return result
def moveWaiter(self, someWaiter, x, y):
isPositionAvailable = True
waiters = self.getWaiters()
self.__waiterLock.acquire()
try:
for waiter in waiters:
if waiter is not someWaiter:
if waiter.getX() == x and waiter.getY() == y:
isPositionAvailable = False
break
if isPositionAvailable:
someWaiter.setX(x)
someWaiter.setY(y)
finally:
self.__waiterLock.release()
# the method is called externally and forces repainting
def forceRepaint(self):
self.__mustRepaint = True

View File

@ -1,50 +1,66 @@
import pygame
from enum import Enum
# images enum
class Images(Enum):
Background = 0
Waiter = 1
Table = 2
Menu = 3
Check = 4
Plate = 5
Guest1 = 6
Guest2 = 7
Guest3 = 8
class ImageCache:
__instance = None
@staticmethod
def getInstance():
if ImageCache.__instance is None:
ImageCache()
return ImageCache.__instance
def __init__(self):
""" Virtually private constructor. """
if ImageCache.__instance is not None:
raise Exception("This class is a singleton!")
else:
ImageCache.__instance = self
self.__images = {}
self.__paths = {Images.Background: './images/Backgroud.png',
Images.Waiter: './images/kelner.png',
Images.Table: './images/stol.png',
Images.Menu: './images/ksiazka.png',
Images.Check: './images/check.png',
Images.Plate: './images/plate.png',
Images.Guest1: './images/wiking_blond.png',
Images.Guest2: './images/wiking_rudy.png',
Images.Guest3: './images/wiking_rudy2.png'}
def getImage(self, imageKind, width, height):
key = str(imageKind.value) + ':' + str(width) + ':' + str(height)
image = self.__images.get(key, None)
if image is None:
image = pygame.transform.scale((pygame.image.load(self.__paths[imageKind])), (width, height))
self.__images[key] = image
return image
import pygame
from enum import Enum
# images enum
class Images(Enum):
Background = 0
WaiterLeftUp = 1
WaiterUp = 2
WaiterRightUp = 3
WaiterRight = 4
WaiterRightDown = 5
WaiterDown = 6
WaiterLeftDown = 7
WaiterLeft = 8
Table = 9
Menu = 10
Check = 11
Plate = 12
Guest1 = 13
Guest2 = 14
Guest3 = 15
ToolTip = 16
class ImageCache:
__instance = None
@staticmethod
def getInstance():
if ImageCache.__instance is None:
ImageCache()
return ImageCache.__instance
def __init__(self):
""" Virtually private constructor. """
if ImageCache.__instance is not None:
raise Exception("This class is a singleton!")
else:
ImageCache.__instance = self
self.__images = {}
self.__paths = {Images.Background: './images/Backgroud.png',
Images.WaiterLeftUp: './images/kelner_full_LU.png',
Images.WaiterUp: './images/kelner_full_U.png',
Images.WaiterRightUp: './images/kelner_full_RU.png',
Images.WaiterRight: './images/kelner_full_R.png',
Images.WaiterRightDown: './images/kelner_full_RD.png',
Images.WaiterDown: './images/kelner_full_D.png',
Images.WaiterLeftDown: './images/kelner_full_LD.png',
Images.WaiterLeft: './images/kelner_full_L.png',
Images.Table: './images/stol.png',
Images.Menu: './images/ksiazka.png',
Images.Check: './images/check.png',
Images.Plate: './images/plate.png',
Images.Guest1: './images/wiking_blond.png',
Images.Guest2: './images/wiking_rudy.png',
Images.Guest3: './images/wiking_rudy2.png',
Images.ToolTip: './images/tooltip.png'}
def getImage(self, imageKind, width, height):
key = str(imageKind.value) + ':' + str(width) + ':' + str(height)
image = self.__images.get(key, None)
if image is None:
image = pygame.transform.scale((pygame.image.load(self.__paths[imageKind])), (width, height))
self.__images[key] = image
return image

View File

@ -1,29 +1,29 @@
import threading
import time
import random
from kelner.src.components.Table import Status
# creates new thread
class TableManager (threading.Thread):
def __init__(self, drawableManager, menuManager):
super().__init__()
self.__drawableManager = drawableManager
self.__menuManager = menuManager
self.__runThread = True
# changes the status of a random table from NotReady to Ready
def run(self):
while self.__runThread:
tables = self.__drawableManager.getTables(Status.NotReady)
if tables:
tableIndex = random.randint(0, len(tables) - 1)
table = tables[tableIndex]
time.sleep(3)
table.setStatus(Status.Ready)
table.setOrder(self.__menuManager.generateOrder())
self.__drawableManager.forceRepaint()
def stop(self):
self.__runThread = False
import threading
import time
import random
from kelner.src.components.Table import Status
# creates new thread
class TableManager (threading.Thread):
def __init__(self, drawableManager, menuManager):
super().__init__()
self.__drawableManager = drawableManager
self.__menuManager = menuManager
self.__runThread = True
# changes the status of a random table from NotReady to Ready
def run(self):
while self.__runThread:
tables = self.__drawableManager.getTables(Status.NotReady)
if tables:
tableIndex = random.randint(0, len(tables) - 1)
table = tables[tableIndex]
time.sleep(1)
table.setStatus(Status.Ready)
table.setOrder(self.__menuManager.generateOrder())
self.__drawableManager.forceRepaint()
def stop(self):
self.__runThread = False

View File

@ -1,57 +1,93 @@
import threading
import time
import sys
from kelner.src.components.Table import Status
from kelner.src.algorithms.AStar.Finder import Finder
# creates new thread
class WaiterManager (threading.Thread):
def __init__(self, drawableManager):
super().__init__()
self.__drawableManager = drawableManager
self.__runThread = True
def __getNearestTargetPath(self, waiter):
distance = sys.maxsize
nearestTargetPath = None
tables = self.__drawableManager.getTables(Status.Ready)
if tables:
reservedPlaces = self.__drawableManager.getReservedPlaces(waiter)
finder = Finder(reservedPlaces)
origin = (waiter.getX(), waiter.getY())
for table in tables:
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
return nearestTargetPath
# changes the status of a random table from NotReady to Ready
def run(self):
while self.__runThread:
waiters = self.__drawableManager.getWaiters()
if waiters:
for waiter in waiters:
path = self.__getNearestTargetPath(waiter)
if path is not None:
waiter.setPath(path)
if not waiter.isPathEmpty():
step = waiter.popStepFromPath()
time.sleep(0.4)
waiter.setX(step[0])
waiter.setY(step[1])
self.__drawableManager.forceRepaint()
if waiter.isPathEmpty():
time.sleep(2)
self.__drawableManager.collectOrders()
self.__drawableManager.forceRepaint()
def stop(self):
self.__runThread = False
import threading
import time
import sys
from kelner.src.components.Table import Status
from kelner.src.algorithms.AStar.Finder import Finder
# creates new thread
class WaiterManager (threading.Thread):
def __init__(self, drawableManager, waiters):
super().__init__()
self.__drawableManager = drawableManager
self.__waiters = waiters
self.__runThread = True
def __getNearestTargetPath(self, waiter):
distance = sys.maxsize
nearestTargetPath = None
tables = self.__drawableManager.getTables(Status.Ready)
if tables:
reservedPlaces = self.__drawableManager.getReservedPlaces(waiter)
finder = Finder(reservedPlaces)
origin = (waiter.getX(), waiter.getY())
for table in tables:
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
return nearestTargetPath
def __changeWaiterDirection(self, waiter, x, y):
targetDirection = x - waiter.getX(), y - waiter.getY()
originDirection = waiter.getDirection()
while originDirection is not None:
originDirection = waiter.getNextDirection(originDirection, targetDirection, True)
if originDirection is not None:
time.sleep(0.3)
waiter.setDirection(originDirection[0], originDirection[1])
self.__drawableManager.forceRepaint()
def __moveWaiter(self, waiter, x, y):
time.sleep(0.4)
self.__drawableManager.moveWaiter(waiter, x, y)
self.__drawableManager.forceRepaint()
def __collectOrder(self, waiter):
doCollectOrder = True
while doCollectOrder:
tables = self.__drawableManager.getNearestTables(waiter, Status.Ready)
turns = sys.maxsize
lessTurnsTable = None
originDirection = waiter.getDirection()
for table in tables:
targetDirection = table.getX() - waiter.getX(), table.getY() - waiter.getY()
result = Finder.getTurnsCount(originDirection, targetDirection, True)
if result < turns:
turns = result
lessTurnsTable = table
if lessTurnsTable is not None:
tables.remove(lessTurnsTable)
self.__changeWaiterDirection(waiter, lessTurnsTable.getX(), lessTurnsTable.getY())
if self.__drawableManager.collectOrder(waiter, lessTurnsTable):
time.sleep(2)
lessTurnsTable.setStatus(Status.Waiting)
self.__drawableManager.forceRepaint()
doCollectOrder = len(tables) > 0
# changes the status of a random table from NotReady to Ready
def run(self):
while self.__runThread:
if self.__waiters:
for waiter in self.__waiters:
path = self.__getNearestTargetPath(waiter)
waiter.setPath([] if path is None else path)
if not waiter.isPathEmpty():
step = waiter.popStepFromPath()
self.__changeWaiterDirection(waiter, step[0], step[1])
self.__moveWaiter(waiter, step[0], step[1])
if waiter.isPathEmpty():
self.__collectOrder(waiter)
def stop(self):
self.__runThread = False