Hilf unseren Robotern, den Teleporter zu erreichen

17

UPDATE: Zum Einstieg wurde ein Python-Framework hinzugefügt.

Die Raumstation wurde von Crusher-Bots überholt. Sie müssen so viele unserer teuren und zerbrechlichen Tech-Bots, die "Kaninchen" genannt werden, zu einem Ausgangsteleporter leiten, bevor sich die Station selbst zerstört, aber die Brecher-Bots patrouillieren durch die Korridore.

Ihr Programm erhält eine ASCII-Karte und in jeder Runde wird Ihnen mitgeteilt, wo sich die Brecher-Bots und Ihre aktuellen Kaninchen befinden. Ihr Programm sollte dann Ihre Kaninchen in Richtung des Ausgangsteleporters bewegen, während Sie den Brecherbots ausweichen.

Demo-Animation

Ausführung

Führen Sie den Python 2-Controller aus mit:

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

Der Startwert ist eine kleine Ganzzahl, die für den Brecher und Ihr Programm PRNG verwendet wird, damit die Abläufe wiederholt werden können. Ihr Programm sollte unabhängig vom tatsächlich verwendeten Startwert eine konsistente Leistung erbringen. Wenn der Startwert Null ist, verwendet der Controller für jeden Lauf einen zufälligen Startwert.

Die Steuerung führt Ihr Programm mit dem Namen der Kartentextdatei und dem Startwert als Argument aus. Z.B:

perl wandomwabbits.pl large.map 322

Wenn Ihr Programm ein PRNG verwendet, sollten Sie es mit dem angegebenen Startwert initialisieren. Die Steuerung sendet dann Ihre Programmaktualisierungen über STDIN und liest Ihre Kaninchenbewegungen über STDOUT.

In jeder Umdrehung gibt der Controller 3 Zeilen aus:

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

Dann wartet das Programm auf die Ausgabe einer Zeile:

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

UPDATE: Ihr Programm muss innerhalb von 2 Sekunden initialisiert werden, bevor die ersten Zeilen von der Steuerung gesendet werden.

Wenn Ihr Programm länger als 0,5 Sekunden benötigt, um nach der Eingabe der Kaninchenposition für die Steuerung mit Bewegungen zu reagieren, wird die Steuerung beendet.

Wenn sich keine Hasen im Raster befinden, hat die Hasenzeile keine Werte, und Ihr Programm sollte eine leere "Move" -Stringzeile ausgeben.

Denken Sie daran, Ihren Programmausgabestream bei jeder Umdrehung zu leeren, da der Controller sonst hängen bleibt.

Beispiel

Prog-Eingabe:

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

Prog-Ausgabe:

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

Steuerungslogik

Die Logik für jede Runde:

  • Wenn links null ist, Punktzahl drucken und beenden.
  • Fügen Sie für jede leere Startzelle ein Kaninchen hinzu, wenn kein Brecher in Sicht ist.
  • Entscheiden Sie für jeden Brecher die Bewegungsrichtung (siehe unten).
  • Bewegen Sie sich für jeden Brecher, wenn möglich.
  • Wenn sich der Brecher an einem Kaninchenstandort befindet, entfernen Sie das Kaninchen.
  • Ausgang links, Brecheraktionen und Kaninchenstandorte zum Programmieren.
  • Kaninchenbewegungsanforderungen aus dem Programm lesen.
  • Wenn ein Kaninchen nicht existiert oder keine Bewegung möglich ist, überspringen Sie.
  • Plotten Sie jeden neuen Standort von Kaninchen.
  • Wenn ein Kaninchen einen Brecher trifft, wird es zerstört.
  • Wenn sich das Kaninchen im Ausgangsteleporter befindet, wird es entfernt und die Punktzahl erhöht.
  • Wenn Kaninchen kollidieren, werden beide zerstört.

Jeder Brecher hat immer eine Kursrichtung (eine von NSEW). Bei jeder Runde folgt ein Brecher dieser Navigationslogik:

  • Wenn ein oder mehrere Kaninchen in einer der vier orthogonalen Richtungen sichtbar sind, wechseln Sie die Richtung zu einem der nächsten Kaninchen. Beachten Sie, dass Brecher nicht an einem anderen Brecher vorbei sehen können.
  • Andernfalls können Sie nach Möglichkeit nach dem Zufallsprinzip zwischen "Vorwärts", "Links" und "Rechts" wählen.
  • Andernfalls, wenn Hindernisse (Mauer oder andere Brecher) vorne links und rechts nach hinten weisen.

Dann für jeden Brecher:

  • Wenn sich in der Richtung des neuen Brechers kein Hindernis befindet, bewegen Sie sich (und quetschen Sie möglicherweise).

Die Kartensymbole

Die Karte ist ein rechteckiges Raster aus ASCII-Zeichen. Die Karte besteht aus Wänden #, Korridorräumen , Startpositionen für Kaninchen s, Ausgangsteleportern eund Startpositionen für Brecherc . Die obere linke Ecke ist Position (0,0).

Kleine Karte

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

Karte testen

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

Beispiel großer Kartenlauf

große Demo

Ergebnis

Um Ihr Programm zu bewerten, führen Sie die Steuerung mit der Testkarte, 500 Umdrehungen, 5 Durchläufen und dem Startwert 0 aus. Ihre Punktzahl ist die Gesamtzahl der Kaninchen, die erfolgreich von der Station in Sicherheit teleportiert wurden. Bei Stimmengleichheit gewinnt die Antwort mit den meisten Stimmen.

