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"> <module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager"> <component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" /> <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" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>
<component name="TestRunnerService"> <component name="TestRunnerService">

View File

@ -3,5 +3,5 @@
<component name="JavaScriptSettings"> <component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" /> <option name="languageLevel" value="ES6" />
</component> </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> </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() menuManager = MenuManager()
# initialize waiter component # 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 # 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 # 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) table = Table(0, GridCountX - 1, 0, GridCountY - 1, CellSize, PaintOffset)
drawableManager.generatePosition(table) if drawableManager.generatePosition(table):
drawableManager.add(table) drawableManager.add(table)
# new thread controlling tables # new thread controlling tables
tableTask = TableManager(drawableManager, menuManager) tableTask = TableManager(drawableManager, menuManager)
tableTask.start() tableTask.start()
# new thread controlling waiter # new thread controlling waiter
waiterTask = WaiterManager(drawableManager) waiter1Task = WaiterManager(drawableManager, [waiter1])
waiterTask.start() waiter1Task.start()
waiter2Task = WaiterManager(drawableManager, [waiter2])
waiter2Task.start()
waiter3Task = WaiterManager(drawableManager, [waiter3])
waiter3Task.start()
waiter4Task = WaiterManager(drawableManager, [waiter4])
waiter4Task.start()
# main loop # main loop
running = True running = True
@ -52,27 +67,51 @@ while running:
for event in pygame.event.get(): for event in pygame.event.get():
if event.type == pygame.QUIT: if event.type == pygame.QUIT:
tableTask.stop() tableTask.stop()
waiterTask.stop() waiter1Task.stop()
waiter2Task.stop()
waiter3Task.stop()
waiter4Task.stop()
running = False running = False
# handles keyboard events # handles keyboard events
if event.type == pygame.KEYDOWN: if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT: if event.key == pygame.K_LEFT:
# checks if new waiter's position to the left is not occupied by other object # checks if new waiter's position to the left is not occupied by other object
if drawableManager.isPositionAvailable(waiter.getX() - 1, waiter.getY()): if drawableManager.isPositionAvailable(waiter1.getX() - 1, waiter1.getY()):
waiter.moveLeft() waiter1.moveLeft()
if event.key == pygame.K_RIGHT: if event.key == pygame.K_RIGHT:
# checks if new waiter's position to the right is not occupied by other object # checks if new waiter's position to the right is not occupied by other object
if drawableManager.isPositionAvailable(waiter.getX() + 1, waiter.getY()): if drawableManager.isPositionAvailable(waiter1.getX() + 1, waiter1.getY()):
waiter.moveRight() waiter1.moveRight()
if event.key == pygame.K_UP: if event.key == pygame.K_UP:
# checks if new waiter's position up is not occupied by other object # checks if new waiter's position up is not occupied by other object
if drawableManager.isPositionAvailable(waiter.getX(), waiter.getY() - 1): if drawableManager.isPositionAvailable(waiter1.getX(), waiter1.getY() - 1):
waiter.moveUp() waiter1.moveUp()
if event.key == pygame.K_DOWN: if event.key == pygame.K_DOWN:
# checks if new waiter's position down is not occupied by other object # checks if new waiter's position down is not occupied by other object
if drawableManager.isPositionAvailable(waiter.getX(), waiter.getY() + 1): if drawableManager.isPositionAvailable(waiter1.getX(), waiter1.getY() + 1):
waiter.moveDown() 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() drawableManager.forceRepaint()
# repaints all objects to the screen # repaints all objects to the screen

View File

