Grid-Routing-Schlacht


22

ANMERKUNG: Diese Herausforderung ist derzeit nicht aktiv, da ich die für die Ausführung eines Matches erforderlichen Sprachen nicht installieren kann. Wenn jemand anderes die Zeit und das Interesse dazu hat, bin ich nicht dagegen.

Eine Rangliste finden Sie unten im Beitrag.

Dies ist eine semi-kooperative King-of-the-Hill-Herausforderung, bei der die Bots Pfade durch ein zweidimensionales Gitterdiagramm konstruieren. Der Bot, der die Knoten mit dem meisten Verkehr kontrolliert, ist der Gewinner. Es sind jedoch mehr als die Ressourcen eines Bots erforderlich, um tatsächlich einen Verbindungspfad zu erstellen, sodass die Bots in gewissem Maße zusammenarbeiten müssen.

Gameplay

Geben Sie im Folgenden N > 0die Anzahl der im Spiel befindlichen Bots an.

Das Gitter

Das Spiel wird auf einem zweidimensionalen, ganzzahligen Gitter gespielt , dessen linke untere Koordinate bei liegt . Jede Koordinate mit hat ausgehende Kanten für die drei Koordinaten , und darüber, wo die -Koordinaten modulo getroffen werden . Dies bedeutet, dass das Gitter an den Ost- und Westkanten umläuft. Jede untere Koordinate ist eine Quelle und jede obere Koordinate ist eine Senke .⌊4/3N2⌋ × ⌊4/3N2(0,0)(x,y)0 ≤ y < ⌊4/3N2⌋-1(x-1,y+1)(x,y+1)(x+1,y+1)x⌊4/3N2(x,0)(x,⌊4/3N2⌋-1)

Das folgende Bild zeigt ein 8 × 8Raster.

Ein 8x8 Raster.

Jeder Scheitelpunkt des Diagramms ist entweder inaktiv , aktiv oder unterbrochen . Alle Vertices beginnen inaktiv und können von Bots aktiviert werden, die dann ihre Eigentümer sind. Außerdem können Bots Scheitelpunkte brechen und können nicht repariert werden.

Turn Order

Eine Runde besteht aus einer Zerstörungsphase und einer Aktivierungsphase . In der Zerstörungsphase kann jeder Bot einen inaktiven Vertex brechen. Dieser Scheitelpunkt ist von da an unterbrochen und darf von niemandem aktiviert werden. In der Aktivierungsphase kann jeder Bot einen inaktiven Vertex aktivieren. Von da an besitzen sie diesen Scheitelpunkt und er kann von niemand anderem reaktiviert werden. Mehrere Bots können einen einzigen Vertex besitzen, wenn sie ihn alle im selben Zug aktivieren. In jeder Phase werden die Scheitelpunktauswahlen gleichzeitig durchgeführt.

Wertung

Eine Runde dauert genau Drehungen. Danach wird die Runde wie folgt gewertet. Von jedem aktiven Quellscheitelpunkt führen wir mal eine zufällige Tiefensuche entlang der aktiven Scheitelpunkte durch (was bedeutet, dass die Kinder jedes Scheitelpunkts in einer zufälligen Reihenfolge besucht werden). Wenn ein Pfad von der Quelle zu einer Senke gefunden wird, erhält jeder Eigentümer des Scheitelpunkts für alle Scheitelpunkte entlang dieses Pfads einen Punkt.N2N

Das gesamte Spiel dauert 100 Runden und der Bot mit den meisten Punkten ist der Gewinner. Ich kann diese Zahl erhöhen, wenn die Varianz der Ergebnisse zu hoch ist.

Zusätzliche Regeln

  • Kein Durcheinander mit dem Controller oder anderen Einsendungen.
  • Höchstens eine Einreichung pro Teilnehmer.
  • Keine externen Ressourcen außer einer privaten Textdatei wurden zu Beginn des Spiels gelöscht.
  • Gestalten Sie Ihren Bot nicht so, dass er bestimmte Gegner schlägt oder unterstützt.
  • Stellen Sie Befehle bereit, um Ihren Bot zu kompilieren und auszuführen. Jeder Compiler / Interpreter, der für Debian Linux frei verfügbar ist, ist akzeptabel.

Der Controller

Der Controller ist in Python 3 geschrieben und befindet sich in GitHub . Ausführliche Anweisungen finden Sie in der README-Datei. Hier ist eine API, mit der Sie loslegen können:

  • Bots werden zu Beginn jeder Runde gestartet und bleiben bis zum Ende der Runde bestehen. Die Kommunikation mit der Steuerung erfolgt über STDIN und STDOUT unter Verwendung von Nachrichten mit Zeilenumbruch.
  • BEGIN [num-of-bots] [num-of-turns] [side-length] wird am Anfang eingegeben.
  • DESTROY [turn]wird zu Beginn jeder Zerstörungsphase eingegeben. Ihr Bot antwortet entweder VERTEX x,ymit, um einen Scheitelpunkt auszuwählen, oder NONE.
  • BROKEN [turn] [your-choice] [other-choices]wird am Ende jeder Zerstörungsphase eingegeben. Die Reihenfolge der anderen Bots wird zu Beginn eines jeden Spiels zufällig festgelegt, bleibt jedoch dabei festgelegt. Die Auswahlmöglichkeiten werden als x,yoder dargestellt N.
  • ACTIVATE [turn]und OWNED [turn] [your-choice] [other-choices]sind die Äquivalente der oben genannten für die Aktivierungsphase und haben die gleiche Semantik.
  • SCORE [your-score] [other-scores] wird am Ende des Spiels eingegeben.
  • Ihr Bot hat 1 Sekunde Zeit , um die Ergebnisse einer Phase zu analysieren und den nächsten Scheitelpunkt auszuwählen, und 1 Sekunde , um nach der Bewertung zu beenden. Ich werde die Einsendungen auf meinem relativ alten Laptop testen, also ist es besser, hier etwas Spielraum zu lassen.

