Bantu robot kami mencapai teleporter


17

PEMBARUAN: Menambahkan kerangka kerja Python untuk memulai.

Stasiun luar angkasa telah diambil alih oleh bot crusher. Anda harus mengarahkan banyak bot teknologi kami yang mahal dan rapuh yang disebut "kelinci" ke teleporter keluar sebelum stasiun hancur sendiri, tetapi bot crusher sedang berpatroli di koridor.

Program Anda diberi peta ASCII, dan setiap belokan diberi tahu di mana bot penghancur berada dan kelinci Anda saat ini. Program Anda kemudian harus memindahkan kelinci Anda ke arah teleporter keluar sambil menghindari bot crusher.

animasi demo

Eksekusi

Jalankan pengontrol Python 2 dengan:

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

Benih adalah bilangan bulat kecil yang digunakan untuk crusher dan program Anda PRNG sehingga menjalankan berulang. Program Anda harus melakukan secara konsisten terlepas dari benih yang sebenarnya digunakan. Jika seed bernilai nol, controller akan menggunakan seed acak untuk setiap run.

Pengontrol akan menjalankan program Anda dengan nama file teks peta dan seed sebagai argumen. Misalnya:

perl wandomwabbits.pl large.map 322

Jika program Anda menggunakan PRNG, Anda harus menginisialisasi dengan seed yang diberikan. Pengontrol kemudian mengirimkan pembaruan program Anda melalui STDIN dan membaca gerakan kelinci Anda melalui STDOUT.

Setiap belokan controller akan menghasilkan 3 baris:

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

kemudian tunggu program mengeluarkan satu baris:

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

UPDATE: Program Anda akan memiliki 2 detik untuk menginisialisasi sebelum baris pertama dikirim oleh controller.

Jika program Anda membutuhkan waktu lebih dari 0,5 detik untuk merespons dengan gerakan setelah input lokasi kelinci controller, controller akan keluar.

Jika tidak ada kelinci di kisi, garis kelinci tidak akan memiliki nilai, dan program Anda harus menampilkan garis string "pindahkan" yang telanjang.

Ingatlah untuk menyiram aliran output program Anda setiap belokan atau pengontrol mungkin menggantung.

Contoh

input 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

keluaran prog:

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

Logika pengontrol

Logika untuk setiap belokan:

  • jika belok kiri adalah nol, cetak skor dan keluar.
  • untuk setiap sel awal yang kosong, tambahkan kelinci jika tidak ada penghancur yang terlihat.
  • untuk setiap crusher, tentukan arah gerakan (lihat di bawah).
  • untuk setiap crusher, bergerak jika memungkinkan.
  • Jika penghancur berada di lokasi kelinci, lepaskan kelinci.
  • Output putaran, tindakan crusher, dan lokasi kelinci ke program.
  • baca permintaan pindah kelinci dari program.
  • jika kelinci tidak ada atau bergerak tidak mungkin, lewati.
  • plot setiap lokasi baru kelinci.
  • jika kelinci mengenai crusher, kelinci dihancurkan.
  • jika kelinci keluar dari teleporter, kelinci dilepas dan nilainya meningkat.
  • jika kelinci bertabrakan, mereka berdua dihancurkan.

Setiap crusher selalu memiliki arah tajuk (salah satu NSEW). Crusher mengikuti logika navigasi ini setiap belokan:

  • Jika satu atau lebih kelinci terlihat di salah satu dari 4 arah ortogonal, ubah arah ke salah satu kelinci terdekat. Perhatikan bahwa crusher tidak dapat melihat melewati crusher lain.
  • lain pilih secara acak antara opsi buka ke depan, kiri, kanan jika memungkinkan.
  • lain jika rintangan (dinding atau penghancur lainnya) di depan, kiri, dan kanan, tetapkan arah ke belakang.

Kemudian untuk setiap crusher:

  • Jika tidak ada penghalang dalam arah penghancur baru, bergerak (dan mungkin hancurkan).

Simbol peta

Peta adalah kisi-kisi persegi panjang karakter ASCII. Peta tersebut terdiri dari dinding #, ruang koridor , posisi awal kelinci s, keluar dari teleporter e, dan lokasi mulai penghancur c. Pojok kiri atas adalah lokasi (0,0).

Peta kecil

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

Tes peta

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

Contoh menjalankan peta besar

demo besar

Skor

Untuk mengevaluasi program Anda, jalankan pengontrol dengan peta uji, 500 putaran, 5 putaran, dan seed of 0. Skor Anda adalah jumlah total kelinci yang berhasil dipindahkan dari stasiun ke tempat yang aman. Dalam hal seri, jawaban dengan suara terbanyak akan menang.

Jawaban Anda harus mencakup judul dengan nama entri, bahasa yang digunakan, dan skor. Di badan jawaban, harap sertakan output skor pengontrol lengkap dengan nomor seed sehingga orang lain dapat mengulangi menjalankan Anda. Sebagai contoh:

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

Anda dapat menggunakan perpustakaan standar dan tersedia secara bebas tetapi celah standar dilarang. Anda tidak boleh mengoptimalkan program Anda untuk seed yang diberikan, hitung giliran, set fitur peta, atau parameter lainnya. Saya berhak mengubah peta, mengubah hitungan, dan mengunggah jika saya menduga ada pelanggaran terhadap aturan ini.

Kode pengontrol

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

Pengontrol membuat log teks dari berjalan di run.logdan serangkaian gambar di animdirektori. Jika instalasi Python Anda tidak dapat menemukan pustaka gambar PIL (unduh sebagai Bantal), tidak akan ada gambar yang dihasilkan. Saya telah menjiwai seri gambar dengan ImageMagick. Misalnya:

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

Anda dipersilakan untuk mengirim animasi atau gambar yang menarik dengan jawaban Anda.

Anda dapat mematikan fitur-fitur ini dan mempercepat pengontrol dengan mengatur GRIDLOG = Falsedan / atauMKIMAGE = False di beberapa baris pertama dari program pengontrol.

Kerangka Python yang disarankan

Untuk membantu memulai, berikut adalah kerangka kerja dalam Python. Langkah pertama adalah membaca di file peta dan menemukan jalur ke pintu keluar. Di setiap belokan harus ada beberapa kode untuk menyimpan di mana penghancur berada, dan kode yang memutuskan ke mana harus memindahkan kelinci kita. Strategi paling sederhana untuk memulainya adalah menggerakkan kelinci menuju jalan keluar tanpa menghiraukan penghancur - beberapa kelinci mungkin bisa melewatinya.

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

Apakah diizinkan untuk mensimulasikan pengontrol, dengan RNG yang sama persis? Ini secara efektif akan membuat crusher menjadi deterministik, memungkinkan Anda untuk memprediksi perilaku mereka dan menghindarinya. Sial, Anda mungkin bisa membuat satu atau beberapa 'kelinci umpan' untuk membuat penghancur tetap sibuk saat menyiapkan jalan raya kelinci yang tidak
terikat

Tidak, Anda tidak dapat mensimulasikan RNG (atau merekamnya untuk seed tertentu). Penghancur dirancang untuk TIDAK deterministik, jadi ini adalah tantangan kode untuk membuat strategi yang cenderung menghindarinya. Ide 'umpan kelinci' tentu saja ok. Saya mengharapkan beberapa strategi yang melibatkan kelinci pengorbanan.
Logic Knight

Jika seed adalah 0, tidak akankah setiap run menggunakan seed secara acak?
TheNumberOne

Iya. Jika seed controller nol, ia akan menggunakan (dan mengeluarkan ke program uji) seed acak untuk setiap run. Benih ini dilaporkan dengan skor lari. Mengumpankan benih ini kembali ke controller harus menyebabkan proses yang tepat (dan skor) diverifikasi. Ini agak rumit, tapi itu adalah cara terbaik yang saya bisa pikirkan untuk memungkinkan replikasi berjalan (deterministik) dan memungkinkan keacakan dalam kontroler dan perilaku program pengujian.
Logic Knight

Jawaban:


2

Gila, Python 45

Saya melakukan 25 kali dengan seed acak, komputer saya tidak cukup cepat untuk mendapatkan 1000 (jika seseorang ingin memperbaiki skor) Program pertama dengan python, sungguh menyebalkan untuk men-debug untuk saya. Saya juga tidak tahu apakah saya menggunakannya dengan baik.

Ini menggunakan algoritma pertama dari keluar, satu memperhitungkan penghancur, yang lain tanpa. Saya punya banyak waktu habis jadi saya tidak pergi untuk sesuatu yang lebih kompleks (satu penghapusan crusher, dll.). Kelinci-kelinci juga menjadi gila jika ada mesin penghancur di dekatnya dengan harapan bisa membawanya ke jalan yang salah.

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

Animasi untuk rata-rata lari


Saya pikir Anda mungkin memiliki kesalahan dalam lekukan Anda. Memimpin ruang putih penting dalam Python. misal: walkables.add((b,a))garis.
Logic Knight

seharusnya berfungsi dengan baik sekarang
Hit

1

Kelinci, Jawa, 26.385

Saya rata-rata skor menjalankan 1 hingga 1000 dan dikalikan dengan 5 untuk mendapatkan skor saya. Saya cukup yakin ini setara dengan skor rata-rata dari semua game yang mungkin dengan opsi standar.

Skor

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));
        }
    }

}

Hasil uji terbaik memiliki benih 1000. Berikut ini adalah GIF:

masukkan deskripsi gambar di sini

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.