KOTH: Hit and Sunk

12

Einführung

Für meine 5. KOTH stelle ich Ihnen eine Herausforderung vor, die auf dem bekannten Spiel Battleship mit ein paar Wendungen basiert . Du kommandierst nur ein Schiff, dessen Typ du zwischen den 5 "traditionellen" Klassen auswählen kannst, aber du kannst in jedem Zug mehrere Aktionen ausführen, einschließlich des Bewegens! Dies wird als FFA (Free For All) gespielt und Ihr Ziel ist es, das letzte Schiff zu sein, das noch steht.

Prinzip

Das Spiel ist rundenbasiert. Zu Beginn des Spiels musst du die Klasse deines Schiffes auswählen. Danach können die Spieler in jeder Runde je nach Schiff mehrere Aktionen ausführen.

Das Spiel findet auf einem 2D-Gitter (X, Y) statt, dessen Seite folgendermaßen definiert ist:
X = 30 + numberOfPlayer
Y = 30 + numberOfPlayer
Die Startposition jedes Schiffs ist zufällig.

Die Spielreihenfolge ist in jeder Runde zufällig und Sie kennen weder Ihre Position in der "Warteschlange" noch die Anzahl der Spieler. Das Spiel dauert 100 Runden oder bis nur noch ein Schiff am Leben ist.

Jedes Mal, wenn Sie ein feindliches Schiff treffen oder getroffen werden, erhalten oder verlieren Sie Punkte. Der Spieler mit der höchsten Punktzahl gewinnt. Der Gewinner erhält ein Kopfgeld (Wert abhängig von der Anzahl der Teilnehmer).

Die Steuerung stellt Ihnen Eingaben über Befehlsargumente zur Verfügung, und Ihr Programm muss Ausgaben über stdout vornehmen.

Syntax

Erste Runde

Ihr Programm wird einmalig ohne Argument aufgerufen . Sie müssen eine ganze Zahl zwischen 1 und 5 (einschließlich) eingeben, um Ihr Schiff auszuwählen:

1: Zerstörer [Länge: 2, Züge / Runde: 3, Schüsse / Runde: 1, Reichweite: 9, Minen: 4]
Fähigkeit : Freie Schiffsrotation (keine Abklingzeit)

2: U-Boot [Länge: 3, Bewegungen / Runde: 2, Schüsse / Runde: 1, Reichweite: 5, Minen: 4]
Fähigkeit : Kann eintauchen / auftauchen (siehe Ausgänge). Unter Wasser können Sie nur "Bewegungsaktionen" ausführen und nur mit einem Scan sehen. Sie können nicht von einem Schuss getroffen werden, können aber durch Minen Schaden erleiden.

3: Kreuzer [Länge: 3, Züge / Runde: 1, Schüsse / Runde: 2, Reichweite: 9, Minen: 2]
Fähigkeit : Kann reparieren (siehe Ausgänge)

4: Schlachtschiff [Länge: 4, Züge / Runde: 1, Schüsse / Runde: 3, Reichweite: 7, Minen: 1]
Fähigkeit : Kann Schild (siehe Ausgänge)

5: Träger [Länge: 5, Bewegungen / Runde: 1, Schüsse / Runde: 1, Reichweite: 7, Minen: 3]
Fertigkeit : Schüsse verursachen AOE-Schaden (Flächenschaden) beim Ziel (1 Reichweite-Spritzschaden). Wenn das Ziel mit dem Schuss getroffen wird, werden auch bis zu 2 Zellen dieses Schiffes beschädigt.

Wendet sich

Eingang

Jedes Mal, wenn Ihr Programm aufgerufen wird, erhält es Argumente in folgendem Format:

Round;YourPlayerId;X,Y,Direction;Hull;Moves,Shots,Mines,Cooldown;Hits,Sunken,Damage;Underwater,Shield,Scan;Map

Runden sind 1-indiziert.

Beispiel Eingabe

