您的程序将控制采矿机器人在地下搜索有价值的矿物。您的机器人会告诉控制器您要移动和挖掘的位置,控制器将提供有关机器人状态的反馈。
最初,将为您的机器人提供矿山的图像图,其中已经存在一些矿井,以及一个数据文件,用于指定矿山中矿物的值和硬度。然后,您的机器人将在轴中移动,以寻找有价值的矿物质。您的机器人可以挖土,但会因坚硬的岩石而减速。
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)从左上角引用的机器人位置的正整数。方向是机器人当前面对的方向(左,右,上,下)。
当您的机器人收到“ 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 3,Tunnel Terror,1352)。答案正文应包含您的代码和最终的地雷图图像。也欢迎其他图像或动画。获胜者将是得分最高的机器人。
其他规定
- 常见的漏洞是禁止的。
- 如果使用随机数生成器,则必须在程序中对种子进行硬编码,以便可以重现程序的运行。其他人必须能够运行您的程序并获得相同的最终地雷图像和分数。
- 您的程序必须针对任何防雷图像进行编程。您不能为这些数据文件或该图像大小,矿物布局,隧道布局等编写程序。如果我怀疑机器人违反了此规则,我保留更改地雷图像和/或数据文件的权利。
编辑
- 解释了0.1秒响应规则。
- 扩展了机器人启动命令行选项和文件的信息。
- 添加了具有更好错误捕获功能的新控制器版本。
- 添加了robot.log注释。
- 解释了默认的矿物硬度和值。
- 解释了电池vs切割机设备。
- 明确显示11号机器人。
- 添加了时间,刀具磨损和电池的计算。