Bitte denken Sie daran, Ihren Ausgabepuffer zu leeren. Andernfalls kann der Controller in einigen Umgebungen hängen bleiben.

Bestenliste

Aktualisiert 13.03.2015

Peacemaker ist in Betrieb und auch Funnelweb hat ein Update erhalten. Die Punktzahlen stiegen um eine Größenordnung. Connector hat das Zeitlimit in zwei Spielen überschritten.

Funnelweb: 30911
Connector: 18431
Watermelon: 3488
Annoyance: 1552
Explorer: 735
Checkpoint: 720
Random Builder: 535
FaucetBot: 236
Peacemaker: 80

Das vollständige Protokoll mit ASCII-Grafiken finden Sie im Repository des Controllers unter graphical_log.txt.

Einige Beobachtungen:

  • Der Konnektor kann sehr einfach gestoppt werden, indem ein einzelner Scheitelpunkt davor gebrochen wird. Ich vermute, dass Ärger dies häufig tut. Derzeit ist dies jedoch wenig sinnvoll, da möglicherweise nur Connector einen Pfad erstellen kann.
  • Wassermelone kann eine anständige Punktzahl erzielen, indem sie sich zufällig auf einem Verbindungspfad befindet (da die DFS sehr wahrscheinlich ihre Scheitelpunkte verwendet).
  • Forscher mag Reben von den Wassermelonen anbauen.
  • Das aktualisierte Funnelweb erzielt wirklich gute Ergebnisse, da Connector normalerweise in der unteren Hälfte des Rasters einrastet.
  • Die Spiele werden ziemlich lang, die durchschnittliche Runde auf meinem Computer dauert ungefähr 25 Sekunden.

1
@Alex Ich habe versucht, die Herausforderung so zu gestalten, dass Selbstmord-Bots nicht alles durcheinander bringen. Drei gut gestaltete Bots sollten immer in der Lage sein, einen gültigen Pfad zu erstellen, wenn sie zusammenarbeiten.
Zgarb

2
@Zgarb Selbstmord sollte es nicht vermasseln, aber ein paar Trollbots, die zusammenarbeiten, könnten wahrscheinlich auch alle Pfade blockieren und das Spiel ruinieren.
Geobits

2
@CarpetPython Aktive Knoten können nicht zerstört werden.
Zgarb

1
Es ist unwahrscheinlich, dass wir mit den aktuellen Spielern und Regeln interessante Spiele sehen. Ich schlage vor, dass Sie die Regeln leicht ändern, um Gelegenheiten für interessante Spiele zu schaffen. Das Ändern der Rastergröße auf 1,5 * N ^ 2 anstelle von 2 * N ^ 2 sollte gut sein und vorhandene Roboter nicht zu sehr verwirren.
Logic Knight

1
@justhalf Das stimmt. Die Spiele im Protokoll wurden tatsächlich mit der weiter reduzierten Rastergröße von gespielt 4/3*N^2, und selbst dort hatten die Bots Probleme, gültige Pfade zu bilden. Connector wurde jedoch vorübergehend aufgrund eines Fehlers disqualifiziert, und jetzt, da es behoben wurde, erwarte ich, dass die Spiele interessanter werden. Ich werde heute Abend eine weitere Charge ausführen.
Zgarb,

Antworten:


7

Anschluss (Java)

Versucht, einen Pfad an einer zufälligen Position zu erstellen. Da es keinen eigenen Pfad erstellen kann, sucht es nach aktiven Zellen und verwendet sie. Gutschrift geht an Geobits, von denen ich Code gestohlen habe. Außerdem ist diese Übermittlung noch nicht abgeschlossen, da sie einfach aufhört, etwas zu tun, sobald der Pfad erstellt ist.

Bearbeiten: Wenn ein Pfad erstellt wird, versucht Connector, mehrere Pfade entlang des vorhandenen Pfads zu erstellen.

