Twój program będzie sterował robotem wydobywczym poszukującym pod ziemią cennych minerałów. Twój robot poinformuje kontroler, gdzie chcesz się poruszać i kopać, a kontroler przekaże informację zwrotną na temat Twojego robota.
Początkowo twój robot otrzyma mapę obrazową kopalni z niektórymi już szybami wydobywczymi oraz plik danych określający wartość i twardość minerałów w kopalni. Twój robot będzie wtedy poruszał się po szybach w poszukiwaniu cennych minerałów do wydobycia. Twój robot może kopać ziemię, ale spowalnia go twarda skała.
Zwycięzcą zostanie robot, który powróci z najcenniejszym ładunkiem po 24-godzinnej zmianie. To może wydawać się skomplikowanym wyzwaniem, ale łatwo jest zbudować podstawowego robota górniczego (patrz odpowiedź Przykładowego robota górniczego poniżej).
Operacja
Twój program zostanie uruchomiony przez kontrolera z obrazem kopalni, danymi mineralnymi i nazwami plików sprzętu. Roboty mogą wykorzystywać obraz kopalni i dane minerałów, aby znaleźć cenną rudę i uniknąć twardego kamienia. Robot może również chcieć kupić sprzęt z listy urządzeń.
na przykład: python driller.py mineimage.png minerals.txt equipmentlist.txt
Po 2 sekundach okresu inicjalizacji sterownik komunikuje się z programem robota za pośrednictwem stdin i stdout. Roboty muszą odpowiedzieć działaniem w ciągu 0,1 sekundy od otrzymania komunikatu o stanie.
Za każdym razem kontroler wysyła robotowi linię statusu:
timeleft cargo battery cutter x y direction
na przykład: 1087 4505 34.65 88.04 261 355 right
Liczba całkowita timeleft
to sekunda gry pozostała do końca zmiany. Jest
cargo
to całkowita liczba minerałów, które wydobywałeś o wiele mniej, niż zapłaciłeś za sprzęt. battery
Poziom odsetek jest całkowitą swojej naładowania baterii. cutter
Wysokość całkowita jest obecny ostrość freza procent wartości standardowe. Wartości x
i y
są dodatnimi liczbami całkowitymi z pozycją robota wskazywaną od lewego górnego rogu w (0, 0). Kierunek to aktualny kierunek, w którym skierowany jest robot (w lewo, w prawo, w górę, w dół).
Kiedy robot otrzyma komunikat „zmiana biegu” lub „błąd”, program wkrótce zostanie zakończony. Być może najpierw chcesz, aby robot zapisał dane debugowania / wydajności w pliku.
Istnieją 4 możliwe polecenia, które kontroler zaakceptuje. direction
left|right|up|down
skieruje robota w tym kierunku i zajmie 15 sekund gry. move <integer>
poinstruuje twojego robota, aby ruszył lub kopał o tyle jednostek do przodu, co zajmuje czas w zależności od twardości wydobytych minerałów i ostrości twojego noża (patrz poniżej). buy <equipment>
zainstaluje określony sprzęt i odliczy koszt od wartości ładunku, ale tylko wtedy, gdy robot znajdzie się na powierzchni (wartość y <= początkowa wartość y). Instalacja sprzętu zajmuje 300 sekund gry. Specjalne polecenie snapshot
zapisuje bieżący obraz kopalni na dysk i nie zajmuje czasu gry. Za pomocą migawek można debugować robota lub tworzyć animacje.
Twój robot uruchomi się ze 100 bateriami i 100 ostrością noża. Przenoszenie i obracanie zużywa niewielką ilość energii baterii. Kopanie zużywa znacznie więcej i jest funkcją twardości minerałów i aktualnej ostrości noża. Gdy robot wkopuje się w minerały, frez straci swoją ostrość, w zależności od czasu i twardości minerałów. Jeśli twój robot ma wystarczającą wartość ładunku, może wrócić na powierzchnię, aby kupić nową baterię lub nóż. Należy pamiętać, że wysokiej jakości sprzęt ma początkową skuteczność ponad 100%. Baterie mają w nazwie ciąg „bateria”, a (zaskakujące) kutry mają w nazwie „nóż”.
Następujące relacje definiują przenoszenie i cięcie:
timecutting = sum(hardness of pixels cut) * 100 / cutter
cutterwear = 0.01 for each second cutting
cutters will not wear below 0.1 sharpness
timemoving = 1 + timecutting
batterydrain = 0.0178 for each second moving
changing direction takes 15 seconds and drains 0.2 from the battery
installing new equipment takes 300 seconds
Pamiętaj, że przeniesienie 1 jednostki bez cięcia minerałów zajmuje 1 sekundę gry i zużywa 0,0178 baterii. Robot może więc prowadzić 5600 jednostek w 93 minuty gry przy standardowym ładunku 100, jeśli nie tnie minerałów ani nie obraca się.
NOWOŚĆ: robot ma szerokość 11 pikseli, dzięki czemu będzie przycinać do 11 pikseli przy każdym pikselu ruchu. Jeśli do cięcia jest mniej niż 11 pikseli, robot będzie się poruszał krócej i spowoduje mniejsze zużycie noża. Jeśli w pliku danych mineralnych nie określono koloru piksela, to jest to wolna przestrzeń o zerowej twardości i zerowej wartości.
Bieg kończy się, gdy skończy się czas, bateria robota jest wyczerpana, część robota przekracza granicę obrazu, wysyłane jest niedozwolone polecenie lub upłynął limit czasu komunikacji robota.
Twój wynik to końcowa wartość ładunku robota. Kontroler wyśle twój wynik i ostateczny obraz mapy. Dane wyjściowe stderr programu są zapisywane w pliku robot.log. Jeśli twój robot zginie, w dzienniku może znajdować się błąd krytyczny.
Dane kopalni
equipment.txt:
Equipment_Name Cost Initial_Value
std_cutter 200 100
carbide_cutter 600 160
diamond_cutter 2000 250
forcehammer_cutter 7200 460
std_battery 200 100
advanced_battery 500 180
megapower_battery 1600 320
nuclear_battery 5200 570
mineraldata.txt:
Mineral_Name Color Value Hardness
sandstone (157,91,46) 0 3
conglomerate (180,104,102) 0 12
igneous (108,1,17) 0 42
hard_rock (219,219,219) 0 15
tough_rock (146,146,146) 0 50
super_rock (73,73,73) 0 140
gem_ore1 (0,255,0) 10 8
gem_ore2 (0,0,255) 30 14
gem_ore3 (255,0,255) 100 6
gem_ore4 (255,0,0) 500 21
mój obraz:
Obraz kopalni może mieć kanał alfa, ale nie jest używany.
Kontroler
Kontroler powinien współpracować z Pythonem 2.7 i wymaga biblioteki PIL. Zostałem poinformowany, że Python Pillow to przyjazny do pobrania system Windows, aby uzyskać moduł obrazu PIL.
Uruchom kontroler z programem robota, cfg.py, plikami obrazów i danych w bieżącym katalogu. Sugerowana linia poleceń to:
python controller.py [<interpreter>] {<switches>} <robotprogram>
Na przykład: python controller.py java underminer.class
Sterownik zapisze plik robot.log i plik finalmine.png na końcu przebiegu.
#!/usr/bin/env python
# controller.py
# Control Program for the Robot Miner on PPCG.
# Tested on Python 2.7 on Ubuntu Linux. May need edits for other platforms.
# V1.0 First release.
# V1.1 Better error catching
import sys, subprocess, time
# Suggest installing Pillow here if you don't have PIL already
from PIL import Image, ImageDraw
from cfg import *
program = sys.argv[1:]
calltext = program + [MINEIMAGE, MINERALFILE, EQUIPMENTFILE]
errorlog = open(ERRORFILE, 'wb')
process = subprocess.Popen(calltext,
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=errorlog)
image = Image.open(MINEIMAGE)
draw = ImageDraw.Draw(image)
BLACK, ORANGE, WHITE = (0,0,0), (255,160,160), (255,255,255)
W,H = image.size
dirmap = dict(right=(1,0), left=(-1,0), up=(0,-1), down=(0,1))
# read in mineral file (Name, Color, Value, Hardness):
data = [v.split() for v in open(MINERALFILE)][1:]
mineralvalue = dict((eval(color), int(value)) for
name, color, value, hard in data)
hardness = dict((eval(color), int(hard)) for
name, color, value, hard in data)
# read in the equipment list:
data = [v.split() for v in open(EQUIPMENTFILE)][1:]
equipment = dict((name, (int(cost), float(init))) for
name, cost, init in data)
# Set up simulation variables:
status = 'OK'
rx, ry, direction = START_X, START_Y, START_DIR # center of robot
cargo, battery, cutter = 0, 100.0, 100.0
clock = ENDSHIFT
size = ROBOTSIZE / 2
msgfmt = '%u %u %u %u %u %u %s'
snapnum = 1
def mkcutlist(x, y, direc, size):
dx, dy = dirmap[direc]
cx, cy = x+dx*(size+1), y+dy*(size+1)
output = [(cx, cy)]
for s in range(1, size+1):
output += [ (cx+dy*s, cy+dx*s), (cx-dy*s, cy-dx*s)]
return output
def send(msg):
process.stdin.write((msg+'\n').encode('utf-8'))
process.stdin.flush()
def read():
return process.stdout.readline().decode('utf-8')
time.sleep(INITTIME)
while clock > 0:
try:
start = time.time()
send(msgfmt % (clock, cargo, battery, cutter, rx, ry, direction))
inline = read()
if time.time() - start > TIMELIMIT:
status = 'Move timeout'
break
except:
status = 'Robot comslink failed'
break
# Process command:
movecount = 0
try:
arg = inline.split()
cmd = arg.pop(0)
if cmd == 'buy':
if ry <= START_Y and arg and arg[0] in equipment:
cost, initperc = equipment[arg[0]]
if cost <= cargo:
cargo -= cost
if 'battery' in arg[0]:
battery = initperc
elif 'cutter' in arg[0]:
cutter = initperc
clock -= 300
elif cmd == 'direction':
if arg and arg[0] in dirmap:
direction = arg[0]
clock -= 15
battery -= 0.2
elif cmd == 'move':
if arg and arg[0].isdigit():
movecount = abs(int(arg[0]))
elif cmd == 'snapshot':
image.save('snap%04u.png' % snapnum)
snapnum += 1
except:
status = 'Robot command malfunction'
break
for move in range(movecount):
# check image boundaries
dx, dy = dirmap[direction]
rx2, ry2 = rx + dx, ry + dy
print rx2, ry2
if rx2-size < 0 or rx2+size >= W or ry2-size < 0 or ry2+size >= H:
status = 'Bounds exceeded'
break
# compute time to move/cut through 1 pixel
try:
cutlist = mkcutlist(rx2, ry2, direction, size)
colors = [image.getpixel(pos)[:3] for pos in cutlist]
except IndexError:
status = 'Mining outside of bounds'
break
work = sum(hardness.get(c, 0) for c in colors)
timetaken = work * 100 / cutter
cutter = max(0.1, cutter - timetaken / 100)
clock -= 1 + int(timetaken + 0.5)
battery -= (1 + timetaken) / 56
if battery <= 0:
status = 'Battery exhausted'
break
cargo += sum(mineralvalue.get(c, 0) for c in colors)
draw.rectangle([rx-size, ry-size, rx+size+1, ry+size+1], BLACK, BLACK)
rx, ry = rx2, ry2
draw.rectangle([rx-size, ry-size, rx+size+1, ry+size+1], ORANGE, WHITE)
if clock <= 0:
break
if status != 'OK':
break
del draw
image.save('finalmine.png')
if status in ('Battery exhausted', 'OK'):
print 'Score = %s' % cargo
send('endshift')
else:
print 'Error: %s at clock %s' % (status, clock)
send('failed')
time.sleep(0.3)
process.terminate()
Połączony plik konfiguracyjny (nie do zmiany):
# This is cfg.py
# Scenario files:
MINEIMAGE = 'testmine.png'
MINERALFILE = 'mineraldata.txt'
EQUIPMENTFILE = 'equipment.txt'
# Mining Robot parameters:
START_X = 270
START_Y = 28
START_DIR = 'down'
ROBOTSIZE = 11 # should be an odd number
ENDSHIFT = 24 * 60 * 60 # seconds in an 24 hour shift
INITTIME = 2.0
TIMELIMIT = 0.1
ERRORFILE = 'robot.log'
Format odpowiedzi
Odpowiedzi powinny mieć tytuł zawierający język programowania, nazwę robota i końcowy wynik (np. Python 3 , Tunnel Terror , 1352 ). Treść odpowiedzi powinna zawierać Twój kod i końcowy obraz mapy kopalni. Inne obrazy lub animacje są również mile widziane. Zwycięzcą zostanie robot z najlepszym wynikiem.
Inne zasady
- Częste luki są zabronione.
- Jeśli używasz generatora liczb losowych, musisz zakodować ziarno w swoim programie, aby program mógł być odtwarzany. Ktoś inny musi być w stanie uruchomić Twój program i uzyskać ten sam końcowy obraz kopalni i wynik.
- Twój program musi być zaprogramowany dla każdego obrazu kopalni. Nie wolno kodować swój program dla tych danych lub tym rozmiar, mineralny układ, układ tunel itp Jeśli podejrzewam robotem łamie tę zasadę, że zastrzegamy sobie prawo do zmiany wizerunku kopalni i / lub plików danych.
Edycje
- Wyjaśniona zasada 0,1 sekundy odpowiedzi.
- Rozbudowany na robotach uruchamiających opcje wiersza poleceń i pliki.
- Dodano nową wersję kontrolera z lepszym wykrywaniem błędów.
- Dodano notatkę robot.log.
- Wyjaśniona domyślna twardość i wartość mineralna.
- Wyjaśniony akumulator w porównaniu do wyposażenia noża.
- Rozmiar robota 11 jest wyraźny.
- Dodano obliczenia czasu, zużycia noża i baterii.