Seu programa controlará um robô de mineração que procura no subsolo minerais valiosos. Seu robô informará ao controlador onde você deseja mover e cavar, e fornecerá feedback sobre o status do robô.
Inicialmente, seu robô receberá um mapa de imagem da mina com alguns eixos de mineração já presentes e um arquivo de dados especificando o valor e a dureza dos minerais na mina. Seu robô passará pelos eixos procurando minerais valiosos para os meus. Seu robô pode cavar a terra, mas é desacelerado pelo hard rock.
O robô que retornar com a carga mais valiosa após um turno de 24 horas será o vencedor. Pode parecer um desafio complicado, mas é simples criar um robô de mineração básico (veja a resposta do robô de mineração de amostra abaixo).
Operação
Seu programa será iniciado pelo controlador com a imagem da mina, dados minerais e nomes de arquivos do equipamento. Os robôs podem usar os dados de imagem e minerais da mina para encontrar minério valioso e evitar rochas duras. O robô também pode querer comprar equipamentos da lista de equipamentos.
por exemplo: python driller.py mineimage.png minerals.txt equipmentlist.txt
Após um período de inicialização de 2 segundos, o controlador se comunica com o programa do robô através de stdin e stdout. Os robôs devem responder com uma ação dentro de 0,1 segundos após receber uma mensagem de status.
A cada turno, o controlador envia ao robô uma linha de status:
timeleft cargo battery cutter x y direction
por exemplo: 1087 4505 34.65 88.04 261 355 right
O número inteiro timeleft
é o segundo de jogo restante antes do final do turno. O
cargo
é o valor inteiro dos minerais que você extraiu até muito menos o que você pagou pelo equipamento. O battery
nível é uma porcentagem inteira da carga restante da bateria. O cutter
nível inteiro é a nitidez atual do cortador como uma porcentagem do valor padrão. Os valores x
e y
são números inteiros positivos com a posição do robô referenciada no canto superior esquerdo em (0, 0). A direção é a direção atual que o robô está enfrentando (esquerda, direita, cima, baixo).
Quando o seu robô receber a entrada 'turno final' ou 'com falha', seu programa será encerrado em breve. Você pode querer que seu robô grave dados de depuração / desempenho em um arquivo primeiro.
Existem 4 comandos possíveis que o controlador aceitará. direction
left|right|up|down
apontará seu robô nessa direção e precisará de 15 segundos de jogo. move <integer>
instruirá seu robô a mover ou cavar tantas unidades para frente, o que leva tempo, dependendo da dureza do corte de minerais e da nitidez do seu cortador (veja abaixo). buy <equipment>
instalará o equipamento especificado e deduzirá o custo do valor da carga, mas apenas se o robô estiver na superfície (valor y <= valor y inicial). A instalação do equipamento leva 300 segundos de jogo. O comando especial snapshot
grava a imagem atual da mina no disco e não leva tempo para o jogo. Você pode usar instantâneos para depurar seu robô ou criar animações.
Seu robô começará com 100 baterias e 100 nitidez do cortador. Mover e girar consome uma pequena quantidade de energia da bateria. A escavação usa muito mais e é uma função da dureza dos minerais e da nitidez atual do cortador. À medida que o robô escavar minerais, o cortador perderá a nitidez, dependendo do tempo gasto e da dureza dos minerais. Se o seu robô tiver valor de carga suficiente, ele poderá retornar à superfície para comprar uma nova bateria ou cortador. Observe que equipamentos de alta qualidade têm uma eficácia inicial de mais de 100%. As baterias têm o nome "bateria" no nome e os cortadores (surpresa) têm o nome "cortador".
Os seguintes relacionamentos definem movimento e corte:
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
Observe que mover 1 unidade sem cortar minerais leva 1 segundo de jogo e usa 0,0178 da bateria. Portanto, o robô pode dirigir 5600 unidades em 93 minutos de jogo com uma carga padrão de 100, se não estiver cortando minerais ou girando.
NOVO: o robô tem 11 pixels de largura e, portanto, cortará até 11 pixels a cada pixel de movimento. Se houver menos de 11 pixels para cortar, o robô levará menos tempo para se mover e causará menos desgaste no cortador. Se uma cor de pixel não for especificada no arquivo de dados minerais, será um espaço livre de dureza zero e valor zero.
A execução termina quando o tempo acaba, a bateria do robô está esgotada, uma parte do robô excede o limite da imagem, um comando ilegal é enviado ou a comunicação do robô expira.
Sua pontuação é o valor final da carga do robô. O controlador exibirá sua pontuação e a imagem final do mapa. A saída stderr do seu programa é registrada no arquivo robot.log. Se o seu robô morrer, o erro fatal pode estar no log.
Os dados da mina
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
minha imagem:
A imagem da mina pode ter um canal alfa, mas isso não é usado.
O controlador
O controlador deve funcionar com o Python 2.7 e requer a biblioteca PIL. Fui informado de que o Python Pillow é um download amigável do Windows para obter o módulo de imagem PIL.
Inicie o controlador com o programa do robô, cfg.py, arquivos de imagem e dados no diretório atual. A linha de comando sugerida é:
python controller.py [<interpreter>] {<switches>} <robotprogram>
Por exemplo: python controller.py java underminer.class
O controlador gravará um arquivo robot.log e um arquivo finalmine.png no final da execução.
#!/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()
O arquivo de configuração vinculado (não deve ser alterado):
# 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 da resposta
As respostas devem ter um título, incluindo linguagem de programação, nome do robô e pontuação final (como Python 3 , Tunnel Terror , 1352 ). O corpo da resposta deve ter seu código e a imagem final do mapa da mina. Outras imagens ou animações também são bem-vindas. O vencedor será o robô com a melhor pontuação.
Outras regras
- As brechas comuns são proibidas.
- Se você usar um gerador de números aleatórios, deverá codificar uma semente no seu programa, para que sua execução seja reproduzível. Outra pessoa deve ser capaz de executar seu programa e obter a mesma imagem e pontuação final da mina.
- Seu programa deve ser programado para qualquer imagem de mina. Você não deve codificar seu programa para esses arquivos de dados ou para esse tamanho de imagem, layout mineral, layout de túnel, etc. Se eu suspeito que um robô está violando essa regra, reservo-me o direito de alterar a imagem da mina e / ou os arquivos de dados.
Editar% s
- Explicou a regra de resposta de 0,1 segundos.
- Expandido no robô, iniciando opções e arquivos de linha de comando.
- Adicionada nova versão do controlador com melhor captura de erro.
- Adicionada nota robot.log.
- Dureza e valor mineral padrão explicados.
- Bateria explicada vs equipamento cortador.
- Tornou o tamanho do robô 11 explícito.
- Cálculos adicionados para tempo, desgaste do cortador e bateria.