from attributes import PackSize, PackStatus 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', pack_size=PackSize.ALL): self.name = name self.passable = passable self.can_store = can_store self.location = location self.pack_size = pack_size def __repr__(self): return self.name class Pack: def __init__(self, size=PackSize.MEDIUM, categ='general', lays_on_field=None): self.size = size self.category = categ 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 == 'Floor': status = PackStatus.LOOSE elif self.lays_on_field.category.name == 'Rack': status = PackStatus.STORED return status CATEGORY = { 'floor': CategoryData('Floor', True, False), #lava 'rack': CategoryData('Rack', False, True), # 'freezer': CategoryData('Freezer', 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.no_of_packages = no_of_packages self.generate_racks() self.open_isolated_areas() self.packages = self.place_packages(no_of_packages) 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) 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) 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): """: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 y.category.name == 'Rack': racks.append(y) return racks def place_packages(self, no_of_packages): packages = [] for i in range(no_of_packages): pack_x, pack_y = self._set_package_position() package_field = self.tiles[pack_x][pack_y] new_package = Pack(lays_on_field=package_field) self.tiles[pack_x][pack_y] = new_package packages.append(new_package) return packages def _set_package_position(self): starting_x, starting_y = random.randrange(self.width), random.randrange(self.height) while not isinstance(self.tiles[starting_x][starting_y], Tile): 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): self.category = CATEGORY.get(category, CATEGORY['floor']) self.x_position = x_position self.y_position = y_position 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)