Votre programme contrôlera un robot minier à la recherche souterraine de minéraux précieux. Votre robot indiquera au contrôleur où vous souhaitez vous déplacer et creuser, et le contrôleur fournira des informations sur l'état de votre robot.
Initialement, votre robot recevra une carte image de la mine avec quelques puits de mine déjà présents, et un fichier de données spécifiant la valeur et la dureté des minéraux dans la mine. Votre robot se déplacera ensuite à travers les puits à la recherche de minéraux précieux à extraire. Votre robot peut creuser à travers la terre, mais est ralenti par la roche dure.
Le robot qui reviendra avec la cargaison la plus précieuse après 24 heures de travail sera le gagnant. Cela peut sembler être un défi compliqué, mais il est simple de créer un robot minier de base (voir la réponse Exemple de robot minier ci-dessous).
Opération
Votre programme sera démarré par le contrôleur avec l'image de la mine, les données minérales et les noms de fichiers de l'équipement. Les robots peuvent utiliser l'image de la mine et les données sur les minéraux pour trouver du minerai précieux et éviter les roches dures. Le robot peut également vouloir acheter de l'équipement dans la liste des équipements.
par exemple: python driller.py mineimage.png minerals.txt equipmentlist.txt
Après une période d'initialisation de 2 secondes, le contrôleur communique avec le programme du robot via stdin et stdout. Les robots doivent répondre par une action dans les 0,1 secondes après avoir reçu un message d'état.
A chaque tour, le contrôleur envoie au robot une ligne d'état:
timeleft cargo battery cutter x y direction
par exemple: 1087 4505 34.65 88.04 261 355 right
L'entier timeleft
est le nombre de secondes de jeu restant avant la fin du quart de travail. C'est
cargo
la valeur entière des minéraux que vous avez extraits jusqu'à présent moins ce que vous avez payé pour l'équipement. Le battery
niveau est un pourcentage entier de votre charge de batterie restante. Le cutter
niveau entier est la netteté actuelle de l'outil de coupe en pourcentage de la valeur standard. Les valeurs x
et y
sont des entiers positifs avec la position du robot référencée dans le coin supérieur gauche à (0, 0). La direction est la direction actuelle du robot (gauche, droite, haut, bas).
Lorsque votre robot reçoit l'entrée 'endshift' ou 'échec', votre programme sera bientôt terminé. Vous voudrez peut-être que votre robot écrive d'abord les données de débogage / performances dans un fichier.
Le contrôleur accepte 4 commandes possibles. direction
left|right|up|down
pointera votre robot dans cette direction et nécessitera 15 secondes de jeu. move <integer>
demandera à votre robot de déplacer ou de creuser autant d'unités vers l'avant, ce qui prend du temps en fonction de la dureté des minéraux coupés et de la netteté de votre couteau (voir ci-dessous). buy <equipment>
installera l'équipement spécifié et déduira le coût de votre valeur de chargement, mais uniquement si le robot est à la surface (valeur y <= valeur y de départ). L'installation de l'équipement prend 300 secondes de jeu. La commande spéciale snapshot
écrit l'image de la mine actuelle sur le disque et ne prend pas de temps de jeu. Vous pouvez utiliser des instantanés pour déboguer votre robot ou créer des animations.
Votre robot démarrera avec 100 piles et une netteté de 100 couteaux. Déplacer et tourner utilise une petite quantité de batterie. Le creusement utilise beaucoup plus et est fonction de la dureté des minéraux et de la netteté actuelle de la fraise. Au fur et à mesure que votre robot creuse des minéraux, le couteau perdra de sa netteté, en fonction du temps et de la dureté des minéraux. Si votre robot a une valeur de chargement suffisante, il peut revenir à la surface pour acheter une nouvelle batterie ou un cutter. Notez qu'un équipement de haute qualité a une efficacité initiale de plus de 100%. Les batteries ont la chaîne "batterie" dans le nom et (surprise) les couteaux ont "cutter" dans le nom.
Les relations suivantes définissent le déplacement et la découpe:
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
Notez que déplacer 1 unité sans couper de minéraux prend 1 seconde de jeu et utilise 0,0178 de la batterie. Ainsi, le robot peut conduire 5600 unités en 93 minutes de jeu sur une charge standard de 100, s'il ne coupe pas de minéraux ou ne tourne pas.
NOUVEAU: Le robot mesure 11 pixels de large, il coupera donc jusqu'à 11 pixels à chaque pixel de mouvement. S'il reste moins de 11 pixels à couper, le robot prendra moins de temps pour se déplacer et causera moins d'usure sur le couteau. Si une couleur de pixel n'est pas spécifiée dans le fichier de données minérales, il s'agit d'un espace libre de dureté zéro et de valeur nulle.
L'exécution est terminée lorsque le temps est écoulé, la batterie du robot est épuisée, une partie du robot dépasse la limite de l'image, une commande illégale est envoyée ou la communication du robot expire.
Votre score est la valeur finale de la cargaison du robot. Le contrôleur affichera votre score et l'image finale de la carte. La sortie stderr de votre programme est enregistrée dans le fichier robot.log. Si votre robot meurt, l'erreur fatale peut être dans le journal.
Les données de la mine
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
mon image:
L'image de la mine peut avoir un canal alpha, mais celui-ci n'est pas utilisé.
Le controlle
Le contrôleur doit fonctionner avec Python 2.7 et nécessite la bibliothèque PIL. J'ai été informé que l'oreiller Python est un téléchargement compatible avec Windows pour obtenir le module d'image PIL.
Démarrez le contrôleur avec le programme robot, cfg.py, les fichiers image et données dans le répertoire courant. La ligne de commande suggérée est:
python controller.py [<interpreter>] {<switches>} <robotprogram>
Par exemple: python controller.py java underminer.class
Le contrôleur écrira un fichier robot.log et un fichier finalmine.png à la fin de l'exécution.
#!/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()
Le fichier de configuration lié (à ne pas modifier):
# 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 de réponse
Les réponses doivent avoir un titre comprenant le langage de programmation, le nom du robot et le score final (comme Python 3 , Tunnel Terror , 1352 ). Le corps de la réponse doit avoir votre code et l'image finale de la carte de la mine. D'autres images ou animations sont également les bienvenues. Le gagnant sera le robot avec le meilleur score.
Autres règles
- Les failles communes sont interdites.
- Si vous utilisez un générateur de nombres aléatoires, vous devez coder en dur une graine dans votre programme, afin que votre exécution de programme soit reproductible. Quelqu'un d'autre doit pouvoir exécuter votre programme et obtenir la même image et le score final de la mine.
- Votre programme doit être programmé pour n'importe quelle image de mine. Vous ne devez pas coder votre programme pour ces fichiers de données ou pour cette taille d'image, la disposition des minéraux, la disposition du tunnel, etc. Si je soupçonne qu'un robot enfreint cette règle, je me réserve le droit de modifier l'image de la mine et / ou les fichiers de données.
Modifications
- Explication de la règle de réponse de 0,1 seconde.
- Développé sur les options et les fichiers de la ligne de commande de démarrage du robot.
- Ajout d'une nouvelle version du contrôleur avec une meilleure capture des erreurs.
- Ajout d'une note robot.log.
- Explication de la dureté et de la valeur par défaut des minéraux.
- Explication de la batterie par rapport à l'équipement de coupe.
- Rendu la taille du robot 11 explicite.
- Ajout de calculs pour le temps, l'usure des couteaux et la batterie.