import java.awt.Point;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Connector {
    private static final int INACTIVE = 0;
    private static final int ACTIVE   = 1;
    private static final int BROKEN   = 2;
    private static final int MINE     = 3;

    private int size = 0;
    private int[][] grid = new int[size][size];
    private Point previousCell = null;
    private final List<Point> path = new ArrayList<>();

    public static void main(String[] args) {
        new Connector().start();
    }

    private void start() {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while(true) {
            try {
                String input = reader.readLine();
                act(input);
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(0);
            }
        }
    }

    private void act(String input) throws Exception {
        String[] msg = input.split(" ");
        String output = "";
        int turn;
        switch(msg[0]){
        case "BEGIN":
            size = Integer.parseInt(msg[3]);
            grid = new int[size][size];
            break;
        case "DESTROY":
            output = "NONE";
            break;
        case "BROKEN":
            update(msg, true);
            break;
        case "ACTIVATE":
            turn = Integer.parseInt(msg[1]);
            output = activate(turn);
            break;
        case "OWNED":
            update(msg, false);
            break;
        case "SCORE":
            System.exit(0);
            break;
        }
        if (output.length() > 0) {
            System.out.println(output);
        }
    }

    private String activate(int turn) {
        if (turn == 0) {
            Random r = new Random();
            previousCell = new Point(r.nextInt(size), 0);
            return "VERTEX " + previousCell.x + "," + 0;
        }
        Point lastCell = findLastPathCell(previousCell.x, previousCell.y);
        if (lastCell.y == size-1) {
            //path is done
            Point extendingPathPoint = findExtendingPathPoint();
            if (extendingPathPoint == null) {
                return "NONE";
            }
            return "VERTEX " + extendingPathPoint.x + "," + extendingPathPoint.y;
        } else {
            int x = findBestX(lastCell.x, lastCell.y);
            return "VERTEX " + x + "," + (lastCell.y + 1);
        }
    }

    private int findBestX(int x, int y) {
        int bestScore = Integer.MIN_VALUE;
        int bestX = 0;
        for (int i = -1; i <= 1; i++) {
            int newY = y + 1;
            int newX = (x + i + size) % size;
            int score = calcCellScore(newX, newY, 10);
            if (score > bestScore) {
                bestScore = score;
                bestX = newX;
            } else if (score == bestScore && Math.random() < 0.3) {
                bestX = newX;
            }
        }
        return bestX;
    }

    private int calcCellScore(int x, int y, int depth) {
        int newY = y + 1;
        if (depth < 0) {
            return 1;
        }
        if (newY >= size)
            return 100;
        int cellScore = 0;
        for (int i = -1; i <= 1; i++) {
            int newX = (x + i + size) % size;
            if (grid[newX][newY] == ACTIVE || grid[newX][newY] == MINE) {
                cellScore += 5;
            } else if (grid[newX][newY] == INACTIVE) {
                cellScore += 1;             
            } else {
                cellScore -= 2;
            }
            cellScore += calcCellScore(newX, newY, depth -1);
        }
        return cellScore;
    }

    private Point findLastPathCell(int x, int y) {
        Point thisCell = new Point(x,y);
        int newY = y + 1;
        if (newY >= size) {
            return thisCell;
        }
        List<Point> endCells = new ArrayList<>();
        endCells.add(thisCell);
        path.add(thisCell);
        for (int i = -1; i <= 1; i++) {
            int newX = (x + i + size) % size;
            if (grid[newX][newY] == ACTIVE || grid[newX][newY] == MINE) {
                endCells.add(findLastPathCell(newX, newY));
            }
        }
        int bestY = -1;
        Point bestPoint = null;
        for (Point p : endCells) {
            if (p.y > bestY) {
                bestY = p.y;
                bestPoint = p;
            }
        }
        return bestPoint;
    }

    private Point findExtendingPathPoint() {
        if (path.size() == 0)
            return null;
        Random rand = new Random();
        for (int i = 0; i < size; i++) {
            Point cell = path.get(rand.nextInt(path.size()));
            for (int j = -1; j <= 1; j += 2) {
                Point newCellX = new Point((cell.x + j + size) % size, cell.y);
                if (grid[newCellX.x][newCellX.y] == INACTIVE)
                    return newCellX;

                Point newCellY = new Point(cell.x, cell.y + j);
                if (cell.y < 0 || cell.y >= size)
                    continue;
                if (grid[newCellY.x][newCellY.y] == INACTIVE)
                    return newCellY;
            }
        }
        return null;
    }

    private void update(String[] args, boolean destroyPhase) {
        for(int i = 2; i < args.length; i++) {
            String[] tokens = args[i].split(",");
            if(tokens.length > 1){
                int x = Integer.parseInt(tokens[0]);
                int y = Integer.parseInt(tokens[1]);
                if (grid[x][y] == INACTIVE) {
                    if (destroyPhase) {
                        grid[x][y] = BROKEN;
                    } else if (i == 2) {
                        grid[x][y] = MINE;
                        path.add(new Point(x,y));
                        previousCell = new Point(x,y);
                    } else {
                        grid[x][y] = ACTIVE;
                    }
                }
            }
        }
    }
}

@Zgarb Entschuldigung, ich habe einen Fehler verursacht, als ich den anderen behoben habe. Es funktioniert jetzt
CommonGuy

@Manu, es ist gut, dass du wieder im Spiel bist. Es gibt zu viele Ausbeuter und zu wenig Erbauer. Wenn Connector ausgeführt wird, werden die Spiele möglicherweise interessanter (mehr als 1 von 100 Spielen mit einer Punktzahl).
Logic Knight

Connector brauchte 28 Sekunden, um in einem der neuesten Spiele zu antworten (siehe Protokoll). Scheint, als ob es in die Wassermelone geflossen ist und es schwierig war, zu entscheiden, wohin es als nächstes gehen soll.
Zgarb,

Ich lief wieder einige Spiele mit den verbesserten Friedensstiftern und Stecker warf einen Fehler: java.lang.ArrayIndexOutOfBoundsException: -1 at Connector.findExtendingPathPoint(Connector.java:166).
Zgarb

7

Funnelweb, Python 2

version 1.2 - Besserer Beitrittscode, neue Animation hinzugefügt

Benannt nach einer der weniger freundlichen Spinnen Australiens. Dieser Bot baut zuerst ein trichterförmiges Nest in den oberen Reihen und lockt dann andere Bots in Baupfade für den Verkehr zum Nest.

Hier ist eine neue Animation eines 6-Bot-Spiels auf einem 4 / 3N ^ 2-Brett, die das Trichterweb und einige einfachere Bots zeigt:

bots6.gif

Der Python-Code des Trichterwebs:

from random import *
import sys
ME = 0
def pt(x,y): return '%u,%u' % (x % side_len, y)

