Xây dựng Robot khai thác


12

Chương trình của bạn sẽ điều khiển một robot khai thác tìm kiếm dưới lòng đất các khoáng sản có giá trị. Robot của bạn sẽ báo cho bộ điều khiển nơi bạn muốn di chuyển và đào, và bộ điều khiển sẽ cung cấp phản hồi về trạng thái robot của bạn.

Ban đầu, robot của bạn sẽ được cung cấp một bản đồ hình ảnh của mỏ với một số trục khai thác đã có, và một tệp dữ liệu chỉ định giá trị và độ cứng của các khoáng chất trong mỏ. Robot của bạn sau đó sẽ di chuyển qua các trục tìm kiếm các khoáng chất có giá trị để khai thác. Robot của bạn có thể đào xuyên trái đất, nhưng bị làm chậm bởi đá cứng.

hình ảnh mỏ nhỏ

Robot trở lại với hàng hóa có giá trị nhất sau ca làm việc 24 giờ sẽ là người chiến thắng. Nó có vẻ là một thách thức phức tạp nhưng thật đơn giản để tạo ra một robot khai thác cơ bản (xem câu trả lời Robot khai thác mẫu bên dưới).

Hoạt động

Chương trình của bạn sẽ được khởi động bởi bộ điều khiển với hình ảnh mỏ, dữ liệu khoáng sản và tên tệp thiết bị. Robot có thể sử dụng hình ảnh mỏ và dữ liệu khoáng sản để tìm quặng có giá trị và tránh đá cứng. Robot cũng có thể muốn mua thiết bị từ danh sách thiết bị.

ví dụ: python driller.py mineimage.png minerals.txt equipmentlist.txt

Sau khoảng thời gian khởi tạo 2 giây, bộ điều khiển giao tiếp với chương trình robot thông qua stdin và stdout. Robot phải phản hồi bằng một hành động trong vòng 0,1 giây sau khi nhận được thông báo trạng thái.

Mỗi lượt, bộ điều khiển gửi cho robot một dòng trạng thái:

timeleft cargo battery cutter x y direction

ví dụ: 1087 4505 34.65 88.04 261 355 right

Số nguyên timeleftlà giây trò chơi còn lại trước khi kết thúc ca. Các cargolà giá trị số nguyên của các khoáng chất bạn đã khai thác cho đến nay ít hơn những gì bạn đã trả tiền cho các thiết bị. Các batterycấp độ là một tỷ lệ phần trăm số nguyên của sạc pin còn lại của bạn. Mức cutternguyên là độ sắc nét hiện tại của dao cắt theo phần trăm của giá trị tiêu chuẩn. Các giá trị xylà các số nguyên dương với vị trí robot được tham chiếu từ góc trên cùng bên trái tại (0, 0). Hướng là hướng hiện tại mà robot đang đối mặt (trái, phải, lên, xuống).

Khi robot của bạn nhận được đầu vào 'endshift' hoặc 'fail', chương trình của bạn sẽ sớm bị chấm dứt. Bạn có thể muốn robot của mình ghi dữ liệu gỡ lỗi / hiệu suất vào một tệp trước tiên.

Có 4 lệnh có thể bộ điều khiển sẽ chấp nhận. direction left|right|up|downsẽ hướng robot của bạn theo hướng đó và cần 15 giây trò chơi. move <integer>sẽ hướng dẫn robot của bạn di chuyển hoặc đào nhiều đơn vị về phía trước, điều này sẽ mất thời gian tùy thuộc vào độ cứng của vết cắt khoáng chất và độ sắc nét của máy cắt của bạn (xem bên dưới). buy <equipment>sẽ cài đặt thiết bị được chỉ định và khấu trừ chi phí từ giá trị hàng hóa của bạn, nhưng chỉ khi robot ở trên bề mặt (giá trị y <= giá trị y bắt đầu). Cài đặt thiết bị mất 300 giây trò chơi. Lệnh đặc biệt snapshotghi hình ảnh mỏ hiện tại vào đĩa và không mất thời gian chơi. Bạn có thể sử dụng ảnh chụp nhanh để gỡ lỗi robot của mình hoặc tạo hình động.

