Построить Горного Робота

12

Ваша программа будет управлять горным роботом, ищущим под землей ценные полезные ископаемые. Ваш робот сообщит контроллеру, куда вы хотите переместить и копать, а контроллер предоставит обратную связь о состоянии вашего робота.

Первоначально вашему роботу будет предоставлена ​​карта с изображением шахты с уже имеющимися шахтными стволами и файл данных, указывающий значение и твердость минералов в шахте. Затем ваш робот будет перемещаться по шахтам в поисках ценных полезных ископаемых. Ваш робот может копаться в земле, но замедляется тяжелым камнем.

маленькое мое изображение

Робот, который вернется с самым ценным грузом после 24-часовой смены, станет победителем. Это может показаться сложной задачей, но сделать простого майнингового робота просто (см. Ответ на пример Sample Mining Robot ниже).

операция

Ваша программа будет запущена контроллером с изображением шахты, данными о минералах и именами оборудования. Роботы могут использовать данные о рудниках и минералах, чтобы найти ценную руду и избежать твердых пород. Робот также может захотеть купить оборудование из списка оборудования.

например: python driller.py mineimage.png minerals.txt equipmentlist.txt

После 2-секундного периода инициализации контроллер связывается с программой робота через stdin и stdout. Роботы должны ответить действием в течение 0,1 секунды после получения сообщения о состоянии.

Каждый ход контроллер отправляет роботу строку состояния:

timeleft cargo battery cutter x y direction

например: 1087 4505 34.65 88.04 261 355 right

Целое число timeleft- игровые секунды, оставшиеся до конца смены. cargoЦелое значение минералов вы добытые до сих пор меньше , чем вы заплатили за оборудование. batteryУровень представляет собой целое число в процентах от оставшегося заряда аккумулятора. cutterУровень целого числа является текущей остротой резца в процентах от стандартного значения. Значения xи yявляются положительными целыми числами с указанием позиции робота из левого верхнего угла в (0, 0). Направление - это текущее направление, в котором стоит робот (влево, вправо, вверх, вниз).

Когда ваш робот получит данные «завершенное смещение» или «сбой», ваша программа будет вскоре остановлена. Возможно, вы захотите, чтобы ваш робот сначала записывал данные отладки / производительности в файл.

Есть 4 возможных команды, которые примет контроллер. direction left|right|up|downнаправит вашего робота в этом направлении и потребует 15 игровых секунд. move <integer>будет инструктировать вашего робота перемещать или копать столько единиц вперед, что требует времени в зависимости от твердости резаных минералов и остроты резца (см. ниже). buy <equipment>установит указанное оборудование и вычтет стоимость из стоимости вашего груза, но только если робот находится на поверхности (значение y <= начальное значение y). Установка оборудования занимает 300 игровых секунд. Специальная команда snapshotзаписывает текущий образ шахты на диск и не занимает игровое время. Вы можете использовать снимки для отладки своего робота или создания анимации.

Ваш робот запустится с 100 батареями и 100 резкостью. Перемещение и поворот используют небольшое количество энергии аккумулятора. Рытье использует гораздо больше и зависит от твердости минералов и текущей резкости резца. По мере того, как ваш робот копает минералы, резак теряет свою остроту, в зависимости от времени и твердости минералов. Если у вашего робота достаточно груза, он может вернуться на поверхность, чтобы купить новую батарею или резак. Обратите внимание, что высококачественное оборудование имеет начальную эффективность более 100%. Аккумуляторы имеют строку «батарея» в названии, а (неожиданно) резцы имеют «катер» в названии.

Следующие отношения определяют перемещение и резку:

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

Обратите внимание, что перемещение 1 единицы без каких-либо минералов занимает 1 игровую секунду и использует 0,0178 батареи. Таким образом, робот может управлять 5600 единицами за 93 игровых минуты при стандартной зарядке 100, если он не резает минералы или не поворачивается.

НОВИНКА: робот имеет ширину 11 пикселей, поэтому будет резать до 11 пикселей с каждым пикселем движения. Если для резки требуется менее 11 пикселей, роботу потребуется меньше времени для перемещения, что приведет к меньшему износу резака. Если цвет пикселя не указан в файле данных о минералах, это свободное пространство с нулевой твердостью и нулевым значением.