1;8;1,12,0;111;1,2,2,0;0,0,0;0,0,0;UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUXXXX.......UUUUUUUUXXXX.......UUUUUUUUXXXX.......UUUUUUUUXXXX.......UUUUUUUUXXXX.......UUUUUUUUXXXX.O.....UUUUUUUUXXXX.O.....UUUUUUUUXXXX.O.....UUUUUUUUXXXX.......UUUUUUUUXXXX.......UUUUUUUUXXXX.......UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU

Hier ist die erste Runde, Sie sind Spieler 8.
Ihr Schiff befindet sich auf (X = 1, Y = 12) und Ihre Richtung ist nach oben gerichtet (0 = oben, 1 = rechts, 2 = unten, 3 = links) ).
Ihr Rumpf ist nicht beschädigt (Ihr Schiff hat eine Länge von 3 und jedes Bit ist wahr [1 = OK, 0 = Beschädigt]). Du kannst dich 1 Mal bewegen, 2 Mal schießen, hast noch 2 Minen übrig und deine "Fertigkeit" ist verfügbar (Abklingzeit = 0).
Sie haben nichts getroffen, kein Schiff versenkt und wurden auch nicht getroffen.
Du bist nicht unter Wasser, deine Schilde (falls vorhanden) sind nicht aktiviert und dein Scan ist es auch nicht.
Mehr auf der Karte später ...

Ausgabe

Sie müssen einen String ausgeben, der beschreibt, welche Aktionen Sie in dieser Runde ausführen werden. Die Reihenfolge der Zeichen in Ihrer Ausgabezeichenfolge bestimmt die Reihenfolge der Aktionen. Sie können dieselben Aktionen mehrmals ausgeben, wenn sie die Grenzen Ihres Schiffes nicht überschreiten. Wenn eine oder mehrere Aktionen ungültig sind, wird jede für sich als ungültig betrachtet W. Hier ist die Liste der verfügbaren Aktionen:

M: Bewege 1 Zelle in die Richtung, in die du blickst (verbrauche 1 Bewegung)
B: Zurück 1 Zelle aus der Richtung, in die du blickst (verbrauche 1 Bewegung)
C: Drehe dein Schiff im Uhrzeigersinn (verbrauche 1 Bewegung / frei für Zerstörer)
K: Drehe dein Schiff Gegen den Uhrzeigersinn (1 Zug verbrauchen / für Zerstörer frei)
A: Rammen Sie Ihr Schiff in die Richtung, in die Sie schauen (funktioniert nur, wenn ein anderes Schiff die Zelle in die Richtung, in die Sie schauen, besetzt / Ihr Schiff nicht bewegt / alle Züge verbrauchen).
F: 1 Schuss auf eine Zelle in Reichweite abfeuern (1 Schuss verbrauchen). : Y / Beispiel muß von der anvisierten Zelle in diesem Format (- - X [+] [+]) verfolgt werden F+2-3)
N: Platz 1 Mine in einer Zelle neben Ihrem Schiff (verbraucht alle Schüsse und 1 Mine). Muss von der anvisierten Zelle in diesem Format ([+ -] X [+ -]) verfolgt wird Y / Beispiel: N+0+1)
S: Aktiviere deinen Scan für die nächste Runde (alle Schüsse aufnehmen)
R: Repariere den beschädigten Rumpf, der dem "Kopf" deines Schiffes am nächsten liegt (alle Schüsse aufnehmen, Abklingzeit = 3 Runden / nur Kreuzer)
P: Eintauchen / Auftauchen (alle Schüsse aufnehmen, Abklingzeit = 3 Runden, maximale Dauer = 5 Runden / nur U-Boot)
D: Aktiviere deinen Schild und verhindere den nächsten Schaden in deiner nächsten Runde (verbrauche alle Schüsse, Abklingzeit = 3 / nur Schlachtschiff)
W: Warte (macht nichts)

