Geoffrey Frogeye
dd206307b9
Nothing should have changed in the results, yet it won three points. Oh well...
362 lines
9.5 KiB
Python
Executable file
362 lines
9.5 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 totalWeight(items):
|
|
s = 0
|
|
for i in items:
|
|
s += Product.get(i).weight
|
|
return s
|
|
|
|
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):
|
|
if payload == -1:
|
|
payload = Drone.PAYLOAD
|
|
p = []
|
|
load = 0
|
|
# # Sort occurences
|
|
# occ = [(i, self.plannedNeeds.count(i)) for i in self.plannedNeeds]
|
|
# occ.sort(key=lambda c: c[1])
|
|
# # TODO Optimise for same product wanted more than once
|
|
# Looks like it's not necessary for set 2, we'll see that later
|
|
# 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):
|
|
return Product.totalWeight(self.items)
|
|
|
|
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 route(roadmap):
|
|
# Find the nearest client that still has things to be delivered
|
|
remainingClients = [c for c in Client.near(roadmap['pos']) if c.plannedNeeds]
|
|
if remainingClients:
|
|
client = remainingClients[0]
|
|
# Create a pack to deliver
|
|
pack = client.pack()
|
|
# TODO if pack ... else ...
|
|
# Plan the delivery
|
|
roadmap['warehouse'].plan(pack)
|
|
client.plan(pack)
|
|
roadmap['deliverTime'] += distance(roadmap['pos'], client.pos) + len(list(set(pack)))
|
|
roadmap['pos'] = client.pos
|
|
roadmap['loads'] += pack
|
|
roadmap['stops'].append((client, pack))
|
|
return roadmap
|
|
|
|
|
|
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]
|
|
roadmap = route({
|
|
'pos': warehouse.pos,
|
|
'warehouse': warehouse,
|
|
'deliverTime': 0,
|
|
'loads': [],
|
|
'stops': []
|
|
})
|
|
|
|
if not roadmap['stops']:
|
|
global done
|
|
done = True
|
|
break
|
|
|
|
loadOcc = dict((i, roadmap['loads'].count(i)) for i in roadmap['loads'])
|
|
for i in loadOcc:
|
|
drone.addTask('load', warehouse, Product.get(i), loadOcc[i])
|
|
|
|
for client, items in roadmap['stops']:
|
|
itemsOcc = dict((j, items.count(j)) for j in items)
|
|
for i in itemsOcc:
|
|
drone.addTask('deliver', client, Product.get(i), itemsOcc[i])
|
|
|
|
|
|
|
|
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)
|
|
|