This repository has been archived on 2019-08-08. You can view files and clone it, but cannot push or open issues or pull requests.
hashcode2016/reborn.py

334 lines
8.7 KiB
Python
Executable file

#!/usr/bin/env python3
import sys
import math
import copy
import progressbar
DEBUG = False
outLines = []
def log(*data):
if DEBUG:
print(*data)
def output(*values):
global outLines
outLines.append(' '.join([str(val) for val in values]))
def distance(A, B):
return math.ceil(math.sqrt(pow(B[0] - A[0], 2) + pow(B[1] - A[1], 2)))
class Product:
ALL = []
def __init__(self, weight):
self.id = len(self.ALL)
self.ALL.append(self)
self.weight = weight
def get(pid):
return __class__.ALL[pid]
def len():
return len(__class__.ALL)
class Warehouse:
ALL = []
def __init__(self, pos, items):
self.id = len(self.ALL)
self.ALL.append(self)
self.pos = pos
self.items = items
self.plannedItems = self.items.copy()
def plan(self, items):
for i in items:
self.plannedItems.remove(i)
# Set functions
def near(pos):
couples = []
for el in __class__.ALL:
couples.append([el, distance(el.pos, pos)])
return [couple[0] for couple in sorted(couples, key=lambda c: c[1])]
def get(pid):
return __class__.ALL[pid]
def len():
return len(__class__.ALL)
class Client:
ALL = []
def __init__(self, pos, needs):
self.id = len(self.ALL)
self.ALL.append(self)
self.pos = pos
self.needs = needs
self.plannedNeeds = self.needs.copy()
def plan(self, needs):
for n in needs:
self.plannedNeeds.remove(n)
def satisfied(self):
return len(self.needs) == 0
def pack(self, payload=-1):
# TODO Optimise for same product wanted more than once
if payload == -1:
payload = Drone.PAYLOAD
p = []
load = 0
# Sort needs by weight
couples = [(i, Product.get(i).weight) for i in self.plannedNeeds]
couples.sort(key=lambda c: c[1])
for couple in couples:
need, weight = couple
if load + weight <= payload:
p.append(need)
load += weight
return p
# Set functions
def near(pos):
couples = []
for el in __class__.ALL:
couples.append([el, distance(el.pos, pos)])
return [couple[0] for couple in sorted(couples, key=lambda c: c[1])]
def get(pid):
return __class__.ALL[pid]
def len():
return len(__class__.ALL)
class Drone:
ALL = []
PAYLOAD = 0
def __init__(self):
self.id = len(self.ALL)
self.ALL.append(self)
self.pos = Warehouse.get(0).pos
self.items = []
self.avail = 0
self.tasks = []
def addTask(self, *task):
self.tasks.append(task)
def executeTask(self):
if self.available():
if len(self.tasks):
task = self.tasks[0]
getattr(self, task[0])(*task[1:])
self.tasks = self.tasks[1:]
else:
self.wait()
def weight(self):
s = 0
for i in self.items:
s += Product.get(i).weight
return s
def busyFor(self, time):
self.avail += time
assert(self.avail < T)
def available(self):
return self.avail <= turn
def load(self, warehouse, product, qt):
assert(self.available())
if (self.pos != warehouse.pos):
self.busyFor(distance(self.pos, warehouse.pos))
self.pos = warehouse.pos
for q in range(qt):
warehouse.items.remove(product.id)
self.items.append(product.id)
self.busyFor(1)
assert(self.weight() <= __class__.PAYLOAD)
log("Drone", self.id, "loads", qt, "of", product.id, "from warehouse", warehouse.id, "", self.avail)
output(self.id, 'L', warehouse.id, product.id, qt)
def unload(self, warehouse, product, qt):
assert(self.available())
if (self.pos != warehouse.pos):
self.busyFor(distance(self.pos, warehouse.pos))
self.pos = warehouse.pos
for q in range(qt):
self.items.remove(product.id)
warehouse.items.append(product.id)
self.busyFor(1)
log("Drone", self.id, "unloads", qt, "of", product.id, "to warehouse", warehouse.id, "", self.avail)
output(self.id, 'U', warehouse.id, product.id, qt)
def deliver(self, client, product, qt):
assert(self.available())
if (self.pos != client.pos):
self.busyFor(distance(self.pos, client.pos))
self.pos = client.pos
for q in range(qt):
self.items.remove(product.id)
client.needs.remove(product.id)
self.busyFor(1)
log("Drone", self.id, "delivers", qt, "of", product.id, "to client", client.id, "", self.avail)
output(self.id, 'D', client.id, product.id, qt)
if client.satisfied():
global score
score += math.ceil((T-(self.avail-1))/T*100)
log("Client", client.id, "satisfied!", "New score:", score)
def wait(self, turns=1):
assert(self.available())
self.busyFor(1)
log("Drone", self.id, "waits", turns, "turn" + ('s' if turns >= 2 else ''), "", self.avail)
output(self.id, 'W', turns)
# Set functions
def near(pos):
couples = []
for el in __class__.ALL:
couples.append([el, distance(el.pos, pos)])
return [couple[0] for couple in sorted(couples, key=lambda c: c[1])]
def get(pid):
return __class__.ALL[pid]
def len():
return len(__class__.ALL)
X = 0 # Nb rows
Y = 0 # Nb columns
T = 0 # Deadline
turn = 0 # Turn
score = 0 # Score
done = False
def readFile(filename):
global X, Y, T
with open(filename, 'r') as f:
# Parameters
X, Y, D, T, Drone.PAYLOAD = [int(i) for i in f.readline().split(' ')]
# Products
P = int(f.readline())
weights = [int(i) for i in f.readline().split(' ')]
assert(len(weights) == P)
for w in weights:
Product(w)
# Warehouses
for i in range(0, int(f.readline())):
pos = [int(i) for i in f.readline().split(' ')]
qtItems = [int(i) for i in f.readline().split(' ')]
assert(len(qtItems) == P)
items = []
for p in range(P):
for i in range(qtItems[p]):
items.append(p)
Warehouse(pos, items)
# Clients
for i in range(0, int(f.readline())):
pos = [int(i) for i in f.readline().split(' ')]
N = int(f.readline())
needs = [int(i) for i in f.readline().split(' ')]
assert(len(needs) == N)
Client(pos, needs)
# Create drones
for d in range(D):
Drone()
readFile(sys.argv[1])
def newTurn():
global turn
# Finishing turn
for drone in Drone.ALL:
drone.executeTask()
# New turn
turn += 1
log("--- Turn", turn)
availableDrones = [str(drone.id) for drone in Drone.ALL if drone.available()]
#log("Drones", ", ".join(availableDrones), "("+str(len(availableDrones))+")", "are available")
# Algorithm that only works for 1 warehouse
def think():
# For each drone that has nothing to do
for drone in [d for d in Drone.ALL if d.available() and not d.tasks]:
# Find the nearest warehouse
warehouse = Warehouse.near(drone.pos)[0]
# Find the nearest client that still has things to be delivered
remainingClients = [c for c in Client.near(warehouse.pos) if c.plannedNeeds]
if remainingClients:
client = remainingClients[0]
# Create a pack to deliver
pack = client.pack()
# Plan the delivery
packOccurences = dict((i, pack.count(i)) for i in pack)
# From http://stackoverflow.com/a/7843090/2766106
for i in packOccurences:
drone.addTask('load', warehouse, Product.get(i), packOccurences[i])
warehouse.plan(pack)
for i in packOccurences:
drone.addTask('deliver', client, Product.get(i), packOccurences[i])
client.plan(pack)
else:
global done
done = True
break
if DEBUG:
SIMULATION = 1000
else:
SIMULATION = 8*T/10
try:
if not DEBUG:
bar = progressbar.ProgressBar(max_value=SIMULATION)
while turn < SIMULATION and not done:
think()
newTurn()
if not DEBUG:
bar.update(turn)
if not DEBUG:
bar.finish()
except KeyboardInterrupt:
pass
with open(sys.argv[1] + 'o', 'w') as f:
f.write(str(len(outLines)) + '\n' + '\n'.join(outLines) + '\n')
print("Turn:", turn)
print("Score:", score)