KOTH: Hit and Sunk

12

Wprowadzenie

Podczas piątej KOTH przedstawiam wam wyzwanie oparte na znanej grze Battleship z kilkoma zwrotami akcji. Będziesz dowodził tylko jednym statkiem, którego typ będziesz mógł wybierać spośród 5 „tradycyjnych” klas, ale będziesz mógł podejmować wiele akcji w każdej turze, w tym w ruchu! Jest to rozgrywane jako FFA (Free For All), a Twoim celem będzie bycie ostatnim stojącym statkiem.

Zasada

Gra jest turowa. Na początku gry musisz wybrać klasę swojego statku. Następnie w każdej turze gracze będą mogli wykonać kilka akcji w zależności od statku.

Gra odbywa się na siatce 2D (X, Y), której bok jest określony w ten sposób:
X = 30 + numberOfPlayer
Y = 30 + numberOfPlayer
Pozycja początkowa każdego statku jest losowa.

Kolejność gry jest losowa w każdej turze i nie będziesz znać swojej pozycji w „kolejce” ani liczby graczy. Gra trwa 100 tur lub dopóki nie pozostanie przy życiu tylko jeden statek.

Za każdym razem, gdy trafisz wrogi statek lub zostaniesz trafiony, zdobędziesz lub stracisz punkty. Gracz z najwyższym wynikiem wygrywa. Zwycięzca otrzyma nagrodę (wartość zależy od liczby uczestników).

Kontroler udostępnia dane wejściowe za pomocą argumentów poleceń, a program musi wyświetlać dane wyjściowe za pomocą standardowego wyjścia.

Składnia

Pierwsza tura

Twój program zostanie wywołany raz bez żadnych argumentów. Musisz wybrać liczbę całkowitą od 1 do 5 (włącznie), aby wybrać swój statek:

1: Niszczyciel [długość: 2, ruchy / obrót: 3, strzały / obrót: 1, zasięg: 9, miny: 4]
Umiejętność : Bezpłatne obroty statku (bez czasu odnowienia)

2: Okręt podwodny [długość: 3, ruchy / obrót: 2, strzały / obrót: 1, zasięg: 5, miny: 4]
Umiejętność : Potrafi pogrążyć / nawrócić (patrz wyniki). Pod wodą możesz korzystać tylko z akcji „Ruch” i można to zobaczyć tylko podczas skanowania. Nie możesz zostać trafiony strzałem, ale możesz przyjąć obrażenia od min.

3: Krążownik [długość: 3, ruchy / obrót: 1, strzały / obrót: 2, zasięg: 9, miny: 2]
Umiejętność : Może naprawić (patrz wyniki)

4: Pancernik [długość: 4, ruchy / tura: 1, strzały / tura: 3, zasięg: 7, miny: 1]
Umiejętność : Może osłonić (patrz wyniki)

5: Przewoźnik [długość: 5, ruchy / obrót: 1, strzały / obrót: 1, zasięg: 7, miny: 3]
Umiejętność : Strzały zadają celowi obrażenia obszarowe (obszar zasięgu). Jeśli cel zostanie trafiony strzałem, uszkodzone zostaną również do 2 komórek tego statku.

Okazuje

Wejście

Za każdym razem, gdy wywoływany jest twój program, będzie on otrzymywał argumenty w następującym formacie:

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

Rundy są indeksowane 1.

Przykładowe dane wejściowe

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

Tutaj jest pierwsza runda, jesteś graczem 8.
Twój statek jest ustawiony na (X = 1, Y = 12), a twój kierunek jest w kierunku góry (0 = Góra, 1 = Prawo, 2 = Dół, 3 = Lewo ).
Twój kadłub nie jest uszkodzony (twój statek ma długość 3, a każdy bit jest prawdziwy [1 = OK, 0 = Uszkodzony]). Możesz poruszyć się 1 raz, strzelić 2 razy, pozostały 2 miny, a twoja „umiejętność” jest dostępna (czas odnowienia = 0).
Nic nie trafiłeś, ani nie zatopiłeś żadnego statku, a ty też nie zostałeś trafiony.
Nie jesteś pod wodą, twoje tarcze (jeśli istnieją) nie są aktywowane i twój skan też nie jest.
Więcej na mapie później ...

Wynik

Musisz podać ciąg znaków opisujący działania, które wykonasz w tej turze. Kolejność znaków w wyjściowym ciągu znaków określa kolejność działań. Możesz wykonać te same działania wiele razy, jeśli nie przekraczają one limitów twojego statku. Jeśli jedno lub wiele działań jest nieważnych, każde z nich będzie osobno traktowane jako W. Oto lista dostępnych działań:

M: Przesuń 1 komórkę w kierunku, w którym patrzysz (skonsumuj 1 ruch)
B: Wstecz 1 komórkę z kierunku, w którym patrzysz (skonsumuj 1 ruch)
C: Obróć statek zgodnie z ruchem wskazówek zegara (zużyj 1 ruch / bezpłatnie dla Niszczycieli)
K: Obróć statek przeciwnie do ruchu wskazówek zegara (zużyj 1 ruch / bezpłatnie dla Niszczycieli)
A: Taranuj swój statek w kierunku, w którym patrzysz (działa tylko wtedy, gdy inny statek zajmuje komórkę w kierunku, w którym jesteś / nie porusza twojego statku / zużywa wszystkie ruchy)
F: Wystrzel 1 strzał do komórki w zasięgu (zużyj 1 strzał). Musi być następnie kierowane przez komórki, w tym formacie ([+ - +] x [-]) T / przykład: F+2-3)
N: Miejsce 1 kopalni komórce przyległej do statku (zużywają wszystkie zdjęcia i 1 kopalni). Po nim musi znajdować się komórka docelowa w tym formacie ([+ -] X [+ -]) Y / example N+0+1:)
S: Aktywuj skanowanie na następną turę (zużyj wszystkie strzały)
R: Napraw uszkodzony kadłub najbliżej „głowy” twojego statku (zużyj wszystkie strzały, czas odnowienia = 3 tury / tylko krążownik)
P: Zanurz / na powierzchni (zużyj wszystkie strzały, czas odnowienia = 3 tury, maksymalny czas trwania = 5 tur / tylko okręt podwodny)
D: Aktywuj swoją tarczę, zapobiegając następnym obrażeniom podczas następnej tury (zużyj wszystkie strzały, czas odnowienia = 3 / tylko pancernik)
W: Poczekaj (nic nie robi)

Wyjaśnienie : „Zużyj wszystkie ruchy / strzały” oznacza, że ​​możesz użyć tej akcji tylko wtedy, gdy nie użyłeś żadnego z twoich ruchów / strzałów podczas tej tury.

Przykładowe dane wyjściowe

MF+9-8CM : Przesuwa 1 komórkę, a następnie strzela w komórkę, której względna pozycja w stosunku do „głowy” twojego statku to (targetX = X + 9, targetY = Y - 8), obraca się zgodnie z ruchem wskazówek zegara i ostatecznie przesuwa 1 komórkę.

Rozgrywka

Siatka

Oto przykładowa siatka (33 x 13), w której umieszczonych jest 3 graczy:

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

Jak widzimy, Mobok gracza 1 jest także Kopalnia .

Weźmy gracza 2, aby zrozumieć pozycję i kierunek:

Pozycja Gracza 2 to X = 3, Y = 4, Kierunek = 3. Ponieważ jego kierunek to „Dno”, reszta jego „komórek statku” jest ustawiona „nad” jego głową (X = 3, Y = 3) & (X = 3, Y = 2)

Mapa gracza

Ostatnim argumentem każdego gracza jest „własna” mapa. Domyślnie statek wykrywa wszystko w zakresie 5 komórek , ale może aktywować skanowanie, aby zwiększyć ten zakres do 9 .

Argument ma zawsze 361 (19 x 19) znaków. Przedstawia kwadrat wyśrodkowany wokół „głowy” twojego statku, gdzie każda postać odpowiada tak zdefiniowanemu elementowi:

.: Pusta komórka
O: Twój statek
M: Miny
X: Ściana (komórki poza mapą)
U: Nieznany (zostanie ujawniony przez skan)
A: Nieuszkodzona komórka
Bwroga
C: Komórka uszkodzona przez wroga: Komórka podwodna nieuszkodzona komórka (widoczne tylko ze skanem)
D: Podwodna uszkodzona komórka wroga (widoczna tylko ze skanem)
W: Wrak (martwy statek)

Ciąg składa się z 19 znaków pierwszego wiersza, a następnie 19 znaków z drugiego wiersza ... aż do 19 wiersza.

Przyjrzyjmy się, co gracz 2 otrzymuje ze skanem i bez (podział linii dla lepszego zrozumienia, ale nie wysyłany do graczy):

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

Kopalnie

Kopalnie są uruchamiane, gdy statek przemieszcza się do komórki zajmowanej przez minę lub gdy wystrzeliwany jest strzał do kopalni. Kopalni nie można uruchomić za pomocą akcji „Ram”.

