Merge branch 'szukanie_paczek'

This commit is contained in:
andrzej 2020-05-07 17:40:32 +02:00
commit 0e0f15a86e
4 changed files with 113 additions and 47 deletions

View File

@ -1,8 +1,7 @@
from warehouse import Coordinates, Tile, Pack from warehouse import Coordinates, Tile, Pack
from queue import PriorityQueue from queue import PriorityQueue
from math import sqrt from math import sqrt
from attributes import TURN_LEFT_DIRECTIONS, TURN_RIGHT_DIRECTIONS, PackStatus, PackSize from attributes import TURN_LEFT_DIRECTIONS, TURN_RIGHT_DIRECTIONS, PackStatus
import pdb
import pygame import pygame
import sys import sys
@ -90,7 +89,16 @@ class Agent:
def heuristic(self, start: Node, goal: Node): def heuristic(self, start: Node, goal: Node):
diff_x = pow(goal.x - start.x, 2) diff_x = pow(goal.x - start.x, 2)
diff_y = pow(goal.y - start.y, 2) diff_y = pow(goal.y - start.y, 2)
return round(sqrt(diff_x + diff_y), 3) additional_cost = 0
# if diff_x < diff_y:
# column = self.warehouse.tiles[start.x]
# tiles = column[goal.y:start.y] if goal.y < start.y else column[start.y:goal.y]
# additional_cost += len([t for t in tiles if not t.category.passable])
# elif diff_x > diff_y:
# row = [col[start.x] for col in self.warehouse.tiles]
# tiles = row[goal.x:start.x] if goal.x < start.x else row[start.x:goal.x]
# additional_cost += len([t for t in tiles if not t.category.passable])
return round(sqrt(diff_x + diff_y), 3) + float(10*additional_cost)
def check_if_open(self, node: Node): def check_if_open(self, node: Node):
return (node.x, node.y) in [(n.x, n.y) for (_,n) in self.open.queue] return (node.x, node.y) in [(n.x, n.y) for (_,n) in self.open.queue]
@ -117,13 +125,6 @@ class Agent:
return return
else: else:
next = self.path.pop() next = self.path.pop()
if (next.x, next.y) == dest_coords:
if self.dest.package:
self.pick_up_package(self.dest.package)
return
elif self.dest.is_rack:
self.unload_package(self.dest)
return
star_dir = self.direction star_dir = self.direction
if self.x > next.x and not self.direction == 'left': if self.x > next.x and not self.direction == 'left':
if self.direction == 'down': if self.direction == 'down':
@ -146,6 +147,14 @@ class Agent:
else: else:
self.turn_left() self.turn_left()
if (next.x, next.y) == dest_coords:
if self.dest.package:
self.pick_up_package(self.dest.package)
return
elif self.dest.is_rack:
self.unload_package(self.dest)
return
if star_dir == self.direction: if star_dir == self.direction:
self.x = next.x self.x = next.x
self.y = next.y self.y = next.y
@ -153,13 +162,13 @@ class Agent:
self.path.append(next) self.path.append(next)
self.closed = [] self.closed = []
def check_if_can_move(self, next_coords: Coordinates, rack_searching=False): def check_if_can_move(self, next_coords: Coordinates):
tile_on_map = 0 <= next_coords.x < self.warehouse.width and 0 <= next_coords.y < self.warehouse.height tile_on_map = 0 <= next_coords.x < self.warehouse.width and 0 <= next_coords.y < self.warehouse.height
tile_passable = True tile_passable = True
if not tile_on_map: if not tile_on_map:
return False return False
next_tile = self.warehouse.tiles[next_coords.x][next_coords.y] next_tile = self.warehouse.tiles[next_coords.x][next_coords.y]
if not self.is_loaded: if not self.is_loaded or (next_coords.x, next_coords.y) != (self.dest.x, self.dest.y):
tile_passable = isinstance(next_tile, Tile) and next_tile.category.passable tile_passable = isinstance(next_tile, Tile) and next_tile.category.passable
return tile_passable return tile_passable
@ -176,14 +185,17 @@ class Agent:
if cost > 0: if cost > 0:
packages_costs.append((package, cost)) packages_costs.append((package, cost))
if not packages_costs: if not packages_costs:
pygame.quit() return
sys.exit() # pygame.quit()
# sys.exit()
package = min(packages_costs, key=lambda l: l[1])[0] package = min(packages_costs, key=lambda l: l[1])[0]
return package return package
def find_nearest_rack_for(self, package, expand_box=0): def find_nearest_rack_for(self, package, expand_box=0):
weight = package.size weight = package.size
accepted_weights = [weight, PackSize.ALL] storage = "Rack"
if package.category == "freezed":
storage = "Fridge"
start_node = Node(self.x, self.y) start_node = Node(self.x, self.y)
quarter_x = int(self.warehouse.width/4) + expand_box quarter_x = int(self.warehouse.width/4) + expand_box
quarter_y = int(self.warehouse.height/4) + expand_box quarter_y = int(self.warehouse.height/4) + expand_box
@ -192,24 +204,25 @@ class Agent:
start_quarter_y = self.y - quarter_y if self.y - quarter_y > 0 else 0 start_quarter_y = self.y - quarter_y if self.y - quarter_y > 0 else 0
end_quarter_y = self.y + quarter_y if self.y + quarter_y < self.warehouse.height else self.warehouse.height - 1 end_quarter_y = self.y + quarter_y if self.y + quarter_y < self.warehouse.height else self.warehouse.height - 1
quarter = [row[start_quarter_y:end_quarter_y] for row in self.warehouse.tiles[start_quarter_x:end_quarter_x]] quarter = [row[start_quarter_y:end_quarter_y] for row in self.warehouse.tiles[start_quarter_x:end_quarter_x]]
quarter_racks = [[t for t in row if t.category.name == "Rack" and not t.occupied and t.category.pack_size in accepted_weights] for row in quarter] quarter_racks = [[t for t in row if t.category.name == storage and t.capacity >= weight] for row in quarter]
quarter_racks = [t for row in quarter_racks for t in row] quarter_racks = [t for row in quarter_racks for t in row]
racks_costs = [] racks_costs = []
if not quarter_racks:
self.find_nearest_rack_for(package, expand_box+1)
for rack in quarter_racks: for rack in quarter_racks:
new_node = Node(rack.x_position, rack.y_position) new_node = Node(rack.x_position, rack.y_position)
cost = self.heuristic(start_node, new_node) cost = self.heuristic(start_node, new_node)
if cost > 0: if cost > 0:
racks_costs.append((rack, cost)) racks_costs.append((rack, cost))
rack = min(racks_costs, key=lambda l: l[1])[0]
rack = self.find_nearest_rack_for(package, expand_box + 1) if not racks_costs else min(racks_costs, key=lambda l: l[1])[0]
return rack return rack
def pick_up_package(self, pack): def pick_up_package(self, pack):
self.warehouse.packages.remove(pack) self.warehouse.packages.remove(pack)
self.is_loaded = True self.is_loaded = True
self.warehouse.tiles[pack.lays_on_field.x_position][pack.lays_on_field.y_position].occupied = False if pack.lays_on_field.category.name in ['Rack', 'Fridge']:
pack.lays_on_field.capacity += pack.size
self.dest.package = None self.dest.package = None
pack.lays_on_field = None pack.lays_on_field = None
self.transported_package = pack self.transported_package = pack
@ -220,6 +233,6 @@ class Agent:
self.transported_package = None self.transported_package = None
self.is_loaded = False self.is_loaded = False
pack.lays_on_field = tile pack.lays_on_field = tile
self.warehouse.tiles[rack.x][rack.y].occupied = True pack.lays_on_field.capacity -= pack.size
pack.status = PackStatus.STORED pack.status = PackStatus.STORED
self.warehouse.packages.append(pack) self.warehouse.packages.append(pack)