Прогон прекращается, когда время истекает, батарея робота разряжена, часть робота выходит за границы изображения, отправляется недопустимая команда или время ожидания связи робота.

Ваша оценка является окончательной стоимостью груза робота. Контроллер выведет ваш счет и окончательное изображение карты. Вывод stderr вашей программы регистрируется в файле robot.log. Если ваш робот умирает, фатальная ошибка может быть в журнале.

Данные шахты

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

мое изображение:

проверить мой

Мое изображение может иметь альфа-канал, но это не используется.

Контроллер

Контроллер должен работать с Python 2.7 и требует библиотеки PIL. Мне сообщили, что Python Pillow - это дружественная для Windows загрузка, чтобы получить модуль изображения PIL.

Запустите контроллер с программой робота, cfg.py, файлами изображений и данных в текущем каталоге. Предлагаемая командная строка:

python controller.py [<interpreter>] {<switches>} <robotprogram>

Например: python controller.py java underminer.class

Контроллер запишет файл robot.log и файл finalmine.png в конце выполнения.

#!/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()

Связанный файл конфигурации (не подлежит изменению):

# 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'

Формат ответа

Ответы должны иметь название, включая язык программирования, имя робота и итоговую оценку (например, Python 3 , Tunnel Terror , 1352 ). В теле ответа должен быть ваш код и окончательное изображение карты шахты. Другие изображения или анимации тоже приветствуются. Победителем станет робот с лучшим счетом.

Другие правила

  • Общие лазейки запрещены.
  • Если вы используете генератор случайных чисел, вы должны жестко закодировать начальное число в вашей программе, чтобы запуск вашей программы был воспроизводимым. Кто-то другой должен иметь возможность запустить вашу программу и получить тот же окончательный образ шахты и счет.
  • Ваша программа должна быть запрограммирована для любого изображения шахты. Вы не должны кодировать свою программу для этих файлов данных или этого размера изображения, расположения минералов, расположения туннелей и т. Д. Если я подозреваю, что робот нарушает это правило, я оставляю за собой право изменять изображение шахты и / или файлы данных.

Правки

  • Объяснил 0,1 секунды правила ответа.
  • Добавлены опции и файлы для запуска командной строки робота.
  • Добавлена ​​новая версия контроллера с улучшенным отловом ошибок.
  • Добавлена ​​заметка robot.log.
  • Объяснены стандартные минеральные твердости и стоимости.
  • Объясненная батарея против режущего оборудования.
  • Сделан робот размером 11 явно.
  • Добавлены расчеты времени, износа резца и аккумулятора.
Логика Найт
источник
2
@TApicella 1. Роботы получают имя файла изображения в качестве аргумента и могут читать и обрабатывать его так, как им нравится. Изображение контроллера будет изменяться по мере движения робота, и робот не сможет этого увидеть. Роботы могут использовать PIL или другие сторонние библиотеки OSS. 2. У роботов есть 2 секунды для инициализации, а затем 0,1 секунды на ответ команды.
Рыцарь логики
1
Вы должны задокументировать 0,1 секунды на ответ команды в вопросе.
Питер Тейлор
1
@KeithRandall Нет. Вы должны прочитать изображение и 2 файла данных из имен файлов, указанных в командной строке. Они могут быть изменены.
Рыцарь логики
1
@TApicella Я добавил еще один ответ со структурой Python, которая может помочь.
Логика Найт
2
Это особенность. Используйте это в своих интересах, если можете :)
Logic Knight

Ответы:

3

Python 2, Sample Miner, 350

Это пример минимального кода для майнинг робота. Он просто копает прямо вниз, пока его батарея не разряжается (все роботы начинают указывать вниз). Он зарабатывает только 350 очков. Не забудьте сбросить стандартный вывод, иначе контроллер зависнет.

import sys
# Robots are started with 3 arguments:
mineimage, mineralfile, equipmentfile = sys.argv[1:4]
raw_input()           # ignore first status report
print 'move 1000'     # dig down until battery dies
sys.stdout.flush()    # remember to flush stdout
raw_input()           # wait for end message

пример шахтерского пути

Логика Найт
источник
2

Python 2, шаблон Python для робота-майнера, 410

Это шаблон для майнинга роботов, который показывает, как работает робот, и предоставляет основу для создания ваших собственных роботов. Есть раздел для анализа минеральных данных и раздел для реагирования с действиями. Алгоритмы-заполнители не очень хороши. Робот находит некоторые ценные полезные ископаемые, но их недостаточно, чтобы купить достаточное количество сменных батарей и резаков. Он останавливается с разряженной батареей на пути к поверхности во второй раз.

Лучшим планом является использование существующих туннелей, чтобы приблизиться к ценным минералам и минимизировать копание.

Обратите внимание, что этот робот записывает файл журнала каждого полученного сообщения о состоянии, чтобы вы могли проверить его решения после запуска.

import sys
from PIL import Image

MINEIMAGE, MINERALFILE, EQUIPMENTFILE = sys.argv[1:4]
image = Image.open(MINEIMAGE)
W,H = image.size
robotwidth = 11
halfwidth = robotwidth / 2

# 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 = [(name, int(cost), float(init)) for 
    name, cost, init in data]
# Find the cheapest battery and cutter for later purchase:
minbatcost, minbatname = min([(c,n) for 
    n,c,v in equipment if n.endswith('battery')])
mincutcost, mincutname = min([(c,n) for 
    n,c,v in equipment if n.endswith('cutter')])

# process the mine image to find good places to mine:
goodspots = [0] * W
for ix in range(W):
    for iy in range(H):
        color = image.getpixel((ix, iy))[:3]   # keep RGB, lose Alpha
        value = mineralvalue.get(color, 0)
        hard = hardness.get(color, 0)
        #
        # -------------------------------------------------------------
        # make a map or list of good areas to mine here
        if iy < H/4:
            goodspots[ix] += value - hard/10.0
        # (you will need a better idea than this)
goodshafts = [sum(goodspots[i-halfwidth : i+halfwidth+1]) for i in range(W)]
goodshafts[:halfwidth] = [-1000]*halfwidth   # stop robot going outside bounds
goodshafts[-halfwidth:] = [-1000]*halfwidth
bestspot = goodshafts.index(max(goodshafts))
# -----------------------------------------------------------------
#

dirmap = dict(right=(1,0), left=(-1,0), up=(0,-1), down=(0,1))
logging = open('mylog.txt', 'wt')
logfmt = '%7s %7s %7s %7s %7s %7s %7s\n'
logging.write(logfmt % tuple('Seconds Cargo Battery Cutter x y Direc'.split()))
surface = None
plan = []

while True:
    status = raw_input().split()
    if status[0] in ('endshift', 'failed'):
        # robot will be terminated soon
        logging.close()
        continue
    logging.write(logfmt % tuple(status))
    direction = status.pop(-1)
    clock, cargo, battery, cutter, rx, ry = map(int, status)
    if surface == None:
        surface = ry    # return to this level to buy equipment
    #
    # -----------------------------------------------------------------
    # Decide here to choose direction, move, buy, or snapshot
    if not plan and rx != bestspot:
        plan.append('direction right' if bestspot > rx else 'direction left')
        plan.append('move %u' % abs(bestspot - rx))
        plan.append('direction down')

    if plan:
        action = plan.pop(0)
    elif battery < 20 and cargo > minbatcost + mincutcost:
        action = 'direction up'
        move = 'move %u' % (ry - surface)
        buybat = 'buy %s' % minbatname
        buycut = 'buy %s' % mincutname
        plan = [move, buybat, buycut, 'direction down', move]
    else:
        action = 'move 1'
    # -----------------------------------------------------------------
    #
    print action
    sys.stdout.flush()

окончательная карта шахты

Логика Найт
источник
Большое спасибо, разоблачение цикла, который управляет взаимодействием между контроллером и программой робота, действительно полезно.
TApicella