Il tuo programma controllerà un robot da miniera alla ricerca sotterranea di minerali preziosi. Il robot comunicherà al controller dove si desidera spostare e scavare e il controller fornirà un feedback sullo stato del robot.
Inizialmente al tuo robot verrà fornita una mappa immagine della miniera con alcuni alberi di estrazione già presenti e un file di dati che specifica il valore e la durezza dei minerali nella miniera. Il robot si sposterà quindi attraverso gli alberi alla ricerca di minerali preziosi da estrarre. Il tuo robot può scavare attraverso la terra, ma è rallentato dalla roccia dura.
Il robot che ritorna con il carico più prezioso dopo un turno di 24 ore sarà il vincitore. Può sembrare una sfida complicata, ma è semplice creare un robot di mining di base (vedere la risposta del robot di mining di esempio di seguito).
operazione
Il programma verrà avviato dal controller con l'immagine della miniera, i dati minerali e i nomi dei file delle attrezzature. I robot possono utilizzare l'immagine di miniera e i dati dei minerali per trovare minerali preziosi ed evitare l'hard rock. Il robot potrebbe anche voler acquistare apparecchiature dall'elenco delle apparecchiature.
per esempio: python driller.py mineimage.png minerals.txt equipmentlist.txt
Dopo un periodo di inizializzazione di 2 secondi, il controller comunica con il programma robot tramite stdin e stdout. I robot devono rispondere con un'azione entro 0,1 secondi dalla ricezione di un messaggio di stato.
Ad ogni turno, il controller invia al robot una riga di stato:
timeleft cargo battery cutter x y direction
per esempio: 1087 4505 34.65 88.04 261 355 right
I numeri interi timeleft
sono i secondi di gioco rimasti prima della fine del turno. Il
cargo
valore intero dei minerali che hai estratto è molto meno quello che hai pagato per le attrezzature. Il battery
livello è una percentuale intera della carica residua della batteria. Il cutter
livello intero è l'attuale nitidezza del cutter come percentuale del valore standard. I valori x
e y
sono numeri interi positivi con la posizione del robot a cui fa riferimento l'angolo in alto a sinistra in (0, 0). La direzione è la direzione corrente verso cui è rivolto il robot (sinistra, destra, su, giù).
Quando il robot riceve l'ingresso "endshift" o "fail", il programma verrà presto interrotto. È possibile che si desideri che il robot scriva prima i dati di debug / prestazioni in un file.
Ci sono 4 possibili comandi che il controller accetterà. direction
left|right|up|down
indicherà il tuo robot in quella direzione e richiederà 15 secondi di gioco. move <integer>
indicherà al tuo robot di spostare o scavare in avanti tante unità, il che richiede tempo a seconda della durezza del taglio dei minerali e della nitidezza del cutter (vedi sotto). buy <equipment>
installerà l'equipaggiamento specificato e detrarrà il costo dal valore del carico, ma solo se il robot è in superficie (valore y <= valore y iniziale). L'installazione dell'attrezzatura richiede 300 secondi di gioco. Il comando speciale snapshot
scrive l'immagine della miniera corrente su disco e non richiede tempo di gioco. È possibile utilizzare le istantanee per eseguire il debug del robot o creare animazioni.
Il robot inizierà con 100 batterie e 100 nitidezza della taglierina. Lo spostamento e la rotazione utilizzano una piccola quantità di energia della batteria. Lo scavo utilizza molto di più ed è una funzione della durezza dei minerali e della nitidezza attuale del cutter. Man mano che il robot scava nei minerali, la taglierina perderà la sua nitidezza, a seconda del tempo impiegato e della durezza dei minerali. Se il robot ha un valore di carico sufficiente, potrebbe tornare in superficie per acquistare una nuova batteria o un cutter. Si noti che le apparecchiature di alta qualità hanno un'efficacia iniziale di oltre il 100%. Le batterie hanno la stringa "batteria" nel nome e le taglierine (a sorpresa) hanno "taglierina" nel nome.
Le seguenti relazioni definiscono lo spostamento e il taglio:
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
Notare che spostare 1 unità senza tagliare minerali richiede 1 secondo di gioco e utilizza 0,0178 della batteria. Quindi il robot può guidare 5600 unità in 93 minuti di gioco con una carica standard di 100, se non sta tagliando minerali o girando.
NOVITÀ: il robot ha una larghezza di 11 pixel, quindi taglierà fino a 11 pixel con ogni pixel di movimento. Se ci sono meno di 11 pixel da tagliare, il robot impiegherà meno tempo a muoversi e causerà meno usura sulla taglierina. Se un file di pixel non è specificato nel file di dati minerali, è spazio libero di durezza zero e valore zero.
La corsa termina allo scadere del tempo, la batteria del robot è scarica, una parte del robot supera il limite dell'immagine, viene inviato un comando illegale o si verifica un timeout della comunicazione del robot.
Il tuo punteggio è il valore finale del carico del robot. Il controller emetterà il tuo punteggio e l'immagine della mappa finale. L'output stderr del programma è registrato nel file robot.log. Se il robot muore, l'errore irreversibile potrebbe essere presente nel registro.
I miei dati
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
la mia immagine:
L'immagine della miniera potrebbe avere un canale alfa, ma questa non viene utilizzata.
Il controller
Il controller dovrebbe funzionare con Python 2.7 e richiede la libreria PIL. Sono stato informato che Python Pillow è un download compatibile con Windows per ottenere il modulo immagine PIL.
Avviare il controller con il programma robot, cfg.py, i file di immagine e dati nella directory corrente. La riga di comando suggerita è:
python controller.py [<interpreter>] {<switches>} <robotprogram>
Per esempio: python controller.py java underminer.class
Il controller scriverà un file robot.log e un file finalmine.png alla fine della corsa.
#!/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()
Il file di configurazione collegato (da non modificare):
# 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'
Formato di risposta
Le risposte dovrebbero avere un titolo che includa linguaggio di programmazione, nome del robot e punteggio finale (come Python 3 , Tunnel Terror , 1352 ). Il corpo della risposta dovrebbe avere il tuo codice e l'immagine finale della mappa delle mine. Altre immagini o animazioni sono benvenute. Il vincitore sarà il robot con il miglior punteggio.
Altre regole
- Le scappatoie comuni sono vietate.
- Se si utilizza un generatore di numeri casuali, è necessario codificare un seme nel proprio programma, in modo che l'esecuzione del programma sia riproducibile. Qualcun altro deve essere in grado di eseguire il programma e ottenere la stessa immagine e punteggio della mia miniera finale.
- Il tuo programma deve essere programmato per qualsiasi immagine di miniera. Non devi codificare il tuo programma per questi file di dati o queste dimensioni di immagine, layout minerale, layout di tunnel, ecc. Se sospetto che un robot stia infrangendo questa regola, mi riservo il diritto di modificare l'immagine di miniera e / o i file di dati.
Le modifiche
- Spiegazione della regola di risposta di 0,1 secondi.
- Espanso su robot a partire da opzioni e file della riga di comando.
- Aggiunta una nuova versione del controller con una migliore rilevazione degli errori.
- Aggiunta nota robot.log.
- Spiegazione della durezza e del valore minerali predefiniti.
- Spiegazione batteria vs attrezzatura taglierina.
- Rendere esplicita la dimensione del robot 11.
- Aggiunti calcoli per tempo, usura della taglierina e batteria.