@ -10,7 +10,6 @@ class Finder:
self._yMin = 0 self._yMin = 0
self._xMax = len(table[0]) - 1 self._xMax = len(table[0]) - 1
self._yMax = len(table) - 1 self._yMax = len(table) - 1
self.__isDiagonal = False
def _set(self, x, y, v): def _set(self, x, y, v):
self._table[y][x] = v self._table[y][x] = v
@ -19,73 +18,82 @@ class Finder:
return self._table[y][x] return self._table[y][x]
# returns right position relative to the node # returns right position relative to the node
def __incXnopY(self, node, neighbours): def __incXnopY(self, node, direction, isDiagonal, neighbours):
if node.x < self._xMax and self._get(node.x + 1, node.y) == 0: 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)) 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 # returns left position relative to the node
def __decXnopY(self, node, neighbours): def __decXnopY(self, node, direction, isDiagonal, neighbours):
if self._xMin < node.x and self._get(node.x - 1, node.y) == 0: 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)) 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 # returns top position relative to the node
def __nopXincY(self, node, neighbours): def __nopXincY(self, node, direction, isDiagonal, neighbours):
if node.y < self._yMax and self._get(node.x, node.y + 1) == 0: 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)) 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 # returns bottom position relative to the node
def __nopXdecY(self, node, neighbours): def __nopXdecY(self, node, direction, isDiagonal, neighbours):
if self._yMin < node.y and self._get(node.x, node.y - 1) == 0: 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)) 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 # returns left top position relative to the node
def __decXdecY(self, node, neighbours): def __decXdecY(self, node, direction, isDiagonal, neighbours):
if (self._xMin < node.x and self._yMin < node.y and 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 - 1) == 0 and
self._get(node.x - 1, node.y) == 0 and self._get(node.x - 1, node.y) == 0 and
self._get(node.x, node.y - 1) == 0): self._get(node.x, node.y - 1) == 0):
neighbours.append(Node(node, node.x - 1, node.y - 1, node.distance + 2)) 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 # returns left bottom position relative to the node
def __decXincY(self, node, neighbours): def __decXincY(self, node, direction, isDiagonal, neighbours):
if (self._xMin < node.x and node.y < self._yMax and 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 + 1) == 0 and
self._get(node.x - 1, node.y) == 0 and self._get(node.x - 1, node.y) == 0 and
self._get(node.x, node.y + 1) == 0): self._get(node.x, node.y + 1) == 0):
neighbours.append(Node(node, node.x - 1, node.y + 1, node.distance + 2)) 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 # returns right bottom position relative to the node
def __incXincY(self, node, neighbours): def __incXincY(self, node, direction, isDiagonal, neighbours):
if (node.x < self._xMax and node.y < self._yMax and 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 + 1) == 0 and
self._get(node.x + 1, node.y) == 0 and self._get(node.x + 1, node.y) == 0 and
self._get(node.x, node.y + 1) == 0): self._get(node.x, node.y + 1) == 0):
neighbours.append(Node(node, node.x + 1, node.y + 1, node.distance + 2)) 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 # returns right top position relative to the node
def __incXdecY(self, node, neighbours): def __incXdecY(self, node, direction, isDiagonal, neighbours):
if (node.x < self._xMax and self._yMin < node.y and 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 - 1) == 0 and
self._get(node.x + 1, node.y) == 0 and self._get(node.x + 1, node.y) == 0 and
self._get(node.x, node.y - 1) == 0): self._get(node.x, node.y - 1) == 0):
neighbours.append(Node(node, node.x + 1, node.y - 1, node.distance + 2)) 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 # returns all plausible positions relative to the node
def __getNeighbours(self, node): def __getNeighbours(self, node, isDiagonal):
direction = node.getDirection()
neighbours = [] neighbours = []
self.__nopXincY(node, neighbours) self.__nopXincY(node, direction, isDiagonal, neighbours)
self.__incXnopY(node, neighbours) self.__incXnopY(node, direction, isDiagonal, neighbours)
self.__decXnopY(node, neighbours) self.__decXnopY(node, direction, isDiagonal, neighbours)
self.__nopXdecY(node, neighbours) self.__nopXdecY(node, direction, isDiagonal, neighbours)
if self.__isDiagonal: if isDiagonal:
self.__decXdecY(node, neighbours) self.__decXdecY(node, direction, isDiagonal, neighbours)
self.__decXincY(node, neighbours) self.__decXincY(node, direction, isDiagonal, neighbours)
self.__incXincY(node, neighbours) self.__incXincY(node, direction, isDiagonal, neighbours)
self.__incXdecY(node, neighbours) self.__incXdecY(node, direction, isDiagonal, neighbours)
return neighbours return neighbours
# main algorithm - simplification of well known A* # main algorithm - simplification of well known A*
def __getPath(self, origin, target): def __getPath(self, origin, target, isDiagonal):
Q = PriorityQueue() Q = PriorityQueue()
V = set() V = set()
Q.put(origin) Q.put(origin)
@ -94,29 +102,36 @@ class Finder:
if head == target: if head == target:
return head return head
V.add(head) V.add(head)
for node in self.__getNeighbours(head): for node in self.__getNeighbours(head, isDiagonal):
if node not in V: if node not in V:
node.estimated = node.distance + node.getDistanceTo(target) node.estimated = node.distance + node.getDistanceTo(target)
Q.put(node) Q.put(node)
V.add(node) V.add(node)
return None 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) # returns neighbours for locationXY-tuple as list of tuple(x,y)
def getNeighbours(self, locationXY, isDiagonal): def getNeighbours(self, locationXY, isDiagonal):
neighboursXY = [] neighboursXY = []
self.__isDiagonal = isDiagonal
location = Node(None, locationXY[0], locationXY[1], 0) location = Node(None, locationXY[0], locationXY[1], 0)
neighbours = self.__getNeighbours(location) neighbours = self.__getNeighbours(location, isDiagonal)
for neighbour in neighbours: for neighbour in neighbours:
neighboursXY.append((neighbour.x, neighbour.y)) neighboursXY.append((neighbour.x, neighbour.y))
return neighboursXY return neighboursXY
# returns the shortest path as list of tuple(x,y) from originXY-tuple to targetXY-tuple # returns the shortest path as list of tuple(x,y) from originXY-tuple to targetXY-tuple
def getPath(self, originXY, targetXY, isDiagonal): def getPath(self, originXY, targetXY, isDiagonal):
self.__isDiagonal = isDiagonal
origin = Node(None, originXY[0], originXY[1], 0) origin = Node(None, originXY[0], originXY[1], 0)
target = Node(None, targetXY[0], targetXY[1], 0) target = Node(None, targetXY[0], targetXY[1], 0)
result = self.__getPath(origin, target) result = self.__getPath(origin, target, isDiagonal)
path = [] path = []
while result is not None: while result is not None:
if result.parent is not None: if result.parent is not None:

View File

@ -1,4 +1,4 @@
from random import randint import random
from kelner.src.algorithms.AStar.Finder import Finder from kelner.src.algorithms.AStar.Finder import Finder
@ -23,8 +23,8 @@ class FinderTest(Finder):
def getRandomTuple(self): def getRandomTuple(self):
while True: while True:
x = randint(self._xMin, self._xMax) x = random.randint(self._xMin, self._xMax)
y = randint(self._yMin, self._yMax) y = random.randint(self._yMin, self._yMax)
if self._get(x, y) == 0: if self._get(x, y) == 0:
break break
return x, y return x, y
@ -33,8 +33,8 @@ class FinderTest(Finder):
xSet = [self._xMin, self._xMax] xSet = [self._xMin, self._xMax]
ySet = [self._yMin, self._yMax] ySet = [self._yMin, self._yMax]
while True: while True:
x = randint(self._xMin, self._xMax) x = random.randint(self._xMin, self._xMax)
y = randint(self._yMin, self._yMax) y = random.randint(self._yMin, self._yMax)
if (x in xSet or y in ySet) and self._get(x, y) == 0: if (x in xSet or y in ySet) and self._get(x, y) == 0:
break break
return x, y return x, y
@ -42,8 +42,8 @@ class FinderTest(Finder):
def fillRandom(self): def fillRandom(self):
for _ in range(120): for _ in range(120):
while True: while True:
x = randint(self._xMin, self._xMax) x = random.randint(self._xMin, self._xMax)
y = randint(self._yMin, self._yMax) y = random.randint(self._yMin, self._yMax)
if self._get(x, y) == 0: if self._get(x, y) == 0:
break break
self._set(x, y, 1) self._set(x, y, 1)

View File

@ -17,6 +17,12 @@ class Node:
return sqrt(dx * dx + dy * dy) return sqrt(dx * dx + dy * dy)
# abs(dx) + abs(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 # used by str() method to represent the object
def __repr__(self): def __repr__(self):
return "%s:%s" % (self.x, self.y) return "%s:%s" % (self.x, self.y)

View File

