Помогите нашим роботам добраться до телепорта


17

ОБНОВЛЕНИЕ: добавлена ​​среда Python для начала работы.

Космическую станцию ​​обогнали роботы-дробилки. Вы должны направить столько же наших дорогих и хрупких технических ботов, называемых «кроликами», к телепорту на выходе, прежде чем станция самоуничтожится, но роботы-дробилки патрулируют коридоры.

Ваша программа получает карту ASCII, а каждый ход - где находятся роботы-дробилки и ваши нынешние кролики. Ваша программа должна затем двигать ваших кроликов к выходному телепорту, избегая при этом ботов-дробилок.

демо анимация

выполнение

Запустите контроллер Python 2 с помощью:

python controller.py <mapfile> <turns> <seed> <runs> <prog>...
<prog> can be <interpreter> <yourprog> or similar.

Семя - это маленькое целое число, используемое для дробилки и вашей программы PRNG, поэтому прогоны повторяются. Ваша программа должна работать последовательно независимо от фактического используемого семян. Если seed равен нулю, контроллер будет использовать случайное seed для каждого запуска.

Контроллер запустит вашу программу с именем текстового файла карты и семенем в качестве аргументов. Например:

perl wandomwabbits.pl large.map 322

Если ваша программа использует PRNG, вы должны инициализировать его с заданным начальным числом. Затем контроллер отправляет обновления вашей программы через STDIN и считывает ваши движения кролика через STDOUT.

Каждый ход контроллер выводит 3 строки:

turnsleft <INT>
crusher <x,y> <movesto|crushes> <x,y>; ...
rabbits <x,y> <x,y> ...

затем ждет, пока программа выведет одну строку:

move <x,y> to <x,y>; ...

ОБНОВЛЕНИЕ: Ваша программа будет иметь 2 секунды для инициализации перед отправкой первых строк контроллером.

Если вашей программе требуется больше 0,5 секунд, чтобы ответить движениями после ввода местоположения кролика контроллера, контроллер выйдет.

Если в сетке нет кроликов, строка кроликов не будет иметь значений, и ваша программа должна вывести пустую строку «move».

Не забудьте очищать поток вывода программы каждый ход, иначе контроллер может зависнуть.

пример

прога ввода:

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

выход проги:

move 14,3 to 14,4; 14,2 to 14,3; 6,4 to 7,4

Логика контроллера

Логика для каждого хода:

  • если повороты влево равны нулю, напечатайте счет и выйдите.
  • для каждой пустой стартовой ячейки добавьте кролика, если дробилки не видно.
  • для каждой дробилки определите направление движения (см. ниже).
  • для каждой дробилки двигайтесь, если это возможно.
  • если дробилка находится на месте кролика, удалите кролика.
  • выходной поворотный вал, действия дробилки и расположение кроликов для программирования.
  • читать запросы на перемещение кролика из программы.
  • если кролик не существует или двигаться невозможно, пропустите.
  • сюжет каждой новой локации кроликов.
  • если кролик попадает в дробилку, кролик уничтожается.
  • если кролик находится в выходном телепорте, кролик удаляется и счет увеличивается.
  • если кролики сталкиваются, они оба уничтожаются.

Каждая дробилка всегда имеет направление движения (одно из NSEW). Дробилка следует этой логике навигации каждый ход:

  • Если один или несколько кроликов видны в любом из 4 ортогональных направлений, измените направление на одного из ближайших кроликов. Обратите внимание, что дробилки не могут видеть мимо другой дробилки.
  • иначе выберите случайным образом между открытыми вперед, влево, вправо, если это возможно.
  • иначе, если препятствия (настенная или другая дробилка) спереди, слева и справа, установите направление сзади.

Тогда для каждой дробилки:

  • Если в новом направлении дробилки нет препятствий, двигайтесь (и, возможно, сокрушайтесь).

Символы карты

Карта представляет собой прямоугольную сетку символов ASCII. Карта состоит из стен #, пространств коридора , стартовых позиций кроликов s, телепортов выхода eи стартовых локаций дробилки c. В верхнем левом углу находится местоположение (0,0).

Маленькая карта

###################
#        c        #
# # ######## # # ##
# ###s    #  ####e#
#   # # # ## ##   #
### # ###  # ## # #
#         ##      #
###################

Тестовая карта

#################################################################
#s                       ############################          s#
## ## ### ############ # #######                ##### ####### ###
## ## ### #            # ####### ########## # # ####   ###### ###
## ## ### # ############ ####### ##########     ##### ####### ###
## ## ##  #              ####### ########## # # ##### ####      #
##    ### #### #### ########     ##########     ##### #### ## ###
######### ####      ######## ################ ####### ####    ###
#########  ################# ################   c     ####### ###
######### ##################          ####### ####### ###########
######### ################## ######## #######         ###########
##### ###   c                          ###### ###################
#         #### ### # # # # # # # # # # ###### ##############    #
# ####### ####                         ###    ####     ##### ## #
#         #### ### # # # # # # # # # # ### # ###   #########    #
##### ### #### ###                   #####   ### #  ######## ####
############## ### # # # # # # # # # # #######   ##  ####### ####
#### #### #### ###                     ###   # # ###  ###### ####
##             ### # # # # # # # # # # ### ### #  ###  ##### ####
##### ######## ### # # # ##### # # # # ### ### # #####  #### ####
##### ##### ######         c   #       ### ###   ######  ### ####
##       c   ######################### ### ##### ####### ### ####
##### # ### #######   ########         ### ##### c  ##    ## ####
#####   #   ####### ########## ## ######## #     ######## ## ####
######### # #######            ## #     ## # # # #####     # ####
### ##### #     ### # ############## # ### #      ###  ## #  ####
#      ## # ### ### # ############## # ### ##### #####    ## ####
### ## ## #     ###                  #           ########       #
#s  ##      ###################################################e#
#################################################################

Пример запуска большой карты

большое демо

Гол

Чтобы оценить вашу программу, запустите контроллер с тестовой картой, 500 витков, 5 пробежек и начальное число 0. Ваша оценка - это общее количество кроликов, успешно телепортированных со станции в безопасное место. В случае ничьей победит ответ с наибольшим количеством голосов.

Ваш ответ должен включать заголовок с именем записи, используемым языком и оценкой. В тексте ответа включите вывод оценки контроллера вместе с начальными номерами, чтобы другие могли повторить ваши прогоны. Например:

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

Вы можете использовать стандартные и свободно доступные библиотеки, но стандартные лазейки запрещены. Вы не должны оптимизировать свою программу для заданного начального числа, числа оборотов, набора функций карты или других параметров. Я оставляю за собой право менять карту, количество ходов и семена, если я подозреваю нарушение этого правила.

Код контроллера

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

Контроллер создает текстовый журнал запусков run.logи серии изображений в animкаталоге. Если ваша установка Python не может найти библиотеку изображений PIL (скачать как подушку), изображения не будут созданы. Я анимировал серию изображений с помощью ImageMagick. Например:

convert -delay 100 -loop 0 anim/run01* run1anim.gif

Вы можете опубликовать интересные анимации или изображения с вашим ответом.

Вы можете отключить эти функции и ускорить работу контроллера, установив GRIDLOG = Falseи / или MKIMAGE = Falseв первых нескольких строках программы контроллера.

Предлагаемая среда Python

Чтобы помочь начать, вот фреймворк на Python. Первый шаг - прочитать файл карты и найти пути к выходам. В каждом ходу должен быть какой-то код для хранения данных о том, где находятся дробилки, и код, который решает, куда двигаться нашим кроликам. Самая простая стратегия, с которой нужно начинать, - это перемещать кроликов к выходу, игнорируя дробилки - некоторые кролики могут пройти.

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

Разрешено ли имитировать контроллер с точно таким же ГСЧ? Это эффективно сделало бы дробилки детерминированными, позволяя вам прогнозировать их поведение и избегать их. Черт, вы, вероятно, могли бы сделать одного или нескольких «кроликов-приманок», чтобы держать дробилки занятыми, устанавливая необузданную дорогу кроликов
orlp

Нет, вы не можете имитировать ГСЧ (или записать его для конкретного семени). Дробилки спроектированы так, чтобы НЕ быть детерминированными, так что это сложная задача для разработки стратегии, которая может их избежать. Идея «кролика-приманки», конечно, в порядке. Я ожидаю некоторых стратегий с участием жертвенных кроликов.
Логика Найт

Если seed равен 0, не будет ли каждый прогон использовать случайное seed?
TheNumberOne

Да. Если начальное число контроллера равно нулю, оно будет использовать (и выдавать тестовой программе) случайное начальное число для каждого запуска. Это семя сообщается с оценкой пробега. Подача этого семени обратно в контроллер должна привести к тому, что этот точный ход (и оценка) будет проверен. Это немного сложно, но это был лучший способ, которым я мог представить, чтобы включить репликацию прогонов (детерминистическую) и позволить случайность в поведении контроллера и тестовой программы.
Логика Найт

Ответы:


2

Crazy, Python 45

Я сделал 25 запусков со случайным начальным числом, мой компьютер недостаточно быстр, чтобы идти за 1000 (если кто-то хочет исправить счет). Первая программа на python, отладка для меня была трудной. Также я не знаю, использовал ли я это хорошо.

Он использует алгоритм выхода в ширину с выхода, один с учетом дробилок, другой без. У меня было много тайм-аута, поэтому я не пошел на что-то более сложное (удаление одной дробилки и т. Д.). Кролики также сходят с ума, если рядом есть дробилка в надежде заставить ее пойти по неверному пути.

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')]
width = len(grid[0])
height = len(grid)

starts = set([])
end = ()
walkables = set([])
crushers = set([])
#
# Process grid to find teleporters and paths to get there
#
for a in range(height):
    for b in range(width):
        if grid[a][b] == 'e':
            end = (b,a)
            walkables.add((b,a))
        elif grid[a][b] == 's':
            starts.add((b,a))
            walkables.add((b,a))
        elif grid[a][b] == 'c':
            crushers.add((b,a))
            walkables.add((b,a))
        elif grid[a][b] == ' ':
            walkables.add((b,a))

toSearch = [ (end, 0) ]
inSearch = [ end ]
visited = set([])
gradient = [[0]*height for x in range(width)]
while len(toSearch) > 0 :
    current = toSearch.pop(0)
    (row, col) = current[0]
    length = current[1]
    visited.add(current[0])
    neighbors = {(row+1,col),(row-1,col),(row,col+1),(row,col-1)}
    neighborSpaces = walkables & neighbors
    unvisited = neighborSpaces - visited
    for node in unvisited:
        if not node in inSearch:
            gradient[node[0]][node[1]]=[current[0][0],current[0][1]]
            inSearch.append(node)
            toSearch.append((node, length+1))
    toSearch.sort(key=lambda node: node[1])

while 1:
    msg = sys.stdin.readline()

    if msg.startswith('turnsleft'):
        turnsleft = int(msg.split()[1])

    elif msg.startswith('crusher'):
        # Update crushers
        actions = re.findall(r'(\d+),(\d+) (movesto|crushes) (\d+),(\d+)', msg)
        for one_action in actions:
            crushers.discard((int(one_action[0]),int(one_action[1])))
            crushers.add((int(one_action[3]),int(one_action[4])))

    elif msg.startswith('rabbits'):
        toSearch = [ (end, 0) ]
        inSearch = [ end ]
        visited = set([])
        gradient2 = [[0]*height for x in range(width)]
        while len(toSearch) > 0 :
            current = toSearch.pop(0)
            (row, col) = current[0]
            length = current[1]
            visited.add(current[0])
            neighbors = {(row+1,col),(row-1,col),(row,col+1),(row,col-1)}
            neighborSpaces = (walkables - crushers) & neighbors
            unvisited = neighborSpaces - visited
            for node in unvisited:
                if not node in inSearch:
                    gradient2[node[0]][node[1]]=[current[0][0],current[0][1]]
                    inSearch.append(node)
                    toSearch.append((node, length+1))
            toSearch.sort(key=lambda node: node[1])
        moves = []
        places = re.findall(r'(\d+),(\d+)', msg)
        for rabbit in [map(int, xy) for xy in places]:
            # If any crushers insight, go crazy to lose him
            directions = [(1,0),(-1,0),(0,1),(0,-1)]
            crazy = False
            newpos = 0
            for direction in directions:
                (row, col) = rabbit
                sight = 0
                while len({(row,col)} & walkables)>0 and sight<5 and crazy == False:
                    sight+=1
                    if (row,col) in crushers:
                        directions.remove(direction)
                        crazy = True
                    (row,col) = (row+direction[0],col+direction[1])
            for direction in directions:
                if not (rabbit[0]+direction[0],rabbit[1]+direction[1]) in walkables:
                    directions.remove(direction)
            if len(directions)==0:
                directions = [(1,0),(-1,0),(0,1),(0,-1)]
            direction = choice(directions)
            newpos = [rabbit[0]+direction[0],rabbit[1]+direction[1]]
            # Else use gradients
            if crazy == False:
                newpos = gradient2[rabbit[0]][rabbit[1]]
                if newpos == 0:
                    newpos = gradient[rabbit[0]][rabbit[1]]
            moves.append('%u,%u to %u,%u' % tuple(rabbit + newpos))
        print 'move ' + '; '.join(moves)
        sys.stdout.flush()

Анимация для среднего пробега


Я думаю, что у вас могут быть ошибки в вашем отступе. Ведущий пробел важен в Python. например: walkables.add((b,a))линии.
Логика Найт

должно работать нормально сейчас
Хит

1

Кролик, Ява, 26.385

Я усреднял количество баллов от 1 до 1000 и умножал на 5, чтобы получить мой результат. Я уверен, что это эквивалентно среднему баллу по всем возможным играм со стандартными опциями.

множество

import java.awt.Point;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.*;
import java.util.stream.Collectors;

public class Main {

    private static final char WALL = '#';
    private static final char CRUSHER = 'c';
    private static final char ESCAPE = 'e';
    private static final char HUTCH = 's';

    private int height;
    private int width;

    private char[][] map;
    private List<Point> escapes = new ArrayList<>();
    private List<Point> crushers = new ArrayList<>();
    private List<Point> rabbits = new ArrayList<>();
    private List<Point> hutches = new ArrayList<>();

    private BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    private PrintStream out = System.out;
    private int[][] distances;

    public Main(String[] args) throws Exception {
        loadMap(args[0]);
    }

    private void loadMap(String mapFileName) throws Exception {
        char[][] preMap = new BufferedReader(new FileReader(mapFileName))
                .lines()
                .map(String::toCharArray)
                .toArray(char[][]::new);

        width = preMap[0].length;
        height = preMap.length;

        map = new char[width][height];    //tranpose

        for (int x = 0; x < width; x++){
            for (int y = 0; y < height; y++){
                map[x][y] = preMap[y][x];
            }
        }

        processMap();

        distances = dijkstra();

    }

    private void processMap() {
        for (int x = 0; x < width; x++){
            for (int y = 0; y < height; y++){
                char c = map[x][y];
                Point p = new Point(x, y);
                if (c == CRUSHER){
                    crushers.add(p);
                }
                if (c == ESCAPE){
                    escapes.add(p);
                }
                if (c == HUTCH){
                    hutches.add(p);
                }
            }
        }
    }

    public static void main(String[] args) throws Exception {
        new Main(args).run();
    }

    private void run() throws Exception{
        while (true) {
            in.readLine();
            readCrushers();
            readRabbits();
            makeDecision();
        }
    }

    private void makeDecision() {
        Map<Point, Point> moves = new HashMap<>();

        for (Point rabbit : rabbits){
            Point bestDirection = null;
            for (Point p : pointsAroundInclusive(rabbit)){
                if (
                        (bestDirection == null ||
                                distances[p.x][p.y] < distances[bestDirection.x][bestDirection.y]
                        ) && !moves.entrySet().contains(p)){
                    bestDirection = p;
                }
            }
            if (bestDirection != null) {
                moves.put(rabbit, bestDirection);
            }
        }

        out.println("move" +
                moves.entrySet().stream().map(a -> {
                    Point l0 = a.getKey();
                    Point l1 = a.getValue();
                    return " " + l0.x + "," + l0.y + " to " + l1.x + "," + l1.y;
                }).collect(Collectors.joining(";")));
    }

    private List<Point> pointsAroundInclusive(Point point) {
        List<Point> result = pointsAroundExclusive(point);
        result.add(point);
        return result;
    }

    private int[][] dijkstra() {
        Queue<Point> queue = new LinkedList<>();
        Set<Point> scanned = new HashSet<>();
        queue.addAll(escapes);
        scanned.addAll(escapes);

        int[][] distances = new int[width][height];

        while (!queue.isEmpty()) {
            Point next = queue.remove();
            int distance = distances[next.x][next.y];

            pointsAroundExclusive(next).stream()
                    .filter(p -> !scanned.contains(p))
                    .forEach(p -> {
                        distances[p.x][p.y] = distance + 1;
                        scanned.add(p);
                        queue.add(p);
                    });
        }

        return distances;
    }

    private List<Point> pointsAroundExclusive(Point p) {
        Point[] around = new Point[]{
                new Point(p.x - 1, p.y),
                new Point(p.x + 1, p.y),
                new Point(p.x, p.y - 1),
                new Point(p.x, p.y + 1)
        };

        List<Point> result = new ArrayList<>(Arrays.asList(around));
        result.removeIf(a -> {
            if (a.x < 0 || a.x >= width){
                return true;
            }
            if (a.y < 0 || a.y >= height){
                return true;
            }
            char c = map[a.x][a.y];
            return c == WALL;
        });

        return result;
    }

    private void readRabbits() throws Exception {
        String[] locations = in.readLine().substring("rabbits".length()).trim().split("\\s");
        rabbits.clear();

        for (String location : locations){

            if (location.equals("")){
                continue;
            }

            String[] decomposed = location.split(",");

            int x = Integer.parseInt(decomposed[0]);
            int y = Integer.parseInt(decomposed[1]);

            rabbits.add(new Point(x, y));
        }

    }

    private void readCrushers() throws Exception {
        String[] locations = in.readLine().substring("crusher".length()).trim().split("(; )?\\d+,\\d+ (movesto|crushes) ");
        crushers.clear();

        for (String location : locations){

            if (location.equals("")){
                continue;
            }

            String[] decomposed = location.split(",");

            int x = Integer.parseInt(decomposed[0]);
            int y = Integer.parseInt(decomposed[1]);

            crushers.add(new Point(x, y));
        }
    }

}

У лучшего испытанного пробега есть семя 1000. Вот GIF этого:

введите описание изображения здесь

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.