from attributes import PackStatus from products_types import PRODUCT_TYPES import random import queue from collections import namedtuple import itertools Coordinates = namedtuple("Coordinates", 'x y') class CategoryData: def __init__(self, name, passable=False, can_store=True, location='general'): self.name = name self.passable = passable self.can_store = can_store self.location = location def __repr__(self): return self.name class Pack: def __init__(self, size=5, categ='normal', lays_on_field=None): self.size = size self.category = categ self.products_inside = self.set_products_inside() assert isinstance(lays_on_field, Tile), AssertionError("Attribute lays_on_field must be a Tile object! You know, package cannot lay in void :)") self.lays_on_field = lays_on_field self.status = self.set_status() def set_status(self): status = PackStatus.LOOSE if self.lays_on_field.category.name in ['Floor', 'FridgeFloor']: status = PackStatus.LOOSE else: status = PackStatus.STORED return status 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"]) self.category = products_category product_inside = random.choice(products_in_category) # print(product_inside) return product_inside def __repr__(self): return "Pack {} -{}, on field {}".format(self.products_inside, self.category, self.lays_on_field) CATEGORY = { 'floor': CategoryData('Floor', True, False), #lava 'rack': CategoryData('Rack', False, True), 'fridge_floor': CategoryData('FridgeFloor', True, False, location='cold_room'), 'fridge': CategoryData('Fridge', False, True, location='cold_room') } class Warehouse: def __init__(self, width, length, no_of_racks, no_of_packages=0): self.width = width self.height = length self.tiles = self.generate_map() self.no_of_racks = no_of_racks self.storage_types = ["Rack", "Fridge"] self.no_of_packages = no_of_packages self.generate_racks() self.open_isolated_areas() self.set_temperature(20, 30) self.set_humidity() self.create_fridge(8) # print([row[0].air_temperature for row in self.tiles]) self.packages = self.place_packages(no_of_packages) self.tiles[1][1] = Tile('floor', 1, 1) def __str__(self): return "Magazyn {}x{}".format(self.width, self.height) def generate_map(self): warehouse_tiles = [] categories = list(CATEGORY.keys()) for x in range(self.width): row = [Tile('floor', x, y) for y in range(self.height)] warehouse_tiles.append(row) return warehouse_tiles def generate_racks(self): q = queue.Queue() rack_counter = 0 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 self.tiles[node_x][node_y] = Tile('rack', node_x, node_y, capacity=random.randrange(15, 20), temperature=node.air_temperature, humidity=node.humidity) 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) 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 node_x = next_node.x_position node_y = next_node.y_position 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) 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)) 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": self.tiles[num][index] = Tile('fridge_floor', num, index, temperature=-5) else: self.tiles[num][index] = Tile('fridge', num, index, capacity=random.randrange(10, 12), temperature=-15) 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 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 def get_all_racks(self, all_storages=False): """:return list of Tile objects""" racks = [] for x in self.tiles: # row_racks = [t for t in self.tiles[x] if t.category.name == 'Rack'] for y in x: if all_storages: if y.category.name in self.storage_types: racks.append(y) elif y.category.name == 'Rack': racks.append(y) return racks def place_packages(self, no_of_packages: int): packages = [] for i in range(no_of_packages): new_package_size = random.randrange(1, 10) pack_x, pack_y = self._set_package_position(new_package_size) package_field = self.tiles[pack_x][pack_y] new_package = Pack(lays_on_field=package_field) new_package.size = new_package_size if package_field.category.name in self.storage_types: package_field.capacity -= new_package.size packages.append(new_package) return packages def _set_package_position(self, pack_size: int): starting_x, starting_y = random.randrange(self.width), random.randrange(self.height) while not isinstance(self.tiles[starting_x][starting_y], Tile) \ or self.tiles[starting_x][starting_y].capacity - pack_size < 0: starting_x, starting_y = random.randrange(self.width), random.randrange( self.height) return starting_x, starting_y 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 class Tile: def __init__(self, category, x_position, y_position, capacity=10, temperature: int=24, humidity: float=0.3): self.category = CATEGORY.get(category, CATEGORY['floor']) self.x_position = x_position self.y_position = y_position self.air_temperature = temperature self.humidity = humidity self.capacity = capacity def __str__(self): return "Tile type: {} on position ({},{})".format(self.category, self.x_position, self.y_position) def __repr__(self): return "Tile type: {} on position ({},{})".format(self.category, self.x_position, self.y_position)