Kopalnie zadają obrażenia obszarowe (obrażenia obszarowe o zasięgu 1) wszystkim, nawet osobie, która umieściła minę. Kopalnie mogą wywoływać wybuchy „łańcuchowe”, jeśli inna kopalnia znajduje się w promieniu wybuchu.

Rotacje

Obroty są centralnymi symetriami wyśrodkowanymi na „głowie” statku. Obracanie uruchomi minę tylko wtedy, gdy zostanie ona umieszczona w „pozycji docelowej” (nie uruchomisz min w łuku.

Obszar działania

Obrażenia obszarowe 1 zasięgu (dla min i strzałów Przewoźnika) są zdefiniowane przez kwadrat 3x3 (9 komórek) wyśrodkowany na pierwszym strzale / eksplozji (x, y). Uderza w te współrzędne:[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]

Punktacja

Punktację określa ta formuła:
Score = Hits + (Sunken x 5) - Damage taken - (Alive ? 0 : 10)

gdzie::
hitsliczba trafień na wrogim statku, albo przez wybuch Ram, Strzał lub Mine (1 trafienie przez uszkodzoną komórkę statku wroga, w tym wybuch łańcuchowy)
sunken: liczba „ostatnich trafień” na wrogim statku, które spowodowały jego zatonięcie
damage: liczba otrzymane trafienia (nie zmniejszone przez naprawę, ale zapobiegane przez tarczę)
alive : sprawdza, czy twój statek żyje na końcu (co najmniej 1 komórka kadłuba nieuszkodzona)

Kontroler

Kontroler można znaleźć na GitHub . Zawiera także dwa sampleboty napisane w Javie. Aby go uruchomić, sprawdź projekt i otwórz go w swoim Java IDE. Punkt wejścia w głównej metodzie klasy Gra. Wymagana Java 8.

Aby dodać boty, najpierw potrzebujesz skompilowanej wersji dla Javy (pliki .class) lub źródeł tłumaczonych języków. Umieść je w folderze głównym projektu. Następnie utwórz nową klasę Java w pakiecie graczy (możesz wziąć przykład z już istniejących botów). Ta klasa musi implementować Player, aby zastąpić metodę String getCmd (). Zwrócony ciąg to polecenie powłoki do uruchamiania botów. Możesz na przykład uruchomić bota Ruby za pomocą tego polecenia: zwróć „C: \ Ruby \ bin \ ruby.exe MyBot.rb” ;. Na koniec dodaj bota do tablicy graczy na górze klasy gry.

Zasady

  • Boty nie powinny być pisane w celu pokonania lub wspierania określonych innych botów.
  • Zapis do plików jest dozwolony. Napisz do „twojasubmissionname.txt”, folder zostanie opróżniony przed rozpoczęciem gry. Inne zasoby zewnętrzne są niedozwolone.
  • Twoje zgłoszenie ma 1 sekundę na odpowiedź.
  • Podaj polecenia, aby skompilować i uruchomić swoje zgłoszenia.
  • Możesz napisać wiele zgłoszeń

Obsługiwane języki

Spróbuję wesprzeć każdy język, ale musi on być dostępny online za darmo. Podaj instrukcje instalacji, jeśli nie używasz języka „głównego nurtu”.

W tej chwili mogę uruchomić: Java 6-7-8, PHP, Ruby, Perl, Python 2-3, Lua, R, node.js, Haskell, Kotlin, C ++ 11.

Thrax
źródło
Ciekawe KotH, mam tylko kilka pytań: czy możemy napisać wiele zgłoszeń (na przykład jedno dla każdego rodzaju statku)? Kiedy mówisz o obszarze działania, jest to kwadrat wokół pozycji po prawej stronie (uderza w [x + 1; y + 1])?
Katenkyo
@Katenkyo Tak, możesz pisać wiele zgłoszeń. Tak, uderza w 9 komórek:[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
więc czy okręt podwodny automatycznie się wynurza? w której turze?
Zniszczalna cytryna
również tury są podejmowane jednocześnie?
Zniszczalna cytryna
także co jest przydatne w zdolności taranowania? (dlaczego nie po prostu strzelać?)
Destructible Lemon

Odpowiedzi:

3

RandomBot

To jest przykładowy bot. Wybiera losowo statek, akcję i komórkę docelową (w razie potrzeby).

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

To jest przykładowy bot. Nic nie robi.

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
źródło
3

PeaceMaker, Python 2 (pancernik)

PeaceMaker strzela 3 razy w najbliższych wrogów (odległość spiralna) i porusza się w przód iw tył w linii, pozostając co najmniej 2 komórki z dala od min.

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
źródło
1
„PeaceMaker strzela”. Zgubiłeś mnie tam.
Okx,