217 lines
8.4 KiB
Python
217 lines
8.4 KiB
Python
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)
|
|
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)
|
|
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]
|
|
self.tiles[pack_x][pack_y].occupied = True
|
|
new_package = Pack(lays_on_field=package_field)
|
|
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
|
|
self.occupied = False
|
|
|
|
|
|
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) |