Obracanie kelnera (algorytm i grafika). Kilku kelnerów może chodzić równocześnie. Tooltip z ilością zebranych zamówień.
@ -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">
|
||||
|
@ -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>
|
Before Width: | Height: | Size: 184 B |
BIN
kelner/images/kelner_full_D.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
kelner/images/kelner_full_L.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
kelner/images/kelner_full_LD.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
kelner/images/kelner_full_LU.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
kelner/images/kelner_full_R.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
kelner/images/kelner_full_RD.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
kelner/images/kelner_full_RU.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
kelner/images/kelner_full_U.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 155 KiB |
BIN
kelner/images/tooltip.png
Normal file
After Width: | Height: | Size: 365 B |
Before Width: | Height: | Size: 18 KiB |
@ -26,15 +26,21 @@ 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)
|
||||
if drawableManager.generatePosition(table):
|
||||
drawableManager.add(table)
|
||||
|
||||
# new thread controlling tables
|
||||
@ -42,8 +48,17 @@ 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
|
||||
|
@ -10,7 +10,6 @@ class Finder:
|
||||
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
|
||||
@ -19,73 +18,82 @@ class Finder:
|
||||
return self._table[y][x]
|
||||
|
||||
# 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:
|
||||
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
|
||||
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:
|
||||
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
|
||||
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:
|
||||
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
|
||||
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:
|
||||
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
|
||||
def __decXdecY(self, node, neighbours):
|
||||
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):
|
||||
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
|
||||
def __decXincY(self, node, neighbours):
|
||||
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):
|
||||
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
|
||||
def __incXincY(self, node, neighbours):
|
||||
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):
|
||||
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
|
||||
def __incXdecY(self, node, neighbours):
|
||||
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):
|
||||
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
|
||||
def __getNeighbours(self, node):
|
||||
def __getNeighbours(self, node, isDiagonal):
|
||||
direction = node.getDirection()
|
||||
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)
|
||||
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):
|
||||
def __getPath(self, origin, target, isDiagonal):
|
||||
Q = PriorityQueue()
|
||||
V = set()
|
||||
Q.put(origin)
|
||||
@ -94,29 +102,36 @@ class Finder:
|
||||
if head == target:
|
||||
return head
|
||||
V.add(head)
|
||||
for node in self.__getNeighbours(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 = []
|
||||
self.__isDiagonal = isDiagonal
|
||||
location = Node(None, locationXY[0], locationXY[1], 0)
|
||||
neighbours = self.__getNeighbours(location)
|
||||
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):
|
||||
self.__isDiagonal = isDiagonal
|
||||
origin = Node(None, originXY[0], originXY[1], 0)
|
||||
target = Node(None, targetXY[0], targetXY[1], 0)
|
||||
result = self.__getPath(origin, target)
|
||||
result = self.__getPath(origin, target, isDiagonal)
|
||||
path = []
|
||||
while result is not None:
|
||||
if result.parent is not None:
|
||||
|
@ -1,4 +1,4 @@
|
||||
from random import randint
|
||||
import random
|
||||
from kelner.src.algorithms.AStar.Finder import Finder
|
||||
|
||||
|
||||
@ -23,8 +23,8 @@ class FinderTest(Finder):
|
||||
|
||||
def getRandomTuple(self):
|
||||
while True:
|
||||
x = randint(self._xMin, self._xMax)
|
||||
y = randint(self._yMin, self._yMax)
|
||||
x = random.randint(self._xMin, self._xMax)
|
||||
y = random.randint(self._yMin, self._yMax)
|
||||
if self._get(x, y) == 0:
|
||||
break
|
||||
return x, y
|
||||
@ -33,8 +33,8 @@ class FinderTest(Finder):
|
||||
xSet = [self._xMin, self._xMax]
|
||||
ySet = [self._yMin, self._yMax]
|
||||
while True:
|
||||
x = randint(self._xMin, self._xMax)
|
||||
y = randint(self._yMin, self._yMax)
|
||||
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
|
||||
@ -42,8 +42,8 @@ class FinderTest(Finder):
|
||||
def fillRandom(self):
|
||||
for _ in range(120):
|
||||
while True:
|
||||
x = randint(self._xMin, self._xMax)
|
||||
y = randint(self._yMin, self._yMax)
|
||||
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)
|
||||
|
@ -17,6 +17,12 @@ class Node:
|
||||
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)
|
||||
|
@ -10,8 +10,8 @@ class Drawable:
|
||||
self.__maxX = maxX
|
||||
self.__minY = minY
|
||||
self.__maxY = maxY
|
||||
self.setX(x)
|
||||
self.setY(y)
|
||||
self.__x = x
|
||||
self.__y = y
|
||||
self.__cellSize = cellSize # cell size in pixels
|
||||
self.__offset = offset # paint offset in pixels
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -5,14 +5,22 @@ 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
|
||||
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:
|
||||
@ -32,14 +40,22 @@ class ImageCache:
|
||||
ImageCache.__instance = self
|
||||
self.__images = {}
|
||||
self.__paths = {Images.Background: './images/Backgroud.png',
|
||||
Images.Waiter: './images/kelner.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.Guest3: './images/wiking_rudy2.png',
|
||||
Images.ToolTip: './images/tooltip.png'}
|
||||
|
||||
def getImage(self, imageKind, width, height):
|
||||
key = str(imageKind.value) + ':' + str(width) + ':' + str(height)
|
||||
|
@ -20,7 +20,7 @@ class TableManager (threading.Thread):
|
||||
if tables:
|
||||
tableIndex = random.randint(0, len(tables) - 1)
|
||||
table = tables[tableIndex]
|
||||
time.sleep(3)
|
||||
time.sleep(1)
|
||||
table.setStatus(Status.Ready)
|
||||
table.setOrder(self.__menuManager.generateOrder())
|
||||
self.__drawableManager.forceRepaint()
|
||||
|
@ -8,9 +8,10 @@ from kelner.src.algorithms.AStar.Finder import Finder
|
||||
# creates new thread
|
||||
class WaiterManager (threading.Thread):
|
||||
|
||||
def __init__(self, drawableManager):
|
||||
def __init__(self, drawableManager, waiters):
|
||||
super().__init__()
|
||||
self.__drawableManager = drawableManager
|
||||
self.__waiters = waiters
|
||||
self.__runThread = True
|
||||
|
||||
def __getNearestTargetPath(self, waiter):
|
||||
@ -33,25 +34,60 @@ class WaiterManager (threading.Thread):
|
||||
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:
|
||||
waiters = self.__drawableManager.getWaiters()
|
||||
if waiters:
|
||||
for waiter in waiters:
|
||||
if self.__waiters:
|
||||
for waiter in self.__waiters:
|
||||
path = self.__getNearestTargetPath(waiter)
|
||||
if path is not None:
|
||||
waiter.setPath(path)
|
||||
waiter.setPath([] if path is None else path)
|
||||
|
||||
if not waiter.isPathEmpty():
|
||||
step = waiter.popStepFromPath()
|
||||
time.sleep(0.4)
|
||||
waiter.setX(step[0])
|
||||
waiter.setY(step[1])
|
||||
self.__drawableManager.forceRepaint()
|
||||
self.__changeWaiterDirection(waiter, step[0], step[1])
|
||||
self.__moveWaiter(waiter, step[0], step[1])
|
||||
|
||||
if waiter.isPathEmpty():
|
||||
time.sleep(2)
|
||||
self.__drawableManager.collectOrders()
|
||||
self.__drawableManager.forceRepaint()
|
||||
self.__collectOrder(waiter)
|
||||
|
||||
def stop(self):
|
||||
self.__runThread = False
|
||||
|