마이닝 로봇 구축


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정수 레벨이 기준치에 대한 비율로 커터의 전류 선명도이다. xy값은 (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을 사용합니다. 따라서 로봇은 광물을 자르거나 돌리지 않는 경우 표준 100 회 충전으로 93 분 동안 5600 대의 유닛을 구동 할 수 있습니다.

새로운 기능 : 로봇의 너비는 11 픽셀이므로 각 픽셀 이동시 최대 11 픽셀을 자릅니다. 잘라낼 픽셀이 11 픽셀 미만이면 로봇이 이동하는 데 시간이 덜 걸리고 커터 마모가 줄어 듭니다. 광물 데이터 파일에 픽셀 색상을 지정하지 않으면 경도가 0이고 값이 0 인 여유 공간이됩니다.

시간이 다 떨어지거나 로봇 배터리가 소진되거나 로봇의 일부가 이미지 경계를 초과하거나 불법 명령이 전송되거나 로봇 통신 시간이 초과되면 실행이 종료됩니다.

당신의 점수는 로봇화물의 최종 가치입니다. 컨트롤러가 점수와 최종지도 이미지를 출력합니다. 프로그램의 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는 PIL 이미지 모듈을 얻기위한 Windows 용 다운로드 파일이라는 정보를 받았습니다.

현재 디렉토리에서 로봇 프로그램, 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 도움이 될만한 파이썬 프레임 워크로 다른 답변을 추가했습니다.
논리 기사

2
기능입니다. 당신이 할 수 있다면 그것을 활용하십시오 :)
Logic Knight

답변:


3

파이썬 2, 샘플 광부, 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.