while True:
    msg = raw_input().split()

    if msg[0] == 'BEGIN':
        turn = 0
        numbots, turns, side_len = map(int, msg[1:])
        R = range(side_len)
        top = side_len - 1
        grid = dict((pt(x, y), []) for x in R for y in R)
        mynodes = set()
        deadnodes = set()
        freenodes = set(grid.keys())
        mycol = choice(R)
        extra = sample([pt(x,top) for x in R], side_len)
        path = [(mycol, y) for y in range(top, top - side_len/6, -1)]
        moves = []
        fence = []
        for x,y in path:
            moves.append( [pt(x,y), pt(x+1,y), pt(x-1,y)] )
            fence.extend( [pt(x+1,y), pt(x-1,y)] )
        for dx in range(2, side_len):
            fence.extend( [pt(x+dx,y), pt(x-dx,y)] )
        for x,y in [(mycol, y) for y in 
                range(top - side_len/6, top - 3*side_len/4, -1)]:
            moves.append( [pt(x,y), pt(x+1,y), pt(x-1,y)] )

    elif msg[0] == 'DESTROY':
        target = 'NONE'
        while fence:
            loc = fence.pop(0)
            if loc in freenodes:
                target = 'VERTEX ' + loc
                break
        print target
        sys.stdout.flush()

    elif msg[0] == 'BROKEN':
        for rid, loc in enumerate(msg[2:]):
            if loc != 'N':
                grid[loc] = None
                deadnodes.add(loc)
                freenodes.discard(loc)
                if loc in extra: extra.remove(loc)

    elif msg[0] == 'ACTIVATE':
        target = 'NONE'
        while moves:
            loclist = moves.pop(0)
            goodlocs = [loc for loc in loclist if loc in freenodes]
            if goodlocs:
                target = 'VERTEX ' + goodlocs[0]
                break
        if target == 'NONE':
            if extra:
                target = 'VERTEX ' + extra.pop(0)
            else:
                target = 'VERTEX ' + pt(choice(R), choice(R))
        print target
        sys.stdout.flush()

    elif msg[0] == 'OWNED':
        for rid, loc in enumerate(msg[2:]):
            if loc != 'N':
                grid[loc].append(rid)
                if rid == ME:
                    mynodes.add(loc)
                freenodes.discard(loc)
                if loc in extra: extra.remove(loc)
        turn += 1

    elif msg[0] == 'SCORE':
        break

Die Spinne läuft mit python funnelweb.py.


Algorithmus geändert und getestet. Es sollte jetzt laufen.
Logic Knight

Funktioniert jetzt super!
Zgarb,

6

Checkpoint, Java

Dieser Bot versucht, Checkpoints zu erstellen, damit ein gültiger Pfad durch einen meiner Vertices verläuft. Da es N 2 Windungen gibt und die Platine 2 N 2 breit ist , kann ich jeden Knoten auf einer einzelnen horizontalen Linie aktivieren / unterbrechen (vorausgesetzt, ich bin zuerst da). Mach das abwechselnd ( xist kaputt, oist meins):

xoxoxoxoxoxox...

Wenn du einen Pfad machen willst, musst du meine Checkpoints durchgehen :)

Nun gibt es einige Probleme, mit denen dies zu tun haben könnte. Erstens wird es überhaupt nicht gut gehen, wenn es nicht viele Wege gibt. Da er selbst keine produktiven Wege geht, ist er absolut darauf angewiesen, dass es Konkurrenten gibt. Selbst ein paar Konkurrenten, die sich zu einem einzigen Pfad zusammenschließen, helfen nicht viel, da er nur einen Punkt für jeden gefundenen Pfad erzielt. Was er braucht, um zu glänzen, sind wahrscheinlich einige Bots, die einige unterschiedliche Wege gehen. Selbst dann könnte es nicht sehr hoch punkten , aber es war eine Idee, die ich im Chat hatte, so ...

Wenn eines der Leerzeichen in der Zeile bereits blockiert / beansprucht ist, suche ich einfach nach einem nahe gelegenen Punkt, den ich verwenden kann (vorzugsweise in derselben xZeile, nur vertikal verschoben).


import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Checkpoint {
    public static void main(String[] args) {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while(true)
            try {
                String input = reader.readLine();
                act(input);
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(0);
            }
    }

    static void act(String input) throws Exception{
        String[] msg = input.split(" ");
        String output = "";
        int turn;
        boolean found = false;
        switch(msg[0]){
        case "BEGIN":
            size = Integer.parseInt(msg[3]);
            grid = new int[size][size];
            target = size/2;
            break;
        case "DESTROY":
            turn = Integer.parseInt(msg[1]);
            for(int x=0;x<size;x+=2)
                for(int y=0;y<size&&!found;y++)
                    if(grid[(x+turn*2)%size][(y+target)%size]==INACTIVE){
                        output = "VERTEX " + ((x+turn*2)%size) + "," + ((y+target)%size);
                        found = true;
                    }
            if(output.length() < 1)
                output = "NONE";
            break;
        case "BROKEN":
            for(int i=2;i<msg.length;i++){
                String[] tokens = msg[i].split(",");
                if(tokens.length>1){
                    int x = Integer.parseInt(tokens[0]);
                    int y = Integer.parseInt(tokens[1]);                    
                    if(grid[x][y]==INACTIVE)
                        grid[x][y] = BROKEN;
                }
            }
            break;
        case "ACTIVATE":
            turn = Integer.parseInt(msg[1]);
            for(int x=1;x<size;x+=2)
                for(int y=0;y<size&&!found;y++)
                    if(grid[(x+turn*2)%size][(y+target)%size]==INACTIVE){
                        output = "VERTEX " + ((x+turn*2)%size) + "," + ((y+target)%size);
                        found = true;
                    }
            if(output.length() < 1)
                output = "NONE";
            break;
        case "OWNED":
            for(int i=2;i<msg.length;i++){
                String[] tokens = msg[i].split(",");
                if(tokens.length>1){
                    int x = Integer.parseInt(tokens[0]);
                    int y = Integer.parseInt(tokens[1]);
                    if(i==2){
                        if(grid[x][y]==INACTIVE)
                            grid[x][y] = MINE;
                    }else{
                        if(grid[x][y]==INACTIVE)
                            grid[x][y]=ACTIVE;
                    }
                }
            }
            break;
        case "SCORE":
            System.exit(0);
            break;
        }
        if(output.length()>0)
            System.out.println(output);
    }

    static int size = 2;
    static int target = size/2;
    static int[][] grid = new int[size][size];

    static final int INACTIVE = 0;
    static final int ACTIVE   = 1;
    static final int BROKEN   = 2;
    static final int MINE     = 3;
}

Zum Kompilieren ist es javac Checkpoint.java. Zu rennen java Checkpoint,. Sie möchten den Pfad hinzufügen / ändern, um ihn überall wiederzugeben.


5

Wassermelone, Java

Versuche, Wassermelonen auf dem Gitter zu zeichnen.