Robot của bạn sẽ bắt đầu với 100 pin và độ sắc nét của máy cắt. Di chuyển và xoay sử dụng một lượng nhỏ pin. Đào sử dụng nhiều hơn và là một chức năng của độ cứng của khoáng chất và độ sắc nét hiện tại của máy cắt. Khi robot của bạn đào vào khoáng chất, máy cắt sẽ mất độ sắc nét, tùy thuộc vào thời gian thực hiện và độ cứng của khoáng chất. Nếu robot của bạn có đủ giá trị hàng hóa, nó có thể quay trở lại bề mặt để mua pin hoặc máy cắt mới. Lưu ý rằng thiết bị chất lượng cao có hiệu quả ban đầu hơn 100%. Pin có chuỗi "pin" trong tên và (bất ngờ) máy cắt có "máy cắt" trong tên.

Các mối quan hệ sau xác định di chuyển và cắt:

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

Lưu ý rằng việc di chuyển 1 đơn vị mà không cắt bất kỳ khoáng chất nào sẽ mất 1 trò chơi thứ hai và sử dụng 0,0178 pin. Vì vậy, robot có thể lái 5600 đơn vị trong 93 phút trò chơi với một lần sạc 100 tiêu chuẩn, nếu nó không cắt giảm khoáng sản hoặc quay đầu.

MỚI: Robot rộng 11 pixel nên sẽ cắt tối đa 11 pixel với mỗi pixel chuyển động. Nếu có ít hơn 11 pixel để cắt, robot sẽ mất ít thời gian hơn để di chuyển và gây ra ít hao mòn hơn cho máy cắt. Nếu một màu pixel không được chỉ định trong tệp dữ liệu khoáng sản, thì đó là không gian trống có độ cứng bằng 0 và giá trị bằng không.

Việc chạy bị chấm dứt khi hết thời gian, pin robot cạn kiệt, một phần của robot vượt quá ranh giới hình ảnh, một lệnh bất hợp pháp được gửi hoặc hết thời gian liên lạc với robot.

Điểm của bạn là giá trị cuối cùng của hàng hóa robot. Bộ điều khiển sẽ xuất điểm của bạn và hình ảnh bản đồ cuối cùng. Đầu ra stderr của chương trình của bạn được ghi vào tệp robot.log. Nếu robot của bạn chết, lỗi nghiêm trọng có thể nằm trong nhật ký.

Dữ liệu mỏ

thiết bị.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

hình ảnh của tôi:

kiểm tra của tôi

Hình ảnh của tôi có thể có một kênh alpha, nhưng điều này không được sử dụng.

Bộ điều khiển

Bộ điều khiển nên hoạt động với Python 2.7 và yêu cầu thư viện PIL. Tôi đã được thông báo rằng Python Python là một bản tải xuống thân thiện với Windows để có được mô-đun hình ảnh PIL.

Khởi động bộ điều khiển với chương trình robot, cfg.py, tệp hình ảnh và dữ liệu trong thư mục hiện tại. Dòng lệnh được đề xuất là:

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

Ví dụ: python controller.py java underminer.class

Bộ điều khiển sẽ ghi một tệp robot.log và một tệp Finalmine.png vào cuối quá trình chạy.

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

Tệp cấu hình được liên kết (không được thay đổi):

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

Định dạng câu trả lời

Các câu trả lời phải có tiêu đề bao gồm ngôn ngữ lập trình, tên robot và điểm cuối cùng (như Python 3 , Tunnel Terror , 1352 ). Phần thân câu trả lời phải có mã của bạn và hình ảnh bản đồ mỏ cuối cùng. Các hình ảnh hoặc hình ảnh động khác cũng được chào đón. Người chiến thắng sẽ là robot có số điểm tốt nhất.

