CẬP NHẬT: Đã thêm một khung Python để bắt đầu.
Trạm vũ trụ đã bị vượt qua bởi các máy nghiền-bot. Bạn phải hướng dẫn nhiều bot công nghệ đắt tiền và dễ vỡ của chúng tôi gọi là "thỏ" đến một dịch chuyển tức thời trước khi trạm tự hủy, nhưng các bot-máy nghiền đang tuần tra trên các hành lang.
Chương trình của bạn được cung cấp một bản đồ ASCII và mỗi lượt được cho biết nơi mà các máy nghiền và các con thỏ hiện tại của bạn đang ở. Chương trình của bạn sau đó sẽ di chuyển những con thỏ của bạn về phía dịch chuyển tức thời trong khi tránh các máy nghiền-bot.
Chấp hành
Chạy bộ điều khiển Python 2 với:
python controller.py <mapfile> <turns> <seed> <runs> <prog>...
<prog> can be <interpreter> <yourprog> or similar.
Hạt giống là một số nguyên nhỏ được sử dụng cho máy nghiền và PRNG chương trình của bạn để các lần chạy được lặp lại. Chương trình của bạn nên thực hiện nhất quán bất kể hạt giống thực tế được sử dụng. Nếu hạt giống bằng 0, bộ điều khiển sẽ sử dụng một hạt giống ngẫu nhiên cho mỗi lần chạy.
Bộ điều khiển sẽ chạy chương trình của bạn với tên của tệp văn bản bản đồ và khởi tạo làm đối số. Ví dụ:
perl wandomwabbits.pl large.map 322
Nếu chương trình của bạn sử dụng PRNG, bạn nên khởi tạo nó với hạt giống đã cho. Sau đó, bộ điều khiển sẽ gửi các cập nhật chương trình của bạn thông qua STDIN và đọc chuyển động của thỏ thông qua STDOUT.
Mỗi lượt điều khiển sẽ xuất ra 3 dòng:
turnsleft <INT>
crusher <x,y> <movesto|crushes> <x,y>; ...
rabbits <x,y> <x,y> ...
sau đó chờ chương trình xuất ra một dòng:
move <x,y> to <x,y>; ...
CẬP NHẬT: Chương trình của bạn sẽ có 2 giây để khởi tạo trước khi các dòng đầu tiên được gửi bởi bộ điều khiển.
Nếu chương trình của bạn mất hơn 0,5 giây để phản hồi với các di chuyển sau khi nhập vị trí thỏ của bộ điều khiển, bộ điều khiển sẽ thoát.
Nếu không có thỏ trên lưới, dòng thỏ sẽ không có giá trị và chương trình của bạn sẽ xuất ra một chuỗi chuỗi "di chuyển" trần.
Nhớ xả luồng đầu ra chương trình của bạn mỗi lượt hoặc bộ điều khiển có thể bị treo.
Thí dụ
đầu vào prog:
turnsleft 35
crusher 22,3 crushes 21,3; 45,5 movesto 45,4
rabbits 6,4 8,7 7,3 14,1 14,2 14,3
đầu ra prog:
move 14,3 to 14,4; 14,2 to 14,3; 6,4 to 7,4
Bộ điều khiển logic
Logic cho mỗi lượt:
- nếu rẽ trái bằng 0, in điểm và thoát.
- đối với mỗi ô bắt đầu trống, thêm một con thỏ nếu không có máy nghiền trong tầm nhìn.
- cho mỗi máy nghiền, quyết định hướng di chuyển (xem bên dưới).
- cho mỗi máy nghiền, di chuyển nếu có thể.
- nếu máy nghiền ở vị trí thỏ, loại bỏ thỏ.
- đầu ra quay vòng, hành động máy nghiền và vị trí thỏ để lập trình.
- đọc yêu cầu di chuyển thỏ từ chương trình.
- Nếu một con thỏ không tồn tại hoặc di chuyển không thể, bỏ qua.
- vẽ từng vị trí mới của thỏ.
- nếu thỏ đâm vào máy nghiền, thỏ bị phá hủy.
- nếu thỏ ở trong dịch chuyển tức thời, thỏ sẽ bị loại bỏ và tăng điểm.
- nếu thỏ va chạm, cả hai đều bị tiêu diệt.
Mỗi máy nghiền luôn có một hướng tiêu đề (một trong NSEW). Một máy nghiền tuân theo logic điều hướng này mỗi lượt:
- Nếu một hoặc nhiều con thỏ có thể nhìn thấy theo bất kỳ hướng nào trong 4 hướng trực giao, hãy đổi hướng sang một trong những con thỏ gần nhất. Lưu ý rằng máy nghiền không thể nhìn qua máy nghiền khác.
- khác chọn ngẫu nhiên giữa các tùy chọn mở về phía trước, bên trái, bên phải nếu có thể.
- khác nếu chướng ngại vật (tường hoặc máy nghiền khác) ở phía trước, bên trái và bên phải, đặt hướng về phía sau.
Sau đó cho mỗi máy nghiền:
- Nếu không có chướng ngại vật trong hướng máy nghiền mới, di chuyển (và có thể nghiền nát).
Các biểu tượng bản đồ
Bản đồ là một lưới hình chữ nhật gồm các ký tự ASCII. Bản đồ được tạo thành từ các bức tường
#
, không gian hành lang , vị trí bắt đầu của thỏ
s
, dịch chuyển tức thời e
và các vị trí bắt đầu máy nghiền c
. Góc trên bên trái là vị trí (0,0).
Bản đồ nhỏ
###################
# c #
# # ######## # # ##
# ###s # ####e#
# # # # ## ## #
### # ### # ## # #
# ## #
###################
Bản đồ kiểm tra
#################################################################
#s ############################ s#
## ## ### ############ # ####### ##### ####### ###
## ## ### # # ####### ########## # # #### ###### ###
## ## ### # ############ ####### ########## ##### ####### ###
## ## ## # ####### ########## # # ##### #### #
## ### #### #### ######## ########## ##### #### ## ###
######### #### ######## ################ ####### #### ###
######### ################# ################ c ####### ###
######### ################## ####### ####### ###########
######### ################## ######## ####### ###########
##### ### c ###### ###################
# #### ### # # # # # # # # # # ###### ############## #
# ####### #### ### #### ##### ## #
# #### ### # # # # # # # # # # ### # ### ######### #
##### ### #### ### ##### ### # ######## ####
############## ### # # # # # # # # # # ####### ## ####### ####
#### #### #### ### ### # # ### ###### ####
## ### # # # # # # # # # # ### ### # ### ##### ####
##### ######## ### # # # ##### # # # # ### ### # ##### #### ####
##### ##### ###### c # ### ### ###### ### ####
## c ######################### ### ##### ####### ### ####
##### # ### ####### ######## ### ##### c ## ## ####
##### # ####### ########## ## ######## # ######## ## ####
######### # ####### ## # ## # # # ##### # ####
### ##### # ### # ############## # ### # ### ## # ####
# ## # ### ### # ############## # ### ##### ##### ## ####
### ## ## # ### # ######## #
#s ## ###################################################e#
#################################################################
Ví dụ chạy bản đồ lớn
Ghi bàn
Để đánh giá chương trình của bạn, hãy chạy bộ điều khiển với bản đồ kiểm tra, 500 lượt, 5 lần chạy và hạt giống 0. Điểm của bạn là tổng số thỏ được dịch chuyển thành công ra khỏi trạm đến nơi an toàn. Trong trường hợp hòa, câu trả lời có nhiều phiếu nhất sẽ giành chiến thắng.
Câu trả lời của bạn nên bao gồm một tiêu đề với tên mục nhập, ngôn ngữ được sử dụng và điểm số. Trong phần thân câu trả lời, vui lòng bao gồm đầu ra điểm của bộ điều khiển hoàn thành với số hạt giống để những người khác có thể lặp lại các lần chạy của bạn. Ví dụ:
Running: controller.py small.map 100 0 5 python bunny.py
Run Seed Score
1 965 0
2 843 6
3 749 11
4 509 10
5 463 3
Total Score: 30
Bạn có thể sử dụng các thư viện tiêu chuẩn và có sẵn miễn phí nhưng các lỗ hổng tiêu chuẩn bị cấm. Bạn không được tối ưu hóa chương trình của mình cho một hạt giống nhất định, số lần lượt, bộ tính năng bản đồ hoặc các tham số khác. Tôi bảo lưu quyền thay đổi bản đồ, số lượt và hạt giống nếu tôi nghi ngờ vi phạm quy tắc này.
Mã điều khiển
#!/usr/bin/env python
# Control Program for the Rabbit Runner on PPCG.
# Usage: controller.py <mapfile> <turns> <seed> <runs> <prog>...
# Tested on Python 2.7 on Ubuntu Linux. May need edits for other platforms.
# v1.0 First release.
# v1.1 Fixed crusher reporting bug.
# v1.2 Control for animation image production.
# v1.3 Added time delay for program to initialise
import sys, subprocess, time, re, os
from random import *
# Suggest installing Pillow if you don't have PIL already
try:
from PIL import Image, ImageDraw
except:
Image, ImageDraw = None, None
GRIDLOG = True # copy grid to run.log each turn (off for speed)
MKIMAGE = False # animation image creation (much faster when off)
IMGWIDTH = 600 # animation image width estimate
INITTIME = 2 # Allow 2 seconds for the program to initialise
point = complex # use complex numbers as 2d integer points
ORTH = [1, -1, 1j, -1j] # all 4 orthogonal directions
def send(proc, msg):
proc.stdin.write((msg+'\n').encode('utf-8'))
proc.stdin.flush()
def read(proc):
return proc.stdout.readline().decode('utf-8')
def cansee(cell):
# return a dict of visible cells containing robots with distances
see = {} # see[cell] = dist
robots = rabbits | set(crushers)
if cell in robots:
see[cell] = 0
for direc in ORTH:
for dist in xrange(1,1000):
test = cell + direc*dist
if test in walls:
break
if test in robots:
see[test] = dist
if test in crushers:
break # can't see past them
return see
def bestdir(cr, direc):
# Decide in best direction for this crusher-bot
seen = cansee(cr)
prey = set(seen) & rabbits
if prey:
target = min(prey, key=seen.get) # Find closest
vector = target - cr
return vector / abs(vector)
obst = set(crushers) | walls
options = [d for d in ORTH if d != -direc and cr+d not in obst]
if options:
return choice(options)
return -direc
def features(fname):
# Extract the map features
walls, crusherstarts, rabbitstarts, exits = set(), set(), set(), set()
grid = [line.strip() for line in open(fname, 'rt')]
grid = [line for line in grid if line and line[0] != ';']
for y,line in enumerate(grid):
for x,ch in enumerate(line):
if ch == ' ': continue
cell = point(x,y)
if ch == '#': walls.add(cell)
elif ch == 's': rabbitstarts.add(cell)
elif ch == 'e': exits.add(cell)
elif ch == 'c': crusherstarts.add(cell)
return grid, walls, crusherstarts, rabbitstarts, exits
def drawrect(draw, cell, scale, color, size=1):
x, y = int(cell.real)*scale, int(cell.imag)*scale
edge = int((1-size)*scale/2.0 + 0.5)
draw.rectangle([x+edge, y+edge, x+scale-edge, y+scale-edge], fill=color)
def drawframe(runno, turn):
if Image == None:
return
scale = IMGWIDTH/len(grid[0])
W, H = scale*len(grid[0]), scale*len(grid)
img = Image.new('RGB', (W,H), (255,255,255))
draw = ImageDraw.Draw(img)
for cell in rabbitstarts:
drawrect(draw, cell, scale, (190,190,255))
for cell in exits:
drawrect(draw, cell, scale, (190,255,190))
for cell in walls:
drawrect(draw, cell, scale, (190,190,190))
for cell in crushers:
drawrect(draw, cell, scale, (255,0,0), 0.8)
for cell in rabbits:
drawrect(draw, cell, scale, (0,0,255), 0.4)
img.save('anim/run%02uframe%04u.gif' % (runno, turn))
def text2point(textpoint):
# convert text like "22,6" to point object
return point( *map(int, textpoint.split(',')) )
def point2text(cell):
return '%i,%i' % (int(cell.real), int(cell.imag))
def run(number, nseed):
score = 0
turnsleft = turns
turn = 0
seed(nseed)
calltext = program + [mapfile, str(nseed)]
process = subprocess.Popen(calltext,
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=errorlog)
time.sleep(INITTIME)
rabbits.clear()
crushers.clear()
crushers.update( dict((cr, choice(ORTH)) for cr in crusherstarts) )
while turnsleft > 0:
# for each empty start cell, add a rabbit if no crusher in sight.
for cell in rabbitstarts:
if cell in rabbits or set(cansee(cell)) & set(crushers):
continue
rabbits.add(cell)
# write the grid to the runlog and create image frames
if GRIDLOG:
for y,line in enumerate(grid):
for x,ch in enumerate(line):
cell = point(x,y)
if cell in crushers: ch = 'X'
elif cell in rabbits: ch = 'o'
runlog.write(ch)
runlog.write('\n')
runlog.write('\n\n')
if MKIMAGE:
drawframe(number, turn)
# for each crusher, decide move direction.
for cr, direc in crushers.items():
crushers[cr] = bestdir(cr, direc)
# for each crusher, move if possible.
actions = []
for cr, direc in crushers.items():
newcr = cr + direc
if newcr in walls or newcr in crushers:
continue
crushers[newcr] = crushers.pop(cr)
action = ' movesto '
# if crusher is at a rabbit location, remove rabbit.
if newcr in rabbits:
rabbits.discard(newcr)
action = ' crushes '
actions.append(point2text(cr)+action+point2text(newcr))
# output turnsleft, crusher actions, and rabbit locations to program.
send(process, 'turnsleft %u' % turnsleft)
send(process, 'crusher ' + '; '.join(actions))
rabbitlocs = [point2text(r) for r in rabbits]
send(process, ' '.join(['rabbits'] + rabbitlocs))
# read rabbit move requests from program.
start = time.time()
inline = read(process)
if time.time() - start > 0.5:
print 'Move timeout'
break
# if a rabbit not exist or move not possible, no action.
# if rabbit hits a crusher, rabbit is destroyed.
# if rabbit is in exit teleporter, rabbit is removed and score increased.
# if two rabbits collide, they are both destroyed.
newrabbits = set()
for p1,p2 in re.findall(r'(\d+,\d+)\s+to\s+(\d+,\d+)', inline):
p1, p2 = map(text2point, [p1,p2])
if p1 in rabbits and p2 not in walls:
if p2-p1 in ORTH:
rabbits.discard(p1)
if p2 in crushers:
pass # wabbit squished
elif p2 in exits:
score += 1 # rabbit saved
elif p2 in newrabbits:
newrabbits.discard(p2) # moving rabbit collision
else:
newrabbits.add(p2)
# plot each new location of rabbits.
for rabbit in newrabbits:
if rabbit in rabbits:
rabbits.discard(rabbit) # still rabbit collision
else:
rabbits.add(rabbit)
turnsleft -= 1
turn += 1
process.terminate()
return score
mapfile = sys.argv[1]
turns = int(sys.argv[2])
argseed = int(sys.argv[3])
runs = int(sys.argv[4])
program = sys.argv[5:]
errorlog = open('error.log', 'wt')
runlog = open('run.log', 'wt')
grid, walls, crusherstarts, rabbitstarts, exits = features(mapfile)
rabbits = set()
crushers = dict()
if 'anim' not in os.listdir('.'):
os.mkdir('anim')
for fname in os.listdir('anim'):
os.remove(os.path.join('anim', fname))
total = 0
print 'Running:', ' '.join(sys.argv)
print >> runlog, 'Running:', ' '.join(sys.argv)
fmt = '%10s %20s %10s'
print fmt % ('Run', 'Seed', 'Score')
for n in range(runs):
nseed = argseed if argseed else randint(1,1000)
score = run(n, nseed)
total += score
print fmt % (n+1, nseed, score)
print 'Total Score:', total
print >> runlog, 'Total Score:', total
Bộ điều khiển tạo một bản ghi văn bản của các lần chạy run.log
và một loạt các hình ảnh trong anim
thư mục. Nếu cài đặt Python của bạn không thể tìm thấy thư viện hình ảnh PIL (tải xuống dưới dạng Gối), sẽ không có hình ảnh nào được tạo. Tôi đã làm hoạt hình cho loạt hình ảnh với ImageMagick. Ví dụ:
convert -delay 100 -loop 0 anim/run01* run1anim.gif
Bạn được chào đón để gửi hình ảnh động thú vị hoặc hình ảnh với câu trả lời của bạn.
Bạn có thể tắt các tính năng này và tăng tốc bộ điều khiển bằng cách cài đặt GRIDLOG
= False
và / hoặc MKIMAGE = False
trong một vài dòng đầu tiên của chương trình điều khiển.
Khung Python được đề xuất
Để giúp bắt đầu, đây là một khung trong Python. Bước đầu tiên là đọc trong tệp bản đồ và tìm đường dẫn đến lối thoát hiểm. Trong mỗi lượt nên có một số mã để lưu trữ nơi máy nghiền và mã quyết định nơi di chuyển thỏ của chúng tôi. Chiến lược đơn giản nhất để bắt đầu là di chuyển những con thỏ về phía lối ra bỏ qua những kẻ nghiền nát - một số con thỏ có thể vượt qua.
import sys, re
from random import *
mapfile = sys.argv[1]
argseed = int(sys.argv[2])
seed(argseed)
grid = [line.strip() for line in open(mapfile, 'rt')]
#
# Process grid to find teleporters and paths to get there
#
while 1:
msg = sys.stdin.readline()
if msg.startswith('turnsleft'):
turnsleft = int(msg.split()[1])
elif msg.startswith('crusher'):
actions = re.findall(r'(\d+),(\d+) (movesto|crushes) (\d+),(\d+)', msg)
#
# Store crusher locations and movement so we can avoid them
#
elif msg.startswith('rabbits'):
moves = []
places = re.findall(r'(\d+),(\d+)', msg)
for rabbit in [map(int, xy) for xy in places]:
#
# Compute the best move for this rabbit
newpos = nextmoveforrabbit(rabbit)
#
moves.append('%u,%u to %u,%u' % tuple(rabbit + newpos))
print 'move ' + '; '.join(moves)
sys.stdout.flush()