import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class Watermelon {

    private static int numberOfBots;
    private static int numberOfTurns;
    private static int sideLength;

    private static int turn = 0;

    private static int[][] theGrid;

    private static final int INACTIVE = -2;
    private static final int BROKEN   = -1;
    private static final int MINE     =  0;
    private static final int ACTIVE   =  1;

    private static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    private static PrintStream out = System.out;

    public static void main(String[] args) throws IOException {
        while (true){
            String[] input = in.readLine().trim().split(" ");
            String instruction = input[0];
            switch (instruction){
                case "BEGIN":
                    begin(input);
                    break;
                case "DESTROY":
                    destroy(input);
                    break;
                case "BROKEN":
                    broken(input);
                    break;
                case "ACTIVATE":
                    activate(input);
                    break;
                case "OWNED":
                    owned(input);
                    break;
                default:
                    return;
            }
            out.flush();
        }
    }

    private static void begin(String[] input) {
        numberOfBots = Integer.parseInt(input[1]);
        numberOfTurns = Integer.parseInt(input[2]);
        sideLength = Integer.parseInt(input[3]);
        theGrid = new int[sideLength][sideLength];
        for (int x = 0; x < sideLength; x++){
            for (int y = 0; y < sideLength; y++){
                theGrid[x][y] = INACTIVE;
            }
        }
    }

    private static void owned(String[] input) {
        turn = Integer.parseInt(input[1]);
        for (int i = input.length - 1; i >= 2; i--){
            if (input[i].equals("N")){
                continue;
            }
            String[] coordinates = input[i].split(",");
            int x = Integer.parseInt(coordinates[0]);
            int y = Integer.parseInt(coordinates[1]);
            int player = i - 2;
            if (player == 0){
                theGrid[x][y] = MINE;
            } else {
                theGrid[x][y] = ACTIVE;
            }
        }
    }

    private static void activate(String[] input) {
        turn = Integer.parseInt(input[1]);
        double[][] values = new double[sideLength][sideLength];
        List<Point> pointList = new ArrayList<>();
        for (int x = 0; x < sideLength; x++){
            for (int y = 0; y < sideLength; y++){
                if (theGrid[x][y] == MINE || theGrid[x][y] == ACTIVE){
                    for (int x1 = 0; x1 < sideLength; x1++){
                        for (int y1 = 0; y1 < sideLength; y1++){
                            double distance = Math.pow(x - x1, 2) + Math.pow(y - y1, 2);
                            values[x1][y1] += 1 / (distance + 1);
                        }
                    }
                }
                pointList.add(new Point(x, y));
            }
        }
        pointList.sort(Comparator.comparingDouble((Point a) -> values[a.x][a.y]).reversed());
        for (Point point : pointList){
            if (theGrid[point.x][point.y] == INACTIVE){
                out.println("VERTEX " + point.x + "," + point.y);
                return;
            }
        }
        out.println("NONE");
    }

    private static void broken(String[] input) {
        turn = Integer.parseInt(input[1]);
        for (int i = 2; i < input.length; i++){
            if (input[i].equals("N")){
                continue;
            }
            String[] coordinates = input[i].split(",");
            int x = Integer.parseInt(coordinates[0]);
            int y = Integer.parseInt(coordinates[1]);
            theGrid[x][y] = BROKEN;
        }
    }

    private static void destroy(String[] input) {
        turn = Integer.parseInt(input[1]);
        double[][] values = new double[sideLength][sideLength];
        List<Point> pointList = new ArrayList<>();
        for (int x = 0; x < sideLength; x++){
            for (int y = 0; y < sideLength; y++){
                if (theGrid[x][y] == MINE){
                    for (int x1 = 0; x1 < sideLength; x1++){
                        for (int y1 = 0; y1 < sideLength; y1++){
                            double distance = Math.pow(x - x1, 2) + Math.pow(y - y1, 2);
                            values[x1][y1] -= 1 / (distance + 1);
                        }
                    }
                }
                if (theGrid[x][y] == ACTIVE){
                    for (int x1 = 0; x1 < sideLength; x1++){
                        for (int y1 = 0; y1 < sideLength; y1++){
                            double distance = Math.pow(x - x1, 2) + Math.pow(y - y1, 2);
                            values[x1][y1] += 1 / (distance + 1) / (numberOfBots - 1);
                        }
                    }
                }
                pointList.add(new Point(x, y));
            }
        }
        pointList.sort(Comparator.comparingDouble((Point a) -> values[a.x][a.y]).reversed());
        for (Point point : pointList){
            if (theGrid[point.x][point.y] == INACTIVE){
                out.println("VERTEX " + point.x + "," + point.y);
                return;
            }
        }
        out.println("NONE");
    }
}

5

WasserhahnBot (in R)

Erstellt einen Engpass in der zweiten Zeile und aktiviert Knoten auf dem Pfad dahinter.

