Ihr Programm steuert einen Bergbauroboter, der im Untergrund nach wertvollen Mineralien sucht. Ihr Roboter teilt der Steuerung mit, wohin Sie sich bewegen und graben möchten, und die Steuerung gibt eine Rückmeldung über Ihren Roboterstatus.
Zu Beginn erhält Ihr Roboter eine Image-Karte der Mine mit bereits vorhandenen Schächten und eine Datendatei, in der der Wert und die Härte der Mineralien in der Mine angegeben sind. Ihr Roboter bewegt sich dann durch die Schächte und sucht nach wertvollen Mineralien, die abgebaut werden können. Ihr Roboter kann durch die Erde graben, wird aber von hartem Fels abgebremst.
Der Roboter, der nach einer 24-Stunden-Schicht mit der wertvollsten Fracht zurückkehrt, wird der Gewinner sein. Es scheint eine komplizierte Herausforderung zu sein, aber es ist einfach, einen einfachen Mining-Roboter zu bauen (siehe die Antwort des Sample Mining Robot weiter unten).
Betrieb
Ihr Programm wird vom Controller mit dem Minenbild, den Mineraldaten und den Dateinamen der Geräte gestartet. Roboter können das Bild und die Mineraldaten der Mine verwenden, um wertvolles Erz zu finden und hartes Gestein zu vermeiden. Der Roboter möchte möglicherweise auch Geräte aus der Geräteliste kaufen.
z.B: python driller.py mineimage.png minerals.txt equipmentlist.txt
Nach einer Initialisierungsperiode von 2 Sekunden kommuniziert die Steuerung über stdin und stdout mit dem Roboterprogramm. Roboter müssen innerhalb von 0,1 Sekunden nach Erhalt einer Statusmeldung mit einer Aktion antworten.
In jeder Runde sendet die Steuerung dem Roboter eine Statuszeile:
timeleft cargo battery cutter x y direction
z.B: 1087 4505 34.65 88.04 261 355 right
Die Ganzzahl timeleft
ist die Spielsekunde vor Schichtende. Dies
cargo
ist der ganzzahlige Wert der Mineralien, die Sie bisher abgebaut haben, abzüglich des Betrags, den Sie für die Ausrüstung bezahlt haben. Der battery
Ladezustand ist ein ganzzahliger Prozentsatz Ihrer verbleibenden Akkuladung. Die cutter
ganzzahlige Ebene ist die aktuelle Schärfe des Schneiders als Prozentsatz des Standardwerts. Die Werte x
und y
sind positive Ganzzahlen, wobei die Roboterposition von der oberen linken Ecke bei (0, 0) angegeben wird. Die Richtung ist die aktuelle Richtung, in die der Roboter schaut (links, rechts, oben, unten).
Wenn Ihr Roboter die Eingabe "Endshift" oder "Failed" empfängt, wird Ihr Programm bald beendet. Möglicherweise möchten Sie, dass Ihr Roboter zuerst Debugging- / Leistungsdaten in eine Datei schreibt.
Es gibt 4 mögliche Befehle, die der Controller akzeptiert. direction
left|right|up|down
zeigt Ihren Roboter in diese Richtung und benötigt 15 Spielsekunden. move <integer>
Weist Ihren Roboter an, so viele Einheiten vorwärts zu bewegen oder zu graben, was je nach Härte der geschnittenen Mineralien und Schärfe Ihres Fräsers einige Zeit in Anspruch nimmt (siehe unten). buy <equipment>
Installiert die angegebene Ausrüstung und zieht die Kosten von Ihrem Frachtwert ab, jedoch nur, wenn sich der Roboter an der Oberfläche befindet (y-Wert <= Start-y-Wert). Die Installation der Ausrüstung dauert 300 Spielsekunden. Der spezielle Befehl snapshot
schreibt das aktuelle Minenbild auf die Festplatte und benötigt keine Spielzeit. Sie können Snapshots zum Debuggen Ihres Roboters oder zum Erstellen von Animationen verwenden.
Ihr Roboter startet mit 100 Akkus und 100 Messerschärfen. Bewegen und Drehen verbrauchen nur wenig Batteriestrom. Das Graben verbraucht viel mehr und hängt von der Härte der Mineralien und der aktuellen Schärfe des Fräsers ab. Wenn Ihr Roboter in Mineralien gräbt, verliert der Fräser abhängig von der Zeit und der Härte der Mineralien seine Schärfe. Wenn Ihr Roboter über genügend Ladungswert verfügt, kehrt er möglicherweise an die Oberfläche zurück, um eine neue Batterie oder einen neuen Cutter zu kaufen. Beachten Sie, dass hochwertige Geräte eine anfängliche Wirksamkeit von über 100% haben. Batterien haben die Zeichenfolge "Batterie" im Namen und (Überraschungs-) Schneider haben "Schneider" im Namen.
Die folgenden Beziehungen definieren das Bewegen und Schneiden:
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
Beachten Sie, dass das Bewegen von 1 Einheit ohne Zerschneiden von Mineralien 1 Spielsekunde dauert und 0,0178 der Batterie verbraucht. Der Roboter kann also in 93 Spielminuten 5600 Einheiten mit einer Standardladung von 100 Einheiten fahren, wenn er keine Mineralien schneidet oder nicht dreht.
NEU: Der Roboter ist 11 Pixel breit und schneidet mit jedem Bewegungspixel bis zu 11 Pixel. Wenn weniger als 11 Pixel zu schneiden sind, benötigt der Roboter weniger Zeit, um sich zu bewegen, und verursacht weniger Verschleiß am Cutter. Wenn in der Mineraldatendatei keine Pixelfarbe angegeben ist, handelt es sich um einen freien Bereich mit einer Härte von Null und einem Wert von Null.
Der Lauf wird beendet, wenn die Zeit abgelaufen ist, die Roboterbatterie leer ist, ein Teil des Roboters die Bildgrenze überschreitet, ein ungültiger Befehl gesendet wird oder die Roboterkommunikation eine Zeitüberschreitung aufweist.
Ihre Punktzahl ist der Endwert der Roboterladung. Der Controller gibt Ihre Punktzahl und das endgültige Kartenbild aus. Die stderr-Ausgabe Ihres Programms wird in der Datei robot.log protokolliert. Wenn Ihr Roboter stirbt, ist der schwerwiegende Fehler möglicherweise im Protokoll verzeichnet.
Die Minendaten
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
Mein Bild:
Das Minenbild hat möglicherweise einen Alpha-Kanal, dieser wird jedoch nicht verwendet.
Der Controller
Der Controller sollte mit Python 2.7 arbeiten und benötigt die PIL-Bibliothek. Ich wurde informiert, dass das Python-Kissen ein Windows-freundlicher Download ist, um das PIL-Bildmodul zu erhalten.
Starten Sie die Steuerung mit dem Roboterprogramm, cfg.py, Bild- und Datendateien im aktuellen Verzeichnis. Die vorgeschlagene Befehlszeile lautet:
python controller.py [<interpreter>] {<switches>} <robotprogram>
Z.B: python controller.py java underminer.class
Die Steuerung schreibt am Ende des Laufs eine robot.log-Datei und eine finalmine.png-Datei.
#!/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()
Die verknüpfte Konfigurationsdatei (nicht zu ändern):
# 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'
Antwortformat
Die Antworten sollten einen Titel enthalten, der Programmiersprache, Roboternamen und Endergebnis enthält (z. B. Python 3 , Tunnel Terror , 1352 ). Der Antworttext sollte Ihren Code und das endgültige Minenkartenbild enthalten. Andere Bilder oder Animationen sind ebenfalls willkommen. Der Gewinner ist der Roboter mit der besten Punktzahl.
Andere Regeln
- Die gemeinsamen Schlupflöcher sind verboten.
- Wenn Sie einen Zufallszahlengenerator verwenden, müssen Sie einen Startwert in Ihrem Programm fest codieren, damit Ihr Programmablauf reproduzierbar ist. Jemand anderes muss in der Lage sein, Ihr Programm auszuführen und das gleiche endgültige Minenbild und -ergebnis zu erhalten.
- Ihr Programm muss für jedes Minenbild programmiert sein . Sie dürfen Ihr Programm nicht für diese Datendateien oder diese Bildgröße, Minerallayout, Tunnellayout usw. codieren . Wenn ich vermute, dass ein Roboter gegen diese Regel verstößt, behalte ich mir das Recht vor, das Minenbild und / oder die Datendateien zu ändern.
Bearbeitungen
- Erläuterte 0,1-Sekunden-Antwortregel.
- Erweiterung der Befehlszeilenoptionen und -dateien zum Starten des Roboters.
- Neue Controller-Version mit besserer Fehlerbehebung hinzugefügt.
- Robot.log note hinzugefügt.
- Grundlegende Mineralhärte und -wert.
- Erklärter Akku vs. Schneidegerät.
- Robotergröße 11 explizit gemacht.
- Berechnungen für Zeit, Messerverschleiß und Batterie hinzugefügt.