Erläuterung : "Alle Züge / Schüsse verbrauchen" bedeutet, dass Sie diese Aktion nur ausführen können, wenn Sie in diesem Zug zuvor noch keine Ihrer Züge / Schüsse verwendet haben.

Beispielausgabe

MF+9-8CM : Verschiebt 1 Zelle und feuert dann auf die Zelle, deren relative Position zum "Kopf" Ihres Schiffes (targetX = X + 9, targetY = Y - 8) ist, dreht sich im Uhrzeigersinn und verschiebt schließlich 1 Zelle erneut.

Spielweise

Das Gitter

Hier ist ein Beispielgitter (33 x 13), in dem 3 Spieler platziert sind:

███████████████████████████████████
█                                 █
█       00                        █
█   2                             █
█   2                             █
█   2                             █
█                                 █
█       11111                     █
█        M                        █
█                                 █
█                                 █
█                                 █
█                                 █
█                                 █
███████████████████████████████████

Wie wir sehen können, gibt es auch eine Mine Mdirekt neben dem Spieler 1.

Nehmen wir Spieler 2, um Position und Richtung zu verstehen:

Die Position von Spieler 2 ist X = 3, Y = 4, Direction = 3. Da die Richtung "Bottom" ist, befinden sich die restlichen "Schiffszellen" "über" seinem "Kopf" (X = 3, Y = 3) & (X = 3, Y = 2)

Spielerkarte

Das letzte Argument, das jeder Spieler erhält, ist seine "eigene" Karte. Standardmäßig erkennt ein Schiff alles in einer Reichweite von 5 Zellen , kann jedoch einen Scan aktivieren , um diese Reichweite auf 9 zu erhöhen .

Das Argument ist immer 361 (19 x 19) Zeichen lang. Es stellt das Quadrat dar, das um den "Kopf" Ihres Schiffes zentriert ist, wobei jedes Zeichen einem auf diese Weise definierten Element entspricht:

.: Leere Zelle
O: Dein Schiff
M: Minen
X: Mauer (Zellen außerhalb der Karte)
U: Unbekannt (werden durch einen Scan aufgedeckt)
A: Unbeschädigte Zelle des
Bfeindlichen Schiffs: Beschädigte Zelle des
Cfeindlichen Schiffs unter Wasser (nur bei einem Scan sichtbar)
D: Unterwasserzelle eines feindlichen Schiffs (nur mit einem Scan zu sehen)
W: Wrack (totes Schiff)

Die Zeichenfolge besteht aus 19 Zeichen der ersten Zeile, gefolgt von 19 Zeichen der zweiten Zeile ... bis zur 19. Zeile.

Werfen wir einen Blick darauf, was Spieler 2 mit und ohne Scan empfängt (Zeilenumbrüche zum besseren Verständnis, aber nicht an Spieler senden):

XXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXX
XXXXXX.............
XXXXXX.......AA....
XXXXXX...O.........
XXXXXX...O.........
XXXXXX...O.........
XXXXXX.............
XXXXXX.......AAAAA.
XXXXXX........M....
XXXXXX.............
XXXXXX.............
XXXXXX.............
XXXXXX.............
XXXXXX.............
XXXXXXXXXXXXXXXXXXX

UUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUU
UUUUXXXXXXXXXXXUUUU
UUUUXX.........UUUU
UUUUXX.......AAUUUU
UUUUXX...O.....UUUU
UUUUXX...O.....UUUU
UUUUXX...O.....UUUU
UUUUXX.........UUUU
UUUUXX.......AAUUUU
UUUUXX........MUUUU
UUUUXX.........UUUU
UUUUXX.........UUUU
UUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUU

Minen

Die Minen werden ausgelöst, wenn ein Schiff in eine Zelle fährt, in der sich eine Mine befindet, oder wenn ein Schuss auf die Mine abgegeben wird. Minen können nicht mit der Aktion "Ram" ausgelöst werden.