infile <- file("stdin")
open(infile)
repeat{
    input <- readLines(infile,1)
    args <- strsplit(input," ")[[1]]
    if(args[1]=="BEGIN"){
        L <- as.integer(args[4])
        M <- N <- matrix(0,nrow=L,ncol=L)
        x0 <- sample(2:(L-1),1)
        }
    if(args[1]=="DESTROY"){
        if(args[2]==0){
            X <- x0
            Y <- 2
            }else{
                free <- which(M[,2] == 0)
                mine <- which(N[,2] == 1)
                X <- free[which.min(abs(free-mine))]
                Y <- 2
                }
        if(length(X)){cat(sprintf("VERTEX %s,%s\n",X-1,Y-1))}else{cat("NONE\n")}
        flush(stdout())
        }
    if(args[1]=="BROKEN"){
        b <- strsplit(args[args!="N"][-(1:2)],",")
        o <- strsplit(args[3],",")[[1]]
        b <- lapply(b,as.integer)
        if(o[1]!="N") N[as.integer(o[1])+1,as.integer(o[2])+1] <- -1
        for(i in seq_along(b)){M[b[[i]][1]+1,b[[i]][2]+1] <- -1}
        }
    if(args[1]=="ACTIVATE"){
        if(args[2]==0){
            broken <- which(M[,2] == -1)
            free <- which(M[,2] == 0)
            X <- free[which.min(abs(broken-free))]
            Y <- 2
            }else{
                y <- 3
                X <- NULL
                while(length(X)<1){
                    lastrow <- which(N[,y-1]==1)
                    newrow <- unlist(sapply(lastrow,function(x)which(M[,y]==0 & abs((1:L)-x)<2)))
                    if(length(newrow)){
                        X <- sample(newrow,1)
                        Y <- y
                        }
                    y <- y+1
                    if(y>L){X <- x0; Y <- 1}
                    }
                }
        cat(sprintf("VERTEX %s,%s\n",X-1,Y-1))
        flush(stdout())
        }
    if(args[1]=="OWNED"){
        b <- strsplit(args[args!="N"][-(1:2)],",")
        o <- strsplit(args[3],",")[[1]]
        b <- lapply(b,as.integer)
        if(o[1]!="N") N[as.integer(o[1])+1,as.integer(o[2])+1] <- 1
        for(i in seq_along(b)){M[b[[i]][1]+1,b[[i]][2]+1] <- 1}
        }
    if(args[1]=="SCORE") q(save="no")
    }

Wenn ich es nicht vermasselt habe, sollte die endgültige Konfiguration ungefähr so ​​aussehen:

........    .a..aa..
..aaa...    ..aaa...
.xxaxx..    xxxaxxx.    etc.
........    ........

Befehl ist Rscript FaucetBot.R.


5

Friedensstifter, Java

Basierend auf Manus Code.

Friedensstifter suchen Konfliktzonen (dh die meisten KAPUTTEN oder AKTIVEN Scheitelpunktkonzentrationen) und aktivieren einen zufälligen Scheitelpunkt in der Nähe.

import java.awt.Point;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.stream.IntStream;

public class Peacemaker {
    private static final int INACTIVE = 0;
    private static final int ACTIVE   = 1;
    private static final int BROKEN   = 2;
    private static final int MINE     = 3;

    private int size = 0;
    private int[][] grid = new int[size][size];
    private int startingPoint = 0;

    public static void main(String[] args) {
        new Peacemaker().start();
    }

    private void start() {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while(true) {
            try {
                String input = reader.readLine();
                act(input);
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(0);
            }
        }
    }

    private void act(String input) throws Exception {
        String[] msg = input.split(" ");
        String output = "";
        int turn;
        switch(msg[0]){
        case "BEGIN":
            size = Integer.parseInt(msg[3]);
            grid = new int[size][size];
            break;
        case "DESTROY":
            output = "NONE";
            break;
        case "BROKEN":
            update(msg, true);
            break;
        case "ACTIVATE":
            turn = Integer.parseInt(msg[1]);
            output = activate(turn);
            break;
        case "OWNED":
            update(msg, false);
            break;
        case "SCORE":
            System.exit(0);
            break;
        }
        if (output.length() > 0) {
            System.out.println(output);
        }
    }

    private String activate(int turn) {
        Random r = new Random();
        if (turn == 0) {
            startingPoint = r.nextInt(size);
            return "VERTEX " + startingPoint + "," + 0;
        } else {

            Point point = searchConflicts();

            int posX = point.x;
            int posY = point.y;

            while (grid[posX][posY] != INACTIVE) {
                 int previousX = (posX - 1 < 0 ? size - 1 : posX - 1);
                 int nextX = (posX + 1 > size - 1 ? 0 : posX + 1);
                 int previousY = (posY - 1 < 0 ? size - 1 : posY - 1);
                 int nextY = (posY + 1 > size - 1 ? 0 : posY + 1);

                 int choice = r.nextInt(4);
                 switch (choice) {
                     case 0: posX = previousX; break;
                     case 1: posX = nextX; break;
                     case 2: posY = previousY; break;
                     case 3: posY = nextY; break;
                 }
            }

            return "VERTEX " + posX + "," + posY;
        }
    }

    private Point searchConflicts() {

        int previousCellScore = 0;
        int cellX = 0;
        int cellY = 0;
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j ++) {
                if (previousCellScore < adjacentCellsScore(i, j)) {
                    cellX = i; cellY = j;
                    previousCellScore = adjacentCellsScore(i, j);
                }
            }
        }
        return new Point(cellX, cellY);
    }

    /*  Format of adjacent cells :
     * 
     *   0 1 2
     *   3 . 4
     *   5 6 7
     */
    private int adjacentCellsScore(int x, int y) {

        int[] scores = new int[8];

        int previousX = (x - 1 < 0 ? size - 1 : x - 1);
        int nextX = (x + 1 > size - 1 ? 0 : x + 1);
        int previousY = (y - 1 < 0 ? size - 1 : y - 1);
        int nextY = (y + 1 > size - 1 ? 0 : y + 1);

        scores[0] = calcScore(previousX, nextY);
        scores[1] = calcScore(x, nextY);
        scores[2] = calcScore(nextX, nextY);
        scores[3] = calcScore(previousX, y);
        scores[4] = calcScore(nextX, y);
        scores[5] = calcScore(previousX, previousY);
        scores[6] = calcScore(x, previousY);
        scores[7] = calcScore(nextX, previousY);

        return IntStream.of(scores).reduce(0, (a, b) -> a + b);
    }

    private int calcScore(int x, int y) {
        int activeScore = 2;
        int mineScore = 1;
        int inactiveScore = 0;
        int brokenScore = 3;

        if (grid[x][y] == ACTIVE) 
            return activeScore;
        else if (grid[x][y] == MINE)
            return mineScore;
        else if (grid[x][y] == INACTIVE) 
            return inactiveScore;
        else if (grid[x][y] == BROKEN) 
            return brokenScore;
        else
            return 0;
    }


    private void update(String[] args, boolean destroyPhase) {
        for(int i = 2; i < args.length; i++) {
            String[] tokens = args[i].split(",");
            if(tokens.length > 1){
                int x = Integer.parseInt(tokens[0]);
                int y = Integer.parseInt(tokens[1]);
                if (grid[x][y] == INACTIVE) {
                    if (destroyPhase) {
                        grid[x][y] = BROKEN;
                    } else if (i == 2) {
                        grid[x][y] = MINE;
                    } else {
                        grid[x][y] = ACTIVE;
                    }
                }
            }
        }
    }       
}