@ -10,10 +10,10 @@ class Drawable:
self.__maxX = maxX self.__maxX = maxX
self.__minY = minY self.__minY = minY
self.__maxY = maxY self.__maxY = maxY
self.setX(x) self.__x = x
self.setY(y) self.__y = y
self.__cellSize = cellSize # cell size in pixels self.__cellSize = cellSize # cell size in pixels
self.__offset = offset # paint offset in pixels self.__offset = offset # paint offset in pixels
def setX(self, x): def setX(self, x):
if x < self.__minX or self.__maxX < 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.components.Drawable import Drawable
from kelner.src.managers.ImageCache import ImageCache, Images 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): class Waiter(Drawable):
def __init__(self, x, y, minX, maxX, minY, maxY, ratio, offset): def __init__(self, x, y, minX, maxX, minY, maxY, ratio, offset):
# call base class constructor # call base class constructor
super().__init__(x, y, minX, maxX, minY, maxY, ratio, offset) super().__init__(x, y, minX, maxX, minY, maxY, ratio, offset)
self.__dx = Direction.Down[0]
self.__dy = Direction.Down[1]
self.__acceptedOrders = [] self.__acceptedOrders = []
self.__currentPath = [] self.__currentPath = []
def moveUp(self): def moveUp(self):
if self.getY() > self.getMinY(): if self.getY() > self.getMinY():
self.setY(self.getY() - 1) self.setY(self.getY() - 1)
self.setX(self.getX())
return True return True
else: else:
return False return False
@ -20,6 +37,7 @@ class Waiter(Drawable):
def moveDown(self): def moveDown(self):
if self.getY() < self.getMaxY(): if self.getY() < self.getMaxY():
self.setY(self.getY() + 1) self.setY(self.getY() + 1)
self.setX(self.getX())
return True return True
else: else:
return False return False
@ -27,6 +45,7 @@ class Waiter(Drawable):
def moveLeft(self): def moveLeft(self):
if self.getX() > self.getMinX(): if self.getX() > self.getMinX():
self.setX(self.getX() - 1) self.setX(self.getX() - 1)
self.setY(self.getY())
return True return True
else: else:
return False return False
@ -34,10 +53,56 @@ class Waiter(Drawable):
def moveRight(self): def moveRight(self):
if self.getX() < self.getMaxX(): if self.getX() < self.getMaxX():
self.setX(self.getX() + 1) self.setX(self.getX() + 1)
self.setY(self.getY())
return True return True
else: else:
return False 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 # accepts orders from the table and stores them in queue
def addOrder(self, table): def addOrder(self, table):
self.__acceptedOrders += [(table, table.getOrder())] self.__acceptedOrders += [(table, table.getOrder())]
@ -52,7 +117,47 @@ class Waiter(Drawable):
return self.__currentPath.pop(0) return self.__currentPath.pop(0)
def draw(self, screen): def draw(self, screen):
imageWaiter = ImageCache.getInstance().getImage(Images.Waiter, self.getCellSize(), self.getCellSize()) direction = self.getDirection()
xBase = self.getX() * self.getCellSize() + self.getOffset() imageKind = None
yBase = self.getY() * self.getCellSize() + self.getOffset() if direction == Direction.LeftUp:
screen.blit(imageWaiter, (xBase, yBase)) 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 import random
from threading import Lock
from kelner.src.components.Table import Table, Status from kelner.src.components.Table import Table, Status
from kelner.src.components.Waiter import Waiter from kelner.src.components.Waiter import Waiter
@ -6,13 +8,15 @@ from kelner.src.components.Waiter import Waiter
# drawable objects manager # drawable objects manager
class DrawableCollection: class DrawableCollection:
# const, minimal distance between objects # const, minimal distance between objects
__MinDistanceX = 0 __MinDistanceX = 1
__MinDistanceY = 0 __MinDistanceY = 0
def __init__(self): def __init__(self):
# collection that holds all drawable objects # collection that holds all drawable objects
self.__mustRepaint = True self.__mustRepaint = True
self.__drawables = [] self.__drawables = []
self.__waiterLock = Lock()
self.__tableLock = Lock()
# adds drawable objects to the collection # adds drawable objects to the collection
def add(self, drawable): 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 # generates and sets random (x, y) and cheks if it's not occupied by other object
def generatePosition(self, drawable): def generatePosition(self, drawable):
isPositionUnique = False isPositionUnique = False
attempt = 0
while not isPositionUnique: while not isPositionUnique:
x = random.randint(drawable.getMinX() + 1, drawable.getMaxX() - 1) x = random.randint(drawable.getMinX() + 1, drawable.getMaxX() - 1)
y = random.randint(drawable.getMinY() + 1, drawable.getMaxY() - 1) y = random.randint(drawable.getMinY() + 1, drawable.getMaxY() - 1)
@ -32,6 +37,11 @@ class DrawableCollection:
if isPositionUnique: if isPositionUnique:
drawable.setX(x) drawable.setX(x)
drawable.setY(y) drawable.setY(y)
return True
else:
attempt += 1
if attempt > 40:
return False
# checks if position (x,y) is not occupied by other object # checks if position (x,y) is not occupied by other object
def isPositionAvailable(self, x, y): def isPositionAvailable(self, x, y):
@ -58,17 +68,6 @@ class DrawableCollection:
result += [item] result += [item]
return result 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 # returns table: 0 - position is available, 1 - position is occupied
def getReservedPlaces(self, waiter): def getReservedPlaces(self, waiter):
cols = waiter.getMaxX() - waiter.getMinX() + 1 cols = waiter.getMaxX() - waiter.getMinX() + 1
@ -78,8 +77,51 @@ class DrawableCollection:
if tables: if tables:
for table in tables: for table in tables:
reservedPlaces[table.getY()][table.getX()] = 1 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 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 # the method is called externally and forces repainting
def forceRepaint(self): def forceRepaint(self):
self.__mustRepaint = True self.__mustRepaint = True