Các quy tắc khác

  • Các sơ hở phổ biến đều bị cấm.
  • Nếu bạn sử dụng trình tạo số ngẫu nhiên, bạn phải mã hóa hạt giống trong chương trình của mình để chương trình của bạn chạy được. Một số người khác phải có khả năng chạy chương trình của bạn và có được hình ảnh và điểm số cuối cùng của tôi.
  • Chương trình của bạn phải được lập trình cho bất kỳ hình ảnh của tôi. Bạn không được mã hóa chương trình của mình cho các tệp dữ liệu này hoặc kích thước hình ảnh, bố cục khoáng sản, bố cục đường hầm, v.v ... Nếu tôi nghi ngờ robot vi phạm quy tắc này, tôi có quyền thay đổi tệp hình ảnh và / hoặc tệp dữ liệu.

Chỉnh sửa

  • Giải thích quy tắc trả lời 0,1 giây.
  • Mở rộng trên các tùy chọn dòng lệnh và robot bắt đầu.
  • Đã thêm phiên bản điều khiển mới với khả năng bắt lỗi tốt hơn.
  • Đã thêm ghi chú robot.log.
  • Giải thích độ cứng và giá trị khoáng sản mặc định.
  • Giải thích về pin vs thiết bị cắt.
  • Làm robot kích thước 11 rõ ràng.
  • Đã thêm tính toán cho thời gian, hao mòn máy cắt và pin.

2
@TApicella 1. Robot lấy tên tệp hình ảnh làm đối số và có thể đọc và xử lý nó theo ý muốn. Hình ảnh bộ điều khiển sẽ thay đổi khi robot di chuyển và robot sẽ không thể nhìn thấy điều đó. Robot có thể sử dụng PIL hoặc các thư viện bên thứ ba OSS khác. 2. Robot có 2 giây để khởi tạo và sau đó 0,1 giây cho mỗi phản hồi lệnh.
Logic Knight

1
Bạn nên ghi lại 0,1 giây cho mỗi câu trả lời trong câu hỏi.
Peter Taylor

1
@KeithRandall Không. Bạn phải đọc trong ảnh và 2 tệp dữ liệu từ tên tệp được cung cấp trên dòng lệnh. Họ có thể được thay đổi.
Logic Knight

1
@TApicella Tôi đã thêm một câu trả lời khác với khung Python có thể giúp ích.
Logic Knight

2
Đó là một tính năng. Sử dụng nó để lợi thế của bạn nếu bạn có thể :)
Logic Knight

Câu trả lời:


3

Python 2, Công cụ khai thác mẫu, 350

Đây là một ví dụ về mã tối thiểu cho một robot khai thác. Nó chỉ đào thẳng xuống cho đến khi hết pin (tất cả các robot bắt đầu chỉ xuống). Nó chỉ kiếm được số điểm 350. Hãy nhớ xả stdout nếu không bộ điều khiển sẽ bị treo.

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

đường dẫn khai thác mẫu


2

Python 2, Robot khai thác mẫu Python, 410

Đây là một mẫu robot khai thác để hiển thị cách robot hoạt động và cung cấp khung để xây dựng robot của riêng bạn. Có một phần để phân tích dữ liệu khoáng sản và một phần để phản hồi bằng các hành động. Các thuật toán giữ chỗ không làm tốt. Robot tìm thấy một số khoáng sản có giá trị, nhưng không đủ để mua đủ pin thay thế và máy cắt. Nó dừng lại với một cục pin chết trên đường đến bề mặt lần thứ hai.

Một kế hoạch tốt hơn là sử dụng các đường hầm hiện có để đến gần các khoáng sản có giá trị và giảm thiểu việc đào.

Lưu ý rằng robot này viết một tệp nhật ký của mỗi thông báo trạng thái mà nó nhận được để bạn có thể kiểm tra các quyết định của nó sau khi chạy.

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()

bản đồ mỏ cuối cùng


Cảm ơn bạn rất nhiều, phơi bày vòng lặp thúc đẩy sự tương tác giữa bộ điều khiển và chương trình robot thực sự hữu ích.
TApicella 5/2/2015
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.