建立一个采矿机器人


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)从左上角引用的机器人位置的正整数。方向是机器人当前面对的方向(左,右,上,下)。

当您的机器人收到“ endshift”或“ failed”输入时,您的程序将很快终止。您可能希望机器人将调试/性能数据首先写入文件。

控制器将接受4种可能的命令。direction left|right|up|down会将您的机器人指向该方向,并需要15个游戏秒。move <integer>会指示您的机器人向前移动或挖掘很多单位,这取决于切割的矿物的硬度和刀具的锋利度(请参见下文),需要一些时间。buy <equipment>将安装指定的设备并从您的货物价值中扣除成本,但前提是机器人位于水面(y值<= y的起始值)。设备安装需要300游戏秒。特殊命令snapshot将当前的地雷映像写入磁盘,并且不占用游戏时间。您可以使用快照来调试机器人或创建动画。

您的机器人将以100电池和100切割器清晰度启动。移动和转动会消耗少量电池电量。挖掘的用途更多,并且取决于矿物的硬度和刀具的当前清晰度。当您的机器人挖掘矿物时,切割机将失去锐度,这取决于所花费的时间和矿物的硬度。如果您的机器人有足够的货物价值,它可能会返回地面以购买新的电池或刀具。请注意,高质量的设备的初始有效性超过100%。电池的名称中包含字符串“ battery”,切割器的名称中包含“ cutter”(惊奇)。

以下关系定义了移动和切割:

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的电池电量。因此,如果不切割矿物或转动,该机器人可以在93个游戏分钟内以标准100电荷驱动5600个单位。

新增:机器人的宽度为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

地雷图片:

测试地雷

地雷图像可能具有Alpha通道,但未使用。

控制器

该控制器应与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 3Tunnel Terror1352)。答案正文应包含您的代码和最终的地雷图图像。也欢迎其他图像或动画。获胜者将是得分最高的机器人。

其他规定

  • 常见的漏洞是禁止的。
  • 如果使用随机数生成器,则必须在程序中对种子进行硬编码,以便可以重现程序的运行。其他人必须能够运行您的程序并获得相同的最终地雷图像和分数。
  • 您的程序必须针对任何防雷图像进行编程。您不能为这些数据文件或图像大小,矿物布局,隧道布局等编写程序。如果我怀疑机器人违反了此规则,我保留更改地雷图像和/或数据文件的权利。

编辑

  • 解释了0.1秒响应规则。
  • 扩展了机器人启动命令行选项和文件的信息。
  • 添加了具有更好错误捕获功能的新控制器版本。
  • 添加了robot.log注释。
  • 解释了默认的矿物硬度和值。
  • 解释了电池vs切割机设备。
  • 明确显示11号机器人。
  • 添加了时间,刀具磨损和电池的计算。

2
@TApicella 1.机器人将图像文件名作为参数,并且可以根据需要读取和处理它。控制器的图像将随着机器人的移动而变化,并且机器人将无法看到该图像。机器人可以使用PIL或其他OSS第三方库。2.机器人有2秒钟的初始化时间,然后每个命令响应0.1秒钟。
逻辑骑士

1
您应该在问题中记录每个命令响应0.1秒。
彼得·泰勒

1
@KeithRandall否。您必须从命令行中提供的文件名中读取图像和2个数据文件。它们可能会更改。
逻辑骑士

1
@TApicella我用一个可能有用的Python框架添加了另一个答案。
2015年

2
这是一个功能。如果可以的话,请使用它来发挥自己的优势:)
逻辑骑士

Answers:


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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.