View File

@ -5,14 +5,22 @@ from enum import Enum
# images enum # images enum
class Images(Enum): class Images(Enum):
Background = 0 Background = 0
Waiter = 1 WaiterLeftUp = 1
Table = 2 WaiterUp = 2
Menu = 3 WaiterRightUp = 3
Check = 4 WaiterRight = 4
Plate = 5 WaiterRightDown = 5
Guest1 = 6 WaiterDown = 6
Guest2 = 7 WaiterLeftDown = 7
Guest3 = 8 WaiterLeft = 8
Table = 9
Menu = 10
Check = 11
Plate = 12
Guest1 = 13
Guest2 = 14
Guest3 = 15
ToolTip = 16
class ImageCache: class ImageCache:
@ -31,15 +39,23 @@ class ImageCache:
else: else:
ImageCache.__instance = self ImageCache.__instance = self
self.__images = {} self.__images = {}
self.__paths = {Images.Background: './images/Backgroud.png', self.__paths = {Images.Background: './images/Backgroud.png',
Images.Waiter: './images/kelner.png', Images.WaiterLeftUp: './images/kelner_full_LU.png',
Images.Table: './images/stol.png', Images.WaiterUp: './images/kelner_full_U.png',
Images.Menu: './images/ksiazka.png', Images.WaiterRightUp: './images/kelner_full_RU.png',
Images.Check: './images/check.png', Images.WaiterRight: './images/kelner_full_R.png',
Images.Plate: './images/plate.png', Images.WaiterRightDown: './images/kelner_full_RD.png',
Images.Guest1: './images/wiking_blond.png', Images.WaiterDown: './images/kelner_full_D.png',
Images.Guest2: './images/wiking_rudy.png', Images.WaiterLeftDown: './images/kelner_full_LD.png',
Images.Guest3: './images/wiking_rudy2.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): def getImage(self, imageKind, width, height):
key = str(imageKind.value) + ':' + str(width) + ':' + str(height) key = str(imageKind.value) + ':' + str(width) + ':' + str(height)

View File

@ -20,7 +20,7 @@ class TableManager (threading.Thread):
if tables: if tables:
tableIndex = random.randint(0, len(tables) - 1) tableIndex = random.randint(0, len(tables) - 1)
table = tables[tableIndex] table = tables[tableIndex]
time.sleep(3) time.sleep(1)
table.setStatus(Status.Ready) table.setStatus(Status.Ready)
table.setOrder(self.__menuManager.generateOrder()) table.setOrder(self.__menuManager.generateOrder())
self.__drawableManager.forceRepaint() self.__drawableManager.forceRepaint()

View File

@ -8,9 +8,10 @@ from kelner.src.algorithms.AStar.Finder import Finder
# creates new thread # creates new thread
class WaiterManager (threading.Thread): class WaiterManager (threading.Thread):
def __init__(self, drawableManager): def __init__(self, drawableManager, waiters):
super().__init__() super().__init__()
self.__drawableManager = drawableManager self.__drawableManager = drawableManager
self.__waiters = waiters
self.__runThread = True self.__runThread = True
def __getNearestTargetPath(self, waiter): def __getNearestTargetPath(self, waiter):
@ -33,25 +34,60 @@ class WaiterManager (threading.Thread):
nearestTargetPath = path nearestTargetPath = path
return nearestTargetPath 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 # changes the status of a random table from NotReady to Ready
def run(self): def run(self):
while self.__runThread: while self.__runThread:
waiters = self.__drawableManager.getWaiters() if self.__waiters:
if waiters: for waiter in self.__waiters:
for waiter in waiters:
path = self.__getNearestTargetPath(waiter) path = self.__getNearestTargetPath(waiter)
if path is not None: waiter.setPath([] if path is None else path)
waiter.setPath(path)
if not waiter.isPathEmpty(): if not waiter.isPathEmpty():
step = waiter.popStepFromPath() step = waiter.popStepFromPath()
time.sleep(0.4) self.__changeWaiterDirection(waiter, step[0], step[1])
waiter.setX(step[0]) self.__moveWaiter(waiter, step[0], step[1])
waiter.setY(step[1])
self.__drawableManager.forceRepaint()
if waiter.isPathEmpty(): if waiter.isPathEmpty():
time.sleep(2) self.__collectOrder(waiter)
self.__drawableManager.collectOrders()
self.__drawableManager.forceRepaint()
def stop(self): def stop(self):
self.__runThread = False self.__runThread = False