Ihre Antwort sollte einen Titel mit dem Namen des Eintrags, der verwendeten Sprache und der Punktzahl enthalten. Geben Sie im Antworttext die Controller-Punktzahl mit den Startnummern an, damit andere Ihre Läufe wiederholen können. Beispielsweise:

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

Sie können Standardbibliotheken und frei verfügbare Bibliotheken verwenden, die Standardlücken sind jedoch verboten. Sie dürfen Ihr Programm nicht für einen bestimmten Startwert, eine bestimmte Anzahl von Runden, einen Kartenfeaturesatz oder andere Parameter optimieren. Ich behalte mir das Recht vor, die Karte zu ändern, zu zählen und zu setzen, wenn ich einen Verstoß gegen diese Regel vermute.

Controller-Code

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

Der Controller erstellt ein Textprotokoll der eingehenden Abläufe run.logund eine Reihe von Bildern im animVerzeichnis. Wenn Ihre Python-Installation die PIL-Bildbibliothek nicht finden kann (als Kissen herunterladen), werden keine Bilder generiert. Ich habe die Bilderserie mit ImageMagick animiert. Z.B:

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

Sie können gerne interessante Animationen oder Bilder mit Ihrer Antwort posten.

Sie können diese Funktionen ausschalten und den Controller beschleunigen, indem Sie GRIDLOG = Falseund / oder MKIMAGE = Falsein den ersten Zeilen des Controller-Programms.

Vorgeschlagenes Python-Framework

Hier finden Sie ein Framework in Python, mit dem Sie loslegen können. Der erste Schritt besteht darin, die Kartendatei einzulesen und Pfade zu den Ausgängen zu finden. In jeder Runde sollte es einen Code geben, um zu speichern, wo sich die Brecher befinden, und einen Code, der entscheidet, wohin unsere Kaninchen bewegt werden sollen. Die einfachste Strategie ist zunächst, die Kaninchen zu einem Ausgang zu bewegen und dabei die Brecher zu ignorieren - einige Kaninchen könnten durchkommen.

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()
Logik-Ritter
quelle
Darf der Controller mit genau demselben RNG simuliert werden? Dies würde die Brecher effektiv deterministisch machen und es Ihnen ermöglichen, ihr Verhalten vorherzusagen und sie zu vermeiden. Verdammt, Sie könnten wahrscheinlich ein oder mehrere "Köderkaninchen" herstellen, um die Brecher zu beschäftigen, während Sie eine ungestörte Straße von Kaninchen errichten
oder
Nein, Sie können das RNG möglicherweise nicht simulieren (oder für ein bestimmtes Saatgut aufzeichnen). Die Brecher sind so konzipiert, dass sie NICHT deterministisch sind. Daher ist dies eine Code-Herausforderung, um eine Strategie zu entwickeln, mit der sie wahrscheinlich vermieden werden. Die Idee des Köderkaninchens ist aber durchaus in Ordnung. Ich erwarte einige Strategien mit Opferkaninchen.
Logic Knight
Wenn der Startwert 0 ist, wird dann nicht bei jedem Lauf ein zufälliger Startwert verwendet?
TheNumberOne
Ja. Wenn der Controller-Startwert Null ist, wird für jeden Lauf ein zufälliger Startwert verwendet (und an das Testprogramm ausgegeben). Dieser Samen wird mit der Punktzahl des Laufs angegeben. Das Zurückführen dieses Samens in die Steuerung sollte dazu führen, dass der genaue Lauf (und die Punktzahl) überprüft werden. Es ist ein bisschen komplex, aber es war der beste Weg, um Replikationsläufe (deterministisch) und Zufälligkeiten im Verhalten von Controllern und Testprogrammen zu ermöglichen.
Logic Knight

Antworten:

2

Verrückt, Python 45

Ich habe 25 Läufe mit zufälligem Startwert durchgeführt, mein Computer ist nicht schnell genug, um 1000 zu erreichen (wenn jemand die Punktzahl korrigieren möchte). Das erste Programm in Python war für mich ein mühsames Debuggen. Ich weiß auch nicht, ob ich es gut benutzt habe.

Es wird ein Algorithmus verwendet, der die Breite des ersten Abschnitts vom Ausgang berücksichtigt, während der andere die Brecher berücksichtigt. Ich hatte viel Auszeit, deshalb habe ich mich nicht für etwas Komplexeres entschieden (Entfernen eines Brechers usw.). Die Kaninchen werden auch verrückt, wenn ein Brecher in der Nähe ist, in der Hoffnung, dass er einen falschen Weg einschlägt.

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

Animation für einen durchschnittlichen Lauf

Schlagen
quelle
Ich denke, Sie haben möglicherweise Fehler in Ihrer Einrückung. Führender Leerraum ist in Python wichtig. zB: die walkables.add((b,a))Linien.
Logic Knight
sollte jetzt gut funktionieren
Hit
1

Bunny, Java, 26,385

Ich habe die Punktzahlen der Läufe 1 bis 1000 gemittelt und mit 5 multipliziert, um meine Punktzahl zu erhalten. Ich bin mir ziemlich sicher, dass dies der durchschnittlichen Punktzahl aller möglichen Spiele mit den Standardoptionen entspricht.

Scores

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

}

Der beste getestete Lauf hat Startwert 1000. Hier ist ein GIF davon:

Bildbeschreibung hier eingeben

Die Nummer eins
quelle