Minen verursachen bei jedem AOE-Schaden (1 Reichweite-Spritzschaden), auch bei der Person, die die Mine platziert hat. Minen können Kettenexplosionen auslösen, wenn sich eine andere Mine im Umkreis der Explosion befindet.

Drehungen

Die Rotationen sind zentrale Symmetrien, die auf dem "Kopf" des Schiffes zentriert sind. Rotationen lösen nur dann eine Mine aus, wenn sie auf der "Zielposition" platziert ist (Sie lösen keine Minen in einem Bogen aus.

Wirkungsbereich

Der 1-Entfernungs-Spritzschaden (für Minen und Trägerschüsse) wird durch ein 3x3 (9 Zellen) großes Quadrat definiert, das bei dem ersten Schuss / der ersten Explosion (x, y) zentriert ist. Es trifft diese Koordinaten:[x - 1; y - 1],[x - 1; y],[x - 1; y + 1],[x; y - 1],[x; y],[x; y + 1],[x + 1; y - 1],[x + 1; y],[x + 1; y + 1]

Wertung

Die Wertung wird durch diese Formel definiert:
Score = Hits + (Sunken x 5) - Damage taken - (Alive ? 0 : 10)

Wo:
hits: Anzahl der Zugriffe auf feindliches Schiff, entweder durch Ram, Schuss oder Minenexplosion (1 Treffer durch feindliches Schiff Zelle beschädigt, einschließlich Kette Explosionen)
sunken: Anzahl der „letzten Schlag“ auf ein feindliches Schiff , die es verursacht sinken
damage: Anzahl der Erhaltene Treffer (nicht durch Reparatur verringert, sondern durch Schild verhindert)
alive: Überprüft, ob Ihr Schiff am Ende noch am Leben ist (mindestens 1 Rumpfzelle unbeschädigt)

Regler

Sie finden den Controller auf GitHub . Es enthält auch zwei in Java geschriebene Beispielbots. Überprüfen Sie das Projekt, und öffnen Sie es in Ihrer Java-IDE, damit es ausgeführt wird. Der Einstiegspunkt in die Hauptmethode des Klassenspiels. Java 8 erforderlich.

Zum Hinzufügen von Bots benötigen Sie zunächst entweder die kompilierte Version für Java (.class-Dateien) oder die Quellen für interpretierte Sprachen. Platzieren Sie sie im Stammverzeichnis des Projekts. Erstellen Sie dann eine neue Java-Klasse im Players-Paket (Sie können ein Beispiel für die bereits vorhandenen Bots verwenden). Diese Klasse muss Player implementieren, um die Methode String getCmd () zu überschreiben. Der zurückgegebene String ist der Shell-Befehl zum Ausführen Ihrer Bots. Mit diesem Befehl können Sie beispielsweise einen Ruby-Bot zum Laufen bringen: Geben Sie "C: \ Ruby \ bin \ ruby.exe MyBot.rb" zurück. Fügen Sie abschließend den Bot in das Spieler-Array oben in der Spielklasse ein.

Regeln

  • Bots sollten nicht geschrieben werden, um bestimmte andere Bots zu schlagen oder zu unterstützen.
  • Das Schreiben in Dateien ist erlaubt. Bitte schreibe an "yoursubmissionname.txt", der Ordner wird vor Spielbeginn geleert. Andere externe Ressourcen sind nicht zulässig.
  • Ihr Beitrag hat 1 Sekunde Zeit, um zu antworten.
  • Stellen Sie Befehle zum Kompilieren und Ausführen Ihrer Übermittlungen bereit.
  • Sie können mehrere Beiträge verfassen

Unterstützte Sprachen

Ich werde versuchen, jede Sprache zu unterstützen, aber sie muss kostenlos online verfügbar sein. Bitte geben Sie Anweisungen für die Installation, wenn Sie keine "Mainstream" -Sprache verwenden.

Ab sofort kann ich Java 6-7-8, PHP, Ruby, Perl, Python 2-3, Lua, R, node.js, Haskell, Kotlin, C ++ 11 ausführen.

Thrax
quelle
Interessant KotH, ich habe nur ein paar Fragen: Können wir mehrere Einreichungen schreiben (zum Beispiel eine für jeden Schiffstyp)? Wenn Sie über AoE sprechen, ist es ein Quadrat um die rechte Position (es trifft [x + 1; y + 1])?
Katenkyo
@Katenkyo Ja, Sie können mehrere Beiträge verfassen. Ja, es trifft 9 Zellen:[x - 1; y - 1],[x - 1; y],[x - 1; y + 1],[x; y - 1],[x; y],[x; y + 1],[x + 1; y - 1],[x + 1; y],[x + 1; y + 1]
Thrax
taucht das U-Boot also automatisch auf? In welcher Kurve?
Destructible Lemon
werden auch gleichzeitig gewechselt?
Destructible Lemon
Was ist auch nützlich an der Widderfähigkeit? (Warum nicht einfach schießen?)
Destructible Lemon

Antworten:

3

RandomBot

Dies ist ein Beispiel-Bot. Es wählt ein Schiff, eine Aktion und eine Zielzelle (falls erforderlich) nach dem Zufallsprinzip aus.

import java.util.Random;

public class RandomBot {

    int round;
    int playerID;

    public static void main(String[] args) {

        if (args.length == 0) {
            Random random = new Random();
            int ship = random.nextInt(5);
            String[] ships = { "1", "2", "3", "4", "5" };
            System.out.println(ships[ship]);
        } else {
            new RandomBot().play(args[0].split(";"));
        }
    }

    private void play(String[] args) {

        round = Integer.parseInt(args[0]);
        playerID = Integer.parseInt(args[1]);

        String[] actions = { "M", "B", "C", "K", "F", "S", "N", "A" };
        Random random = new Random();
        int action = random.nextInt(8);

        int rangeX = random.nextInt(5);
        int rangeY = random.nextInt(5);
        int mineX = random.nextInt(1);
        int mineY = random.nextInt(1);

        String signX = random.nextInt(1) == 1 ? "+" : "-";
        String signY = random.nextInt(1) == 1 ? "+" : "-";

        System.out.println(actions[action] + (action == 4 ? signX + rangeX + signY + rangeY : "") + (action == 6 ? signX + mineX + signY + mineY : ""));
    }

}

PassiveBot

Dies ist ein Beispiel-Bot. Es macht nichts.

public class PassiveBot {

    int round;
    int playerID;

    public static void main(String[] args) {

        if (args.length == 0) {
            System.out.println("5");
        } else {
            new PassiveBot().play(args[0].split(";"));
        }
    }

    private void play(String[] args) {

        round = Integer.parseInt(args[0]);
        playerID = Integer.parseInt(args[1]);

        System.out.println("W");
    }

}
Thrax
quelle
3

PeaceMaker, Python 2 (Schlachtschiff)

PeaceMaker schießt dreimal auf die nächsten Feinde (Spiralabstand) und bewegt sich in einer Linie hin und her, wobei der Abstand zu Minen mindestens 2 Zellen beträgt.

from os import sys

def reversedSpiralOrder(length):

    #Initialize our four indexes
    top = 0
    down = length - 1
    left = 0
    right = length - 1
    result = ""

    while 1:

        # Print top row
        for j in range(left, right + 1):
            result += str(top * length + j) + ";"
        top += 1
        if top > down or left > right:
            break

        # Print the rightmost column
        for i in range(top, down + 1):
            result += str(i * length + right) + ";"
        right -= 1
        if top > down or left > right:
            break

        # Print the bottom row
        for j in range(right, left + 1, -1):
            result += str(down * length + j) + ";"
        down -= 1
        if top > down or left > right:
            break

        # Print the leftmost column
        for i in range(down, top + 1, -1):
            result += str(i * length + left) + ";"
        left += 1
        if top > down or left > right:
            break

    result = result.split(";")
    del result[-1]
    return result[::-1]

def canMove(x, y, direction, hull, map):

    # M = 1, B = 2
    moves = 0

    if direction == 0:
        y1 = -1
        y2 = -2
        hx1 = hx2 = x1 = x2 = 0
        hy1 = -y1 + hull
        hy2 = -y2 + hull
    elif direction == 1:
        x1 = 1
        x2 = 2
        hy1 = hy2 = y1 = y2 = 0
        hx1 = -x1 - hull
        hx2 = -x2 - hull
    elif direction == 2:
        y1 = 1
        y2 = 2
        hx1 = hx2 = x1 = x2 = 0
        hy1 = -y1 - hull
        hy2 = -y2 - hull
    elif direction == 3:
        x1 = -1
        x2 = -2
        hy1 = hy2 = y1 = y2 = 0
        hx1 = -x1 + hull
        hx2 = -x2 + hull

    if map[y + y1][x + x1] == "." and map[y + y2][x + x2] != "M":
        moves += 1

    if map[y + hy1][x + hx1] == "." and map[y + hy2][x + hx2] != "M":
        moves += 2

    return moves

if len(sys.argv) <= 1:
    f = open("PeaceMaker.txt","w")
    f.write("")
    print "4"
else:
    arguments = sys.argv[1].split(";")
    sight = 19

    round = int(arguments[0])
    playerID = int(arguments[1])
    x = int(arguments[2].split(",")[0])
    y = int(arguments[2].split(",")[1])
    direction = int(arguments[2].split(",")[2])
    hull = arguments[3]
    moves = int(arguments[4].split(",")[0])
    shots = int(arguments[4].split(",")[1])
    mines = int(arguments[4].split(",")[2])
    cooldown = int(arguments[4].split(",")[3])
    hits = int(arguments[5].split(",")[0])
    kills = int(arguments[5].split(",")[0])
    taken = int(arguments[5].split(",")[0])
    underwater = int(arguments[6].split(",")[0])
    shield = int(arguments[6].split(",")[1])
    scan = int(arguments[6].split(",")[2])
    map = [[list(arguments[7])[j * sight + i] for i in xrange(sight)] for j in xrange(sight)]

    initialShots = shots


    priorities = reversedSpiralOrder(sight)

    actions = ""
    sighted = 0
    for priority in priorities:
        pX = int(priority) % sight
        pY = int(priority) / sight

        if map[pY][pX] == "A":
            sighted += 1
            if shots > 0:
                shots -= 1
                actions += "F" + ("+" if pX - 9 >= 0 else "") + str(pX - 9)  + ("+" if pY - 9 >= 0 else "") + str(pY - 9)

    if shots == initialShots and sighted > 0:
        actions += "D"
    elif shots == initialShots and sighted <= 0:
        actions += "S"
    else:
        actions += ""

    f = open("PeaceMaker.txt","r")
    fC = f.read(1)
    lastDirection = int("1" if fC == "" else fC)

    y = 9
    x = 9

    if lastDirection == 1:
        if canMove(x, y, direction, len(hull), map) == 1 or canMove(x, y, direction, len(hull), map) == 3:
            actions += "M"
        elif canMove(x, y, direction, len(hull), map) == 2:
            actions += "B"
            lastDirection = 0
    elif lastDirection == 0:
        if canMove(x, y, direction, len(hull), map) == 2 or canMove(x, y, direction, len(hull), map) == 3:
            actions += "B"
        elif canMove(x, y, direction, len(hull), map) == 1:
            actions += "M"
            lastDirection = 1

    f = open("PeaceMaker.txt","w")
    f.write(str(lastDirection))

    print actions
Juin
quelle
1
'PeaceMaker schießt'. Du hast mich dort verloren.
Ok