@Zgarb Danke, ich hätte das Problem jetzt lösen sollen.
Thrax

Der Friedensstifter arbeitet jetzt und ist in der Rangliste enthalten. Es scheint jedoch nicht viel zu bewirken, so dass wahrscheinlich noch einige Bugs übrig sind.
Zgarb,

Wenn ich mir Ihren Code anschaue, denke ich, dass das Problem in der whileSchleife der activateMethode liegt. Sie brechen die Suche ab, sobald Sie einen Scheitelpunkt gefunden haben, der Ihnen nicht gehört und der nicht beschädigt ist. Sie können ihn also nicht aktivieren.
Zgarb,

@Zgarb Ich habe die Spezifikationen falsch gelesen und dachte, mehrere Spieler könnten jederzeit den gleichen Vertex aktivieren. Ich denke, ich muss nur meine Suche ändern und nach inaktiven Eckpunkten suchen.
Thrax

2

Zufälliger Builder, Python 3

Dies ist ein dummer Beispielbot, der niemals etwas zerstört und versucht, in jeder Runde einen zufälligen Scheitelpunkt zu aktivieren. Beachten Sie, dass nicht überprüft werden muss, ob der Scheitelpunkt inaktiv ist. das regelt der controller.

import random as r

while True:
    msg = input().split()
    if msg[0] == "BEGIN":
        side_len = int(msg[3])
    elif msg[0] == "DESTROY":
        print("NONE")
    elif msg[0] == "ACTIVATE":
        print("VERTEX %d,%d"%(r.randrange(side_len), r.randrange(side_len)), flush=True)
    elif msg[0] == "SCORE":
        break

Führen Sie den Befehl aus

python3 random_builder.py

Sie können ersetzen müssen python3durch pythonje nach Ihrer Python - Installation. Bearbeiten Sie dazu einfach die bots.txtDatei. Ich habe den Controller aktualisiert und muss nicht mehr mit Dateipfaden herumspielen.


Da Sie Python 3 verwenden, sys.stdout.flush()können Sie dies nur flush=Trueals Argument verwenden print.
Matsjoyce

@matsjoyce Danke, das wusste ich nicht. Ich werde die Repository-Version später bearbeiten.
Zgarb,

2

Explorer, Python 3

Aktivierungsstrategie:

Erstellt eine Heatmap basierend auf dem Status jedes Knotens (aktiv / inaktiv / defekt) und wählt den Knoten aus, der den größten erwarteten Heatmap-Wert pro Person hätte, wenn er diesen auswählen würde.

Zerstörungsstrategie:

Zerstöre niemals etwas, da dies dem Bot nicht viel hilft.

import sys

class bd:

    def __init__(s, l):

        s.l=l
        s.b=[]
        s.v=[]
        s.m=[]
        s.bm=[]
        s.utd=False #up_to_date
        s.bmc=1

        for i in range(s.l):
            s.b+=[[]]
            s.v+=[[]]
            s.m+=[[]]
            s.bm+=[[]]
            for k in range(s.l):
                s.b[i]+=[0]
                s.v[i]+=[0]
                s.m[i]+=[0]
                s.bm[i]+=[s.bmc]

    def update(s):
        s.utd=True

        vu=[]
        vd=[]
        for i in range(s.l):
            vu+=[[]]
            vd+=[[]]
            for k in range(s.l):
                vu[i]+=[1]
                vd[i]+=[1]

        #spread up
        for i in range(s.l):
            vu[i][0]*=s.bm[i][0]

        for k in range(1,s.l):
            for i in range(s.l):
                sumv=vu[(i-1)%s.l][k-1]+vu[(i)%s.l][k-1]+vu[(i+1)%s.l][k-1]  
                vu[i][k]*=sumv*s.bm[i][k]/3

        #spread down
        t=s.l-1
        for i in range(s.l):
            vd[i][t]*=s.bm[i][t]

        for k in range(s.l-2,-1,-1):
            for i in range(s.l):
                sumv=vd[(i-1)%s.l][k+1]+vd[(i)%s.l][k+1]+vd[(i+1)%s.l][k+1]  
                vd[i][k]*=sumv*s.bm[i][k]/3

        #mult
        for i in range(s.l):
            for k in range(s.l):
                if s.b[i][k]==-1 or s.m[i][k]==1:
                    s.v[i][k]=float(-1)
                else:
                    s.v[i][k]=vu[i][k]*vd[i][k]/(s.b[i][k]+1)

    def add_act(s,al):
        s.utd=False

        for ind, ap in enumerate(al):
            i,k=ap
            s.b[i][k]+=1            
            s.bm[i][k]=2*s.bmc            
            #doesn't work alone WHY???
            if ind==0: s.m[i][k]=1

    def add_ina(s,il):
        s.utd=False

        for ind, ip in enumerate(il):
            i,k=ip
            s.b[i][k]=-1
            s.bm[i][k]=0                    

    def get_newact(s):
        s.update()
        vm=-28
        pm=None
        for i in range(s.l):
            for k in range(s.l):
                if s.v[i][k]>vm:
                    vm=s.v[i][k]
                    pm=(i,k)
        #doesn't work alone WHY???
        s.m[pm[0]][pm[1]]=1
        return pm


b=None

