Побудуйте гірничого робота


12

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

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

мале зображення міни

Робот, який повернеться з найціннішим вантажем після 24-годинної зміни, стане переможцем. Це може здатися складним викликом, але зробити основний робочий майнінг просто (див. Відповідь Роботи з видобутку зразків нижче).

Операція

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

наприклад: 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). Напрямок - поточний напрямок, з яким стикається робот (вліво, вправо, вгору, вниз).

Коли ваш робот отримає вхід 'endhift' або 'fail', ваша програма незабаром буде припинена. Можливо, ви бажаєте, щоб робот спочатку записав дані про налагодження / продуктивність у файл.

Існує 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 пікселів, роботу знадобиться менше часу на переміщення та зменшить знос різака. Якщо колір пікселів не вказаний у файлі мінеральних даних, це вільний простір нульової твердості та нульове значення.

Запуск припиняється, коли закінчується час, акумулятор роботи вичерпується, частина робота перевищує межу зображення, надсилається незаконна команда або вимикається час спілкування робота.

Ваш бал - остаточне значення вантажу робота. Контролер видасть ваш результат та кінцеве зображення карти. Швидший вихід вашої програми реєструється у файлі robot.log. Якщо ваш робот помирає, у журналі може бути фатальна помилка.

Дані шахти

обладнання.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
Це особливість. Використовуйте це на свою користь, якщо зможете :)
Логічний лицар

Відповіді:


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, Robot Miner Шаблон 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.