2020-05-03 18:01:23 +02:00
|
|
|
from attributes import PackStatus
|
2020-05-09 14:01:48 +02:00
|
|
|
from products_types import PRODUCT_TYPES
|
2020-04-02 15:25:15 +02:00
|
|
|
import random
|
2020-04-04 22:03:15 +02:00
|
|
|
import queue
|
2020-04-05 16:49:45 +02:00
|
|
|
from collections import namedtuple
|
2020-04-04 22:03:15 +02:00
|
|
|
import itertools
|
2020-04-05 15:04:38 +02:00
|
|
|
|
2020-05-10 14:31:00 +02:00
|
|
|
Coordinates = namedtuple("Coordinates", 'x y')
|
2020-04-05 16:49:45 +02:00
|
|
|
|
2020-04-02 15:25:15 +02:00
|
|
|
class CategoryData:
|
2020-05-03 18:01:23 +02:00
|
|
|
def __init__(self, name, passable=False, can_store=True, location='general'):
|
2020-04-02 15:25:15 +02:00
|
|
|
self.name = name
|
|
|
|
self.passable = passable
|
|
|
|
self.can_store = can_store
|
|
|
|
self.location = location
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return self.name
|
|
|
|
|
|
|
|
class Pack:
|
2020-05-10 14:31:00 +02:00
|
|
|
def __init__(self, size=5, categ='normal', lays_on_field=None):
|
2020-04-02 15:25:15 +02:00
|
|
|
self.size = size
|
|
|
|
self.category = categ
|
2020-05-09 14:01:48 +02:00
|
|
|
self.products_inside = self.set_products_inside()
|
|
|
|
|
2020-04-05 15:26:28 +02:00
|
|
|
assert isinstance(lays_on_field, Tile), AssertionError("Attribute lays_on_field must be a Tile object! You know, package cannot lay in void :)")
|
2020-04-05 15:04:38 +02:00
|
|
|
self.lays_on_field = lays_on_field
|
2020-04-05 15:26:28 +02:00
|
|
|
self.status = self.set_status()
|
2020-04-02 15:25:15 +02:00
|
|
|
|
2020-04-05 15:26:28 +02:00
|
|
|
def set_status(self):
|
|
|
|
status = PackStatus.LOOSE
|
2020-05-03 18:01:23 +02:00
|
|
|
if self.lays_on_field.category.name in ['Floor', 'FridgeFloor']:
|
2020-04-05 15:26:28 +02:00
|
|
|
status = PackStatus.LOOSE
|
2020-05-10 14:31:00 +02:00
|
|
|
else:
|
|
|
|
status = PackStatus.STORED
|
2020-04-05 15:26:28 +02:00
|
|
|
return status
|
2020-04-02 15:25:15 +02:00
|
|
|
|
2020-05-09 14:01:48 +02:00
|
|
|
def set_products_inside(self):
|
|
|
|
seed = random.random()
|
|
|
|
products_category = None
|
|
|
|
if seed < 0.6:
|
|
|
|
products_category = "normal"
|
|
|
|
elif 0.6 <= seed < 0.75:
|
|
|
|
products_category = "keep_dry"
|
|
|
|
elif 0.75 <= seed < 0.9:
|
|
|
|
products_category = "fragile"
|
|
|
|
elif 0.9 <= seed < 0.98:
|
|
|
|
products_category = "freezed"
|
|
|
|
elif 0.96 <= seed <= 1.0:
|
|
|
|
products_category = "flammable"
|
|
|
|
|
|
|
|
products_in_category = PRODUCT_TYPES.get(products_category, ["food"])
|
2020-05-10 14:31:00 +02:00
|
|
|
self.category = products_category
|
2020-05-09 14:01:48 +02:00
|
|
|
product_inside = random.choice(products_in_category)
|
2020-05-10 14:31:00 +02:00
|
|
|
# print(product_inside)
|
2020-05-09 14:01:48 +02:00
|
|
|
return product_inside
|
|
|
|
|
2020-05-10 14:31:00 +02:00
|
|
|
def __repr__(self):
|
|
|
|
return "Pack {} -{}, on field {}".format(self.products_inside, self.category, self.lays_on_field)
|
2020-05-09 14:01:48 +02:00
|
|
|
|
2020-04-02 15:25:15 +02:00
|
|
|
CATEGORY = {
|
|
|
|
'floor': CategoryData('Floor', True, False), #lava
|
|
|
|
'rack': CategoryData('Rack', False, True),
|
2020-05-03 18:01:23 +02:00
|
|
|
'fridge_floor': CategoryData('FridgeFloor', True, False, location='cold_room'),
|
|
|
|
'fridge': CategoryData('Fridge', False, True, location='cold_room')
|
2020-04-02 15:25:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class Warehouse:
|
2020-04-05 15:04:38 +02:00
|
|
|
def __init__(self, width, length, no_of_racks, no_of_packages=0):
|
2020-04-02 15:25:15 +02:00
|
|
|
self.width = width
|
2020-04-04 22:03:15 +02:00
|
|
|
self.height = length
|
2020-04-02 15:25:15 +02:00
|
|
|
self.tiles = self.generate_map()
|
2020-04-05 15:04:38 +02:00
|
|
|
self.no_of_racks = no_of_racks
|
2020-05-03 18:01:23 +02:00
|
|
|
self.storage_types = ["Rack", "Fridge"]
|
2020-04-05 15:04:38 +02:00
|
|
|
self.no_of_packages = no_of_packages
|
2020-04-04 22:03:15 +02:00
|
|
|
self.generate_racks()
|
2020-04-05 16:49:45 +02:00
|
|
|
self.open_isolated_areas()
|
2020-05-09 14:01:48 +02:00
|
|
|
self.set_temperature(20, 30)
|
|
|
|
self.set_humidity()
|
2020-05-10 14:31:00 +02:00
|
|
|
self.create_fridge(8)
|
|
|
|
# print([row[0].air_temperature for row in self.tiles])
|
2020-04-05 15:04:38 +02:00
|
|
|
self.packages = self.place_packages(no_of_packages)
|
2020-05-11 14:54:33 +02:00
|
|
|
self.tiles[1][1] = Tile('floor', 1, 1)
|
2020-04-02 15:25:15 +02:00
|
|
|
def __str__(self):
|
2020-04-04 22:03:15 +02:00
|
|
|
return "Magazyn {}x{}".format(self.width, self.height)
|
2020-04-02 15:25:15 +02:00
|
|
|
|
|
|
|
def generate_map(self):
|
|
|
|
warehouse_tiles = []
|
|
|
|
categories = list(CATEGORY.keys())
|
|
|
|
for x in range(self.width):
|
2020-04-04 22:03:15 +02:00
|
|
|
row = [Tile('floor', x, y) for y in range(self.height)]
|
2020-04-02 15:25:15 +02:00
|
|
|
warehouse_tiles.append(row)
|
|
|
|
return warehouse_tiles
|
|
|
|
|
2020-04-04 22:03:15 +02:00
|
|
|
def generate_racks(self):
|
|
|
|
q = queue.Queue()
|
2020-04-05 15:04:38 +02:00
|
|
|
rack_counter = 0
|
2020-04-04 22:03:15 +02:00
|
|
|
node_x, node_y = random.randrange(1, self.width-1), random.randrange(1, self.height-1)
|
|
|
|
node = self.tiles[node_x][node_y]
|
|
|
|
next_node = None
|
2020-05-10 14:31:00 +02:00
|
|
|
self.tiles[node_x][node_y] = Tile('rack', node_x, node_y, capacity=random.randrange(15, 20), temperature=node.air_temperature, humidity=node.humidity)
|
2020-04-04 22:03:15 +02:00
|
|
|
q.put(node)
|
|
|
|
while not q.empty():
|
|
|
|
if next_node is not None:
|
|
|
|
q.put(next_node)
|
|
|
|
not_rack_nodes = self.get_not_rack_nodes(node_x, node_y)
|
|
|
|
if len(not_rack_nodes) == 0:
|
|
|
|
next_node = q.get()
|
|
|
|
while len(self.get_not_rack_nodes(next_node.x_position, next_node.y_position)) == 0:
|
|
|
|
if q.empty():
|
|
|
|
return
|
|
|
|
next_node = q.get()
|
|
|
|
|
|
|
|
else:
|
|
|
|
next_node = random.choice(not_rack_nodes)
|
2020-04-05 15:04:38 +02:00
|
|
|
current_node = next_node if next_node is not None else node
|
|
|
|
self.break_wall(next_node, current_node)
|
|
|
|
rack_counter += 1
|
|
|
|
if rack_counter > self.no_of_racks:
|
|
|
|
return
|
2020-04-04 22:03:15 +02:00
|
|
|
node_x = next_node.x_position
|
|
|
|
node_y = next_node.y_position
|
2020-05-10 14:31:00 +02:00
|
|
|
self.tiles[node_x][node_y] = Tile('rack', node_x, node_y, capacity=random.randrange(15, 20), temperature=next_node.air_temperature, humidity=next_node.humidity)
|
2020-04-04 22:03:15 +02:00
|
|
|
|
2020-05-09 14:01:48 +02:00
|
|
|
def set_temperature(self, min_temperature=20, max_temperature=30):
|
|
|
|
for num, row in enumerate(self.tiles):
|
|
|
|
row_temperature = min_temperature + round(num/2)
|
|
|
|
for cell in row:
|
|
|
|
cell.air_temperature = row_temperature
|
|
|
|
|
|
|
|
def set_humidity(self, min_humidity=0.2, max_humidity=0.7):
|
|
|
|
current_humidity = min_humidity
|
|
|
|
for y in range(self.height):
|
|
|
|
current_humidity += (0.1/4)
|
|
|
|
for x in range(self.width):
|
|
|
|
self.tiles[x][y].humidity = round(current_humidity, 1)
|
|
|
|
# print(round(current_humidity, 1))
|
|
|
|
|
|
|
|
|
2020-05-03 18:01:23 +02:00
|
|
|
def create_fridge(self, size):
|
|
|
|
x_corner = random.choice(['left', 'right'])
|
|
|
|
y_corner = random.choice(['top', 'bottom'])
|
|
|
|
start_x = None
|
|
|
|
start_y = None
|
|
|
|
end_x = None
|
|
|
|
end_y = None
|
|
|
|
if x_corner == 'left':
|
|
|
|
start_x = 0
|
|
|
|
end_x = size
|
|
|
|
else:
|
|
|
|
start_x = (self.width-1) - size
|
|
|
|
end_x = self.width - 1
|
|
|
|
|
|
|
|
if y_corner == 'top':
|
|
|
|
start_y = 0
|
|
|
|
end_y = size
|
|
|
|
else:
|
|
|
|
start_y = (self.height-1) - size
|
|
|
|
end_y = self.height - 1
|
|
|
|
|
|
|
|
rows = self.tiles[start_x:end_x].copy()
|
|
|
|
for num, row in enumerate(rows, start_x):
|
|
|
|
for index, tile in enumerate(row[start_y:end_y], start_y):
|
|
|
|
if self.tiles[num][index].category.name == "Floor":
|
2020-05-09 14:01:48 +02:00
|
|
|
self.tiles[num][index] = Tile('fridge_floor', num, index, temperature=-5)
|
2020-05-03 18:01:23 +02:00
|
|
|
else:
|
2020-05-09 14:01:48 +02:00
|
|
|
self.tiles[num][index] = Tile('fridge', num, index, capacity=random.randrange(10, 12), temperature=-15)
|
2020-05-03 18:01:23 +02:00
|
|
|
|
2020-04-04 22:03:15 +02:00
|
|
|
def get_not_rack_nodes(self, node_x, node_y):
|
|
|
|
adjacent_tiles = self.get_adjacent_tiles(node_x, node_y)
|
|
|
|
possible_nodes = [line for line in adjacent_tiles if line.category.name != "Rack"]
|
|
|
|
return possible_nodes
|
2020-04-02 15:25:15 +02:00
|
|
|
|
2020-04-05 16:49:45 +02:00
|
|
|
def open_isolated_areas(self):
|
|
|
|
racks = self.get_all_racks()
|
|
|
|
for rack in racks:
|
|
|
|
isolated = self.check_if_isolated(rack.x_position, rack.y_position)
|
|
|
|
if isolated:
|
|
|
|
self.tiles[rack.x_position][rack.y_position] = Tile('floor', rack.x_position, rack.y_position)
|
|
|
|
tmp_tiles = self.tiles
|
|
|
|
for row_number, row_tiles in enumerate(self.tiles):
|
|
|
|
if all([(t.category.name == 'Rack') for t in row_tiles]):
|
|
|
|
puncture_point = random.randrange(1, self.width-1)
|
|
|
|
tmp_tiles[row_number][puncture_point] = Tile('floor', row_number, puncture_point)
|
|
|
|
for column in range(self.height):
|
|
|
|
column_tiles = [r[column] for r in self.tiles]
|
|
|
|
if all([(t.category.name == 'Rack') for t in column_tiles]):
|
|
|
|
puncture_point = random.randrange(1, self.height-1)
|
|
|
|
tmp_tiles[puncture_point][column] = Tile('floor', row_number, puncture_point)
|
|
|
|
self.files = tmp_tiles
|
|
|
|
|
|
|
|
|
|
|
|
def check_if_isolated(self, x, y):
|
|
|
|
diagonals = self.get_diagonals(x,y)
|
|
|
|
diagonal_racks = [t for t in diagonals if t.category.name == 'Rack']
|
|
|
|
return len(diagonal_racks) > 0
|
|
|
|
|
|
|
|
def get_diagonals(self, x, y):
|
|
|
|
upper_right = Coordinates(
|
|
|
|
x + 1 if x + 1 < self.width else self.width - 1,
|
|
|
|
y + 1 if y + 1 < self.height else self.height - 1
|
|
|
|
)
|
|
|
|
upper_left = Coordinates(
|
|
|
|
x - 1 if x - 1 > 0 else 0,
|
|
|
|
y + 1 if y + 1 < self.height else self.height - 1
|
|
|
|
)
|
|
|
|
bottom_right = Coordinates(
|
|
|
|
x + 1 if x + 1 < self.width else self.width - 1,
|
|
|
|
y - 1 if y - 1 > 0 else 0
|
|
|
|
)
|
|
|
|
bottom_left = Coordinates(
|
|
|
|
x - 1 if x - 1 > 0 else 0,
|
|
|
|
y - 1 if y - 1 > 0 else 0
|
|
|
|
)
|
|
|
|
diagonals = [
|
|
|
|
self.tiles[upper_right.x][upper_right.y],
|
|
|
|
self.tiles[upper_left.x][upper_left.y],
|
|
|
|
self.tiles[bottom_right.x][bottom_right.y],
|
|
|
|
self.tiles[bottom_left.x][bottom_left.y]
|
|
|
|
]
|
|
|
|
return diagonals
|
2020-05-09 14:01:48 +02:00
|
|
|
|
2020-05-11 12:17:58 +02:00
|
|
|
def get_all_racks(self, all_storages=False):
|
2020-04-05 16:49:45 +02:00
|
|
|
""":return list of Tile objects"""
|
|
|
|
racks = []
|
2020-05-11 12:17:58 +02:00
|
|
|
for x in self.tiles:
|
2020-04-05 16:49:45 +02:00
|
|
|
# row_racks = [t for t in self.tiles[x] if t.category.name == 'Rack']
|
|
|
|
for y in x:
|
2020-05-11 12:17:58 +02:00
|
|
|
if all_storages:
|
|
|
|
if y.category.name in self.storage_types:
|
|
|
|
racks.append(y)
|
|
|
|
elif y.category.name == 'Rack':
|
2020-04-05 16:49:45 +02:00
|
|
|
racks.append(y)
|
|
|
|
return racks
|
|
|
|
|
2020-05-07 21:35:24 +02:00
|
|
|
def place_packages(self, no_of_packages: int):
|
2020-04-05 15:04:38 +02:00
|
|
|
packages = []
|
|
|
|
for i in range(no_of_packages):
|
2020-05-07 21:35:24 +02:00
|
|
|
new_package_size = random.randrange(1, 10)
|
|
|
|
pack_x, pack_y = self._set_package_position(new_package_size)
|
2020-04-05 15:04:38 +02:00
|
|
|
package_field = self.tiles[pack_x][pack_y]
|
|
|
|
new_package = Pack(lays_on_field=package_field)
|
2020-05-07 21:35:24 +02:00
|
|
|
new_package.size = new_package_size
|
2020-06-15 03:17:57 +02:00
|
|
|
new_package.id = i
|
|
|
|
new_package.x = pack_x
|
|
|
|
new_package.y = pack_y
|
2020-05-03 18:01:23 +02:00
|
|
|
if package_field.category.name in self.storage_types:
|
|
|
|
package_field.capacity -= new_package.size
|
2020-04-05 15:04:38 +02:00
|
|
|
packages.append(new_package)
|
|
|
|
return packages
|
2020-05-07 21:35:24 +02:00
|
|
|
|
|
|
|
def _set_package_position(self, pack_size: int):
|
2020-04-05 15:04:38 +02:00
|
|
|
starting_x, starting_y = random.randrange(self.width), random.randrange(self.height)
|
2020-05-07 21:35:24 +02:00
|
|
|
while not isinstance(self.tiles[starting_x][starting_y], Tile) \
|
2020-05-10 14:31:00 +02:00
|
|
|
or self.tiles[starting_x][starting_y].capacity - pack_size < 0:
|
2020-04-05 15:04:38 +02:00
|
|
|
starting_x, starting_y = random.randrange(self.width), random.randrange(
|
|
|
|
self.height)
|
|
|
|
return starting_x, starting_y
|
2020-04-02 15:25:15 +02:00
|
|
|
|
2020-04-04 22:03:15 +02:00
|
|
|
def get_adjacent_tiles(self, x, y):
|
|
|
|
x_start = x - 2 if x - 2 >= 0 else 0
|
|
|
|
x_stop = x + 2 if x + 2 < self.width else self.width-1
|
|
|
|
y_start = y - 2 if y - 2 >= 0 else 0
|
|
|
|
y_stop = y + 2 if y + 2 < self.height else self.height - 1
|
|
|
|
adjacent_tiles = [self.tiles[x_start][y], self.tiles[x_stop][y], self.tiles[x][y_start], self.tiles[x][y_stop]]
|
|
|
|
return adjacent_tiles
|
|
|
|
|
|
|
|
def break_wall(self, next_node, current_node):
|
|
|
|
wall = self.get_wall(next_node, current_node)
|
|
|
|
self.tiles[wall.x_position][wall.y_position] = Tile("rack", wall.x_position, wall.y_position)
|
|
|
|
|
|
|
|
def get_wall(self, next_node, current_node):
|
|
|
|
x_relative = 0
|
|
|
|
y_relative = 0
|
|
|
|
|
|
|
|
if next_node.x_position > current_node.x_position:
|
|
|
|
x_relative = 1
|
|
|
|
elif next_node.x_position < current_node.x_position:
|
|
|
|
x_relative = -1
|
|
|
|
|
|
|
|
if next_node.y_position > current_node.y_position:
|
|
|
|
y_relative = 1
|
|
|
|
elif next_node.y_position < current_node.y_position:
|
|
|
|
y_relative = -1
|
|
|
|
wall_x = current_node.x_position + x_relative
|
|
|
|
wall_y = current_node.y_position + y_relative
|
|
|
|
wall = self.tiles[wall_x][wall_y]
|
|
|
|
return wall
|
2020-04-05 15:04:38 +02:00
|
|
|
|
2020-04-02 15:25:15 +02:00
|
|
|
class Tile:
|
2020-05-09 14:01:48 +02:00
|
|
|
def __init__(self, category, x_position, y_position, capacity=10, temperature: int=24, humidity: float=0.3):
|
2020-04-02 15:25:15 +02:00
|
|
|
self.category = CATEGORY.get(category, CATEGORY['floor'])
|
|
|
|
self.x_position = x_position
|
|
|
|
self.y_position = y_position
|
2020-05-09 14:01:48 +02:00
|
|
|
self.air_temperature = temperature
|
|
|
|
self.humidity = humidity
|
2020-05-03 18:01:23 +02:00
|
|
|
self.capacity = capacity
|
2020-04-02 15:25:15 +02:00
|
|
|
|
2020-04-04 22:03:15 +02:00
|
|
|
|
2020-04-02 15:25:15 +02:00
|
|
|
def __str__(self):
|
|
|
|
return "Tile type: {} on position ({},{})".format(self.category, self.x_position, self.y_position)
|
|
|
|
|
|
|
|
def __repr__(self):
|
2020-05-11 14:54:33 +02:00
|
|
|
return "Tile type: {} on position ({},{})".format(self.category, self.x_position, self.y_position)
|