while True:
    inp=input()
    msg = inp.split()
    if msg[0] == "BEGIN":        
        b = bd(int(msg[3]))
    elif msg[0] == "DESTROY":
        print("NONE")
    elif msg[0] == "BROKEN":
        pl=[]
        for m in msg[2:]:
            if m!='N':
                pl+=[tuple(map(int,m.split(',')))]
        b.add_ina(pl)
    elif msg[0] == "ACTIVATE":
        at=b.get_newact()
        print("VERTEX %d,%d"%(at[0], at[1]))
    elif msg[0] == "OWNED":
        pl=[]
        for m in msg[2:]:
            if m!='N':
                pl+=[tuple(map(int,m.split(',')))]        
        b.add_act(pl)
    elif msg[0] == "SCORE":
        break       

    sys.stdout.flush()

1

Ärger, Bash

#!/bin/bash

declare -A avail
broken=
owned=

while read c p
    case "$c" in
        ACTIVATE|BROKEN) v=broken;;
        *) v=owned
    esac
    case "$c" in
        BEGIN)
            read b t n <<<"$p"
            list=$(
                eval "echo {0..$((n-1))},{0..$((n-1))}\$'\\n'" |
                shuf
            )
            for i in $list; do
                avail[$i]=1
            done;;
        DESTROY|ACTIVATE)
            for t in $(
                for i in ${!v}; do
                    [ "$i" != N ] &&
                    if [ "$c" = ACTIVATE ]; then
                        echo $(((${i%,*}+2)%n)),${i#*,}
                        echo $(((${i%,*}-2+n)%n)),${i#*,}
                    else
                        echo ${i%,*},$(((${i#*,}+1)%n))
                        echo ${i%,*},$(((${i#*,}-1+n)%n))
                    fi
                done |
                shuf
            ) $list; do
                [ "${avail[$t]}" ] && echo VERTEX $t && break
            done ||
            echo NONE;;
        BROKEN|OWNED)
            read x m $v <<<"$p";
            for i in $m ${!v}; do
                unset avail[$i]
            done;;
        SCORE)! :
    esac
do :;done

Versucht, das Ergebnis interessanter aussehen zu lassen.

Laufen Sie mit bash annoyance.sh.


1
Ihr Bot druckt alle seine Eingaben in STDERR. Es ist nicht verboten oder so, nur ein Ärger (Wortspiel beabsichtigt).
Zgarb,

@ Zgarb Sorry, ich habe die falsche Version eingefügt. Fest.
Jimmy23013

1

Middle Man

Ich habe gesehen, dass einige Bots von oben und einige von unten gebaut wurden. Dies ist der erste (glaube ich), der in der Mitte beginnt und auf und ab arbeitet.

(Es wird nicht mit dem Controller getestet. Wenn es also nicht funktioniert, lassen Sie es mich wissen.)

class Node

  def self.set_size s
    @@grid = Array.new(s,Array.new(s,0))
  end

  def initialize x,y
    @x=x
    @y=y
  end

  def offset dx,dy
    return Node.new @x+dx,@y+dy
  end

  def state
    return -1 if @x<0 || @y<0 || @x>=@@grid.length || @y>=@@grid.length
    @@grid[@x][@y]
  end

  def state= n
    return -1 if @x<0 || @y<0 || @x>=@@grid.length || @y>=@@grid.length
     @@grid[@x][@y]=n
  end

  def active?
    state > 0
  end

  def open?
    state == 0
  end
  attr_reader :x,:y

  def to_s
    "VERTEX #{@x},#{@y}"
  end


  def scan_down
    ans = nil
    [0,-1,1].each do|offset|
      n = Node.new @x+offset,@y-1
      ans = (ans||n) if n.open?
      ans = (n.scan_down||ans) if n.active?
    end
    return ans
  end

  def scan_up
    ans = nil
    [0,-1,1].each do|offset|
      n = Node.new @x+offset,@y+1
      ans = (ans||n) if n.open?
      ans = (n.scan_up||ans) if n.active?
    end
    return ans
  end

end

input = gets.split
input.shift

BotCount = input.shift.to_i
Turns = input.shift.to_i
GridSize = input.shift.to_i

Node.set_size GridSize

midRow = GridSize/2

toDestroy = (0...GridSize).map{|i|Node.new i,midRow}
toDestroy.reject!{|n| n.x==midRow}

chain = []
Turns.times do
  gets;
  toDestroy.each{|x|
    if x.active?
      toDestroy.push x.offset 0,1
      toDestroy.push x.offset 1,1
      toDestroy.push x.offset -1,1
    end
  }
  toDestroy.reject!{|x|!x.open?}
  puts toDestroy.sample
  input = gets.split
  input.shift;input.shift
  input.each{|str|
    a,b = str.split ','
    (Node.new a.to_i,b.to_i).state=1
  }
  gets;

  if chain.length == 0
    n = Node.new midRow,midRow
    until n.open?
      n = Node.new n.x+1,midRow
    end
    puts chain[0]=n
  elsif rand>0.5
    n=nil
    loop do
      h=chain[0]
      n = h.scan_down
     break if !n
      chain.shift
    end
    h.unshift n
    puts n
  else
    loop do
      h=chain[-1]
      n = h.scan_up
      h.pop if !n
      brake if n
    end
    chain.push n
    puts n
  end

  input = gets.split
  input.shift;input.shift
  input.each{|str|
    a,b = str.split ','
    (Node.new a,b).state=-1
  }

end
gets
exit

Vielen Dank für die Einreichung! Leider ruht diese Herausforderung seit fast einem halben Jahr und ich kann derzeit die meisten Bots nicht ausführen, da ich keinen Zugang zu einem Computer habe, auf dem ich die Sprachen installieren könnte.
Zgarb

1
@ Zgarb Ich verstehe. Vielleicht werde ich eines Tages eine Herausforderung in einem angemessenen Zeitrahmen beantworten ...
MegaTom
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.