View File

@ -1,11 +1,5 @@
from enum import Enum from enum import Enum
class PackSize(Enum):
ALL = 0
SMALL = 1
MEDIUM = 2
LARGE = 3
HUGE = 4
class PackStatus(Enum): class PackStatus(Enum):
LOOSE = 0 LOOSE = 0
@ -25,7 +19,9 @@ COLORS = {
'yellow': (235, 235, 0), 'yellow': (235, 235, 0),
'lightgreen': (70, 238, 70), 'lightgreen': (70, 238, 70),
'red': (255, 0, 0), 'red': (255, 0, 0),
'lightblue': (135, 206, 250), 'lightblue': (120, 180, 230),
'iceblue': (186, 242, 239),
'blue': (0, 0, 255),
'orange': (255, 165, 0) 'orange': (255, 165, 0)
} }

25
main.py
View File

@ -3,14 +3,16 @@ import warehouse
import agent import agent
import random import random
import sys import sys
from attributes import PackSize, PackStatus, COLORS, DIRECTION_ANGLES from attributes import PackStatus, COLORS, DIRECTION_ANGLES
WINDOW_SIZE = (640, 640) WINDOW_SIZE = (640, 640)
COLOR_OF_FIELD = { COLOR_OF_FIELD = {
'Floor': 'gray', 'Floor': 'gray',
'Rack': 'white', 'Rack': 'white',
'Pack': 'yellow', 'Pack': 'yellow',
'path': 'orange' 'path': 'orange',
'FridgeFloor': 'lightblue',
'Fridge': 'iceblue'
} }
TILE_WIDTH = 32 TILE_WIDTH = 32
TILE_HEIGHT = 32 TILE_HEIGHT = 32
@ -18,10 +20,13 @@ CIRCLE_CENTER_X, CIRCLE_CENTER_Y = int(TILE_WIDTH/2), int(TILE_HEIGHT/2)
class MainGameFrame: class MainGameFrame:
def __init__(self): def __init__(self):
pygame.font.init()
self.display = pygame.display.set_mode(WINDOW_SIZE) self.display = pygame.display.set_mode(WINDOW_SIZE)
pygame.display.set_caption("Smart ForkLift")
agent_radius = int(TILE_WIDTH/2) agent_radius = int(TILE_WIDTH/2)
self.agent_tex = pygame.image.load('forklift.png') self.agent_tex = pygame.image.load('forklift.png')
self.warehouse_map = warehouse.Warehouse(20, 20, 150, 10) self.font = pygame.font.Font('freesansbold.ttf', 16)
self.warehouse_map = warehouse.Warehouse(20, 20, 150, 20)
starting_x, starting_y = self.set_starting_agent_position() starting_x, starting_y = self.set_starting_agent_position()
self.agent = agent.Agent(starting_x, starting_y, self.warehouse_map, agent_radius) self.agent = agent.Agent(starting_x, starting_y, self.warehouse_map, agent_radius)
self.clock = pygame.time.Clock() self.clock = pygame.time.Clock()
@ -35,6 +40,7 @@ class MainGameFrame:
self.draw_floor() self.draw_floor()
self.draw_packages() self.draw_packages()
self.draw_agent() self.draw_agent()
self.draw_nums()
self.agent.move() self.agent.move()
pygame.display.update() pygame.display.update()
self.clock.tick(5) self.clock.tick(5)
@ -52,8 +58,6 @@ class MainGameFrame:
def draw_field(self, x, y): def draw_field(self, x, y):
current_tile = self.warehouse_map.tiles[x][y] current_tile = self.warehouse_map.tiles[x][y]
# if not isinstance(current_tile, warehouse.Tile):
# current_tile = current_tile.lays_on_field if isinstance(current_tile, warehouse.Pack) else None
color = COLOR_OF_FIELD.get(current_tile.category.name, 'white') color = COLOR_OF_FIELD.get(current_tile.category.name, 'white')
color = COLORS[color] color = COLORS[color]
if (current_tile.x_position,current_tile.y_position) in [(a.x, a.y) for a in self.agent.path]: if (current_tile.x_position,current_tile.y_position) in [(a.x, a.y) for a in self.agent.path]:
@ -77,6 +81,17 @@ class MainGameFrame:
package_color = get_package_color(pack) package_color = get_package_color(pack)
pygame.draw.rect(self.display, package_color, pygame.draw.rect(self.display, package_color,
((pack_x * TILE_WIDTH) + 3, (pack_y * TILE_HEIGHT) + 3, TILE_WIDTH - 5, TILE_HEIGHT - 5)) ((pack_x * TILE_WIDTH) + 3, (pack_y * TILE_HEIGHT) + 3, TILE_WIDTH - 5, TILE_HEIGHT - 5))
if pack.category == "freezed":
pygame.draw.rect(self.display, COLORS['blue'],
((pack_x * TILE_WIDTH) + 2, (pack_y * TILE_HEIGHT) + 2, TILE_WIDTH - 4,
TILE_HEIGHT - 4), 3)
def draw_nums(self):
for row in self.warehouse_map.tiles:
for cell in row:
if cell.category.name in self.warehouse_map.storage_types:
text_surface = self.font.render(str(cell.capacity), True, (0, 0, 0))
self.display.blit(text_surface, ((cell.x_position * TILE_WIDTH) + 6, (cell.y_position * TILE_HEIGHT) + 6))
def draw_agent(self): def draw_agent(self):
rotated = pygame.transform.rotate(self.agent_tex, DIRECTION_ANGLES.get(self.agent.direction)) rotated = pygame.transform.rotate(self.agent_tex, DIRECTION_ANGLES.get(self.agent.direction))

View File

@ -1,4 +1,4 @@
from attributes import PackSize, PackStatus from attributes import PackStatus
import random import random
import queue import queue
from collections import namedtuple from collections import namedtuple
@ -7,18 +7,17 @@ import itertools
Coordinates = namedtuple("Coordinates",'x y') Coordinates = namedtuple("Coordinates",'x y')
class CategoryData: class CategoryData:
def __init__(self, name, passable=False, can_store=True, location='general', pack_size=PackSize.ALL): def __init__(self, name, passable=False, can_store=True, location='general'):
self.name = name self.name = name
self.passable = passable self.passable = passable
self.can_store = can_store self.can_store = can_store
self.location = location self.location = location
self.pack_size = pack_size
def __repr__(self): def __repr__(self):
return self.name return self.name
class Pack: class Pack:
def __init__(self, size=PackSize.MEDIUM, categ='general', lays_on_field=None): def __init__(self, size=5, categ='general', lays_on_field=None):
self.size = size self.size = size
self.category = categ 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 :)") assert isinstance(lays_on_field, Tile), AssertionError("Attribute lays_on_field must be a Tile object! You know, package cannot lay in void :)")
@ -27,16 +26,20 @@ class Pack:
def set_status(self): def set_status(self):
status = PackStatus.LOOSE status = PackStatus.LOOSE
if self.lays_on_field.category.name == 'Floor': if self.lays_on_field.category.name in ['Floor', 'FridgeFloor']:
status = PackStatus.LOOSE status = PackStatus.LOOSE
elif self.lays_on_field.category.name == 'Rack': elif self.lays_on_field.category.name in ['Rack', 'Fridge']:
if self.category == 'freezed' and self.lays_on_field.category.name != 'Fridge':
status = PackStatus.STORED_BAD_LOCATION
else:
status = PackStatus.STORED status = PackStatus.STORED
return status return status
CATEGORY = { CATEGORY = {
'floor': CategoryData('Floor', True, False), #lava 'floor': CategoryData('Floor', True, False), #lava
'rack': CategoryData('Rack', False, True), 'rack': CategoryData('Rack', False, True),
# 'freezer': CategoryData('Freezer', False, True, location='cold_room') 'fridge_floor': CategoryData('FridgeFloor', True, False, location='cold_room'),
'fridge': CategoryData('Fridge', False, True, location='cold_room')
} }
@ -46,9 +49,11 @@ class Warehouse:
self.height = length self.height = length
self.tiles = self.generate_map() self.tiles = self.generate_map()
self.no_of_racks = no_of_racks self.no_of_racks = no_of_racks
self.storage_types = ["Rack", "Fridge"]
self.no_of_packages = no_of_packages self.no_of_packages = no_of_packages
self.generate_racks() self.generate_racks()
self.open_isolated_areas() self.open_isolated_areas()
self.create_fridge(10)
self.packages = self.place_packages(no_of_packages) self.packages = self.place_packages(no_of_packages)
self.tiles[1][1] = Tile('floor', 1, 1) self.tiles[1][1] = Tile('floor', 1, 1)
def __str__(self): def __str__(self):
@ -68,7 +73,7 @@ class Warehouse:
node_x, node_y = random.randrange(1, self.width-1), random.randrange(1, self.height-1) node_x, node_y = random.randrange(1, self.width-1), random.randrange(1, self.height-1)
node = self.tiles[node_x][node_y] node = self.tiles[node_x][node_y]
next_node = None next_node = None
self.tiles[node_x][node_y] = Tile('rack', node_x, node_y) self.tiles[node_x][node_y] = Tile('rack', node_x, node_y, capacity=random.randrange(16, 20))
q.put(node) q.put(node)
while not q.empty(): while not q.empty():
if next_node is not None: if next_node is not None:
@ -92,6 +97,37 @@ class Warehouse:
node_y = next_node.y_position node_y = next_node.y_position
self.tiles[node_x][node_y] = Tile('rack', node_x, node_y) self.tiles[node_x][node_y] = Tile('rack', node_x, node_y)
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:
# import pdb
# pdb.set_trace()
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)
else:
self.tiles[num][index] = Tile('fridge', num, index, capacity=random.randrange(10, 12))
def get_not_rack_nodes(self, node_x, node_y): def get_not_rack_nodes(self, node_x, node_y):
adjacent_tiles = self.get_adjacent_tiles(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"] possible_nodes = [line for line in adjacent_tiles if line.category.name != "Rack"]
@ -161,8 +197,14 @@ class Warehouse:
for i in range(no_of_packages): for i in range(no_of_packages):
pack_x, pack_y = self._set_package_position() pack_x, pack_y = self._set_package_position()
package_field = self.tiles[pack_x][pack_y] package_field = self.tiles[pack_x][pack_y]
self.tiles[pack_x][pack_y].occupied = True
new_package = Pack(lays_on_field=package_field) new_package = Pack(lays_on_field=package_field)
categ_seed = random.random()
if categ_seed > 0.8:
new_package.category = "freezed"
new_package.set_status()
new_package.size = random.randrange(1, 10)
if package_field.category.name in self.storage_types:
package_field.capacity -= new_package.size
packages.append(new_package) packages.append(new_package)
return packages return packages
def _set_package_position(self): def _set_package_position(self):
@ -203,11 +245,11 @@ class Warehouse:
return wall return wall
class Tile: class Tile:
def __init__(self, category, x_position, y_position): def __init__(self, category, x_position, y_position, capacity=10):
self.category = CATEGORY.get(category, CATEGORY['floor']) self.category = CATEGORY.get(category, CATEGORY['floor'])
self.x_position = x_position self.x_position = x_position
self.y_position = y_position self.y_position = y_position
self.occupied = False self.capacity = capacity
def __str__(self): def __str__(self):