KOTH: Acertou e Afundou

12

Introdução

Para o meu quinto KOTH, apresento a você um desafio baseado no conhecido jogo Battleship, com algumas reviravoltas. Você comandará apenas uma nave, cujo tipo poderá selecionar entre as 5 classes "tradicionais", mas poderá realizar várias ações a cada turno, incluindo o movimento! Isso é jogado como um FFA (Free For All) e seu objetivo será ser o último navio em pé.

Princípio

O jogo é baseado em turnos. No início do jogo, você terá que escolher a classe do seu navio. Então, a cada turno, os jogadores poderão executar várias ações, dependendo do navio.

O jogo ocorre em uma grade 2D (X, Y), cujo lado é definido desta maneira:
X = 30 + numberOfPlayer
Y = 30 + numberOfPlayer
A posição inicial de cada navio é aleatória.

A ordem do jogo é aleatória a cada turno, e você não saberá sua posição na "fila" nem o número de jogadores. O jogo dura 100 turnos ou até restar apenas uma nave viva.

Cada vez que você atinge um navio inimigo ou é atingido, você ganha ou perde pontos. O jogador com mais pontos ganha. Uma recompensa será concedida ao vencedor (valor dependendo do número de participantes).

O controlador fornece entrada por meio de argumentos de comando, e seu programa precisa gerar saída por stdout.

Sintaxe

Primeira volta

Seu programa será chamado uma vez sem nenhum argumento. Você precisará calcular um número inteiro entre 1 e 5 (inclusive) para selecionar sua remessa:

1: Destruidor [comprimento: 2, movimentos / turno: 3, tiros / turno: 1, alcance: 9, minas: 4]
Habilidade : Rotações gratuitas de navios (sem recarga)

2: Submarino [comprimento: 3, movimentos / turno: 2, tiros / turno: 1, alcance: 5, minas: 4]
Habilidade : Pode mergulhar / superfície (ver resultados). Enquanto estiver embaixo d'água, você pode usar apenas as ações "Movimento" e pode ser visto apenas com uma varredura. Você não pode ser atingido por um tiro, mas pode sofrer danos de minas.

3: Cruzador [comprimento: 3, movimentos / turno: 1, tiros / turno: 2, alcance: 9, minas: 2]
Habilidade : Consertar (ver saídas)

4: Encouraçado [comprimento: 4, movimentos / turno: 1, tiros / turno: 3, alcance: 7, minas: 1]
Habilidade : Can Shield (ver saídas)

5: Transportadora [comprimento: 5, movimentos / turno: 1, tiros / turno: 1, alcance: 7, minas: 3]
Habilidade : Tiros causam dano AOE (Área de Efeito) ao alvo (1 dano por respingo de alcance). Se o alvo for atingido com o tiro, até 2 células deste navio também serão danificadas.

Turns

Entrada

Cada vez que seu programa é chamado, ele recebe argumentos neste formato:

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

As rodadas são indexadas em 1.

Exemplo de entrada

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

Aqui, é a 1ª rodada, você é o jogador 8.
Seu navio está posicionado em (X = 1, Y = 12) e sua direção está em direção ao topo (0 = Superior, 1 = Direita, 2 = Inferior, 3 = Esquerda )
Seu casco não está danificado (seu navio tem um comprimento de 3 e cada bit é verdadeiro [1 = OK, 0 = Danificado]). Você pode se mover 1 vez, atirar 2 vezes, sobrar 2 minas e sua "habilidade" está disponível (recarga = 0).
Você não atingiu nada, nem afundou nenhum navio e também não foi atingido.
Você não está debaixo d'água, seus escudos (se houver) não estão ativados e sua verificação também não.
Mais no mapa mais tarde ...

Resultado

Você precisa emitir uma String descrevendo quais ações você executará neste turno. A ordem dos caracteres em sua String de saída definirá as ordens das ações. Você pode executar as mesmas ações várias vezes se não exceder os limites do seu navio. Se uma ou várias ações forem inválidas, cada uma será considerada separadamente W. Aqui está a lista de ações disponíveis:

M: Mova 1 célula na direção em que você está voltado (consuma 1 movimento)
B: Retroceda 1 célula na direção em que está voltado (consuma 1 movimento)
C: Gire seu navio no sentido horário (consuma 1 movimento / livre para Destruidores)
K: Gire seu navio no sentido anti-horário (consuma 1 movimento / livre para Destruidores)
A: Bata o seu navio na direção em que está voltado (funciona apenas se outro navio estiver ocupando a célula na direção que você está enfrentando / não move o seu navio / consome todos os movimentos)
F: Dispara 1 tiro em uma célula ao alcance (consuma 1 tiro). Deve ser seguido pela célula alvejado neste formato ([+ -] X [+ -]) Y / exemplo: F+2-3)
N: Colocar uma mina para uma célula adjacente ao seu navio (consumir todos os disparos e uma mina). Deve ser seguido pela célula de destino neste formato ([+ -] X [+ -]) Y / exemplo N+0+1:)
S: Ative sua varredura para o próximo turno (consuma todos os tiros)
R: Repare o casco danificado o mais próximo possível da "cabeça" do seu navio (consuma todos os tiros, recarga = 3 turnos / apenas Cruiser)
P: Mergulho / Superfície (consuma todos os tiros, cooldown = 3 turnos, duração máxima = 5 turnos / apenas submarino)
D: ative seu escudo evitando o próximo dano durante o próximo turno (consuma todos os tiros, cooldown = 3 / apenas navio de guerra)
W: espera (não faz nada)

Esclarecimento : "Consuma todos os movimentos / tacadas" significa que você só pode usar esta ação se não tiver usado nenhuma das suas tacadas / tacadas antes durante este turno.

Saída de exemplo

MF+9-8CM : Move 1 célula, depois dispara na célula cuja posição relativa à "cabeça" da sua nave é (targetX = X + 9, targetY = Y - 8), gira no sentido horário e finalmente move 1 célula novamente.

Jogabilidade

The Grid

Aqui está um exemplo de grade (33 x 13) onde 3 jogadores são colocados:

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

Como podemos ver, há também uma mina Mao lado do jogador 1.

Vamos levar o jogador 2 para entender o posicionamento e a direção:

A posição do jogador 2 é X = 3, Y = 4, Direção = 3. Como a direção é "Inferior", o restante de suas "células do navio" são posicionadas "sobre" sua "cabeça" (X = 3, Y = 3) & (X = 3, Y = 2)

Mapa do jogador

O último argumento que cada jogador recebe é o seu mapa "próprio". Por padrão, uma nave detecta tudo em um intervalo de 5 células , mas pode ativar uma Varredura para aumentar esse intervalo para 9 .

O argumento sempre tem 361 (19 x 19) caracteres. Representa o quadrado centrado em torno da "cabeça" da sua nave, onde cada personagem corresponde a um elemento definido desta maneira:

.: Célula vazia
O: Sua nave
M: Minas
X: Parede (células fora do mapa)
U: Desconhecido (será revelado por uma varredura)
A: Célula não
Bdanificada do
Cnavio inimigo: Célula danificada do navio inimigo: Célula danificada do navio inimigo: Célula subaquática não danificada do navio inimigo (somente vista com uma varredura)
D: Célula danificada por navio submarino inimigo (vista apenas com um exame)
W: Naufrágios (navio morto)

A sequência é composta por 19 caracteres da primeira linha, seguidos por 19 caracteres da segunda linha ... até a 19ª linha.

Vamos dar uma olhada no que o jogador 2 recebe com e sem uma varredura (quebras de linha para melhor entendimento, mas não são enviadas aos jogadores):

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

Minas

As minas são acionadas quando um navio se move para uma célula ocupada por uma mina ou quando um tiro é disparado na mina. Minas não podem ser acionadas com a ação "Ram".

Minas causam dano AOE (1 alcance de dano de respingo) a todos, até à pessoa que colocou a mina. As minas podem desencadear explosões em "cadeia" se outra mina estiver no raio da explosão.

Rotações

As rotações são simetrias centrais centralizadas na "cabeça" do navio. As rotações só acionarão uma mina se ela for colocada na "posição de destino" (você não ativará minas em um arco.

Área de efeito

1 dano de respingo de alcance (para minas e tiros de Carrier) é definido por um quadrado de 3x3 (9 células) centrado no tiro / explosão inicial (x, y). Ele atinge as coordenadas:[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]

Pontuação

A pontuação é definida por esta fórmula:
Score = Hits + (Sunken x 5) - Damage taken - (Alive ? 0 : 10)

em que::
hitsnúmero de acertos no navio inimigo, por explosão de Ram, Shot ou Mine (1 acerto de célula do navio inimigo danificada, incluindo explosões em cadeia)
sunken: número de "último golpe" em um navio inimigo que o afundou
damage: número de acertos recebidos (não diminuídos pelo reparo, mas impedidos pelo escudo)
alive: verifica se o seu navio está vivo no final (pelo menos 1 célula do casco sem danos)

Controlador

Você pode encontrar o controlador no GitHub . Ele também contém dois samplebots, escritos em Java. Para executá-lo, confira o projeto e abra-o no seu IDE Java. O ponto de entrada no método principal da classe Game. É necessário o Java 8.

Para adicionar bots, primeiro você precisa da versão compilada para Java (arquivos .class) ou das fontes para linguagens interpretadas. Coloque-os na pasta raiz do projeto. Em seguida, crie uma nova classe Java no pacote players (você pode dar um exemplo nos bots já existentes). Esta classe deve implementar o Player para substituir o método String getCmd (). A String retornada é o comando do shell para executar seus bots. Você pode, por exemplo, fazer um bot Ruby funcionar com este comando: retornar "C: \ Ruby \ bin \ ruby.exe MyBot.rb" ;. Por fim, adicione o bot na matriz de jogadores na parte superior da classe Game.

Regras

  • Os bots não devem ser escritos para vencer ou suportar outros bots específicos.
  • A gravação em arquivos é permitida. Por favor escreva para "YOURSubmissionname.txt", a pasta será esvaziada antes do início do jogo. Outros recursos externos não são permitidos.
  • O seu envio tem 1 segundo para responder.
  • Forneça comandos para compilar e executar seus envios.
  • Você pode escrever vários envios

Idiomas suportados

Vou tentar dar suporte a todos os idiomas, mas ele precisa estar disponível online gratuitamente. Por favor, forneça instruções de instalação se você não estiver usando um idioma "mainstream".

A partir de agora, eu posso executar: Java 6-7-8, PHP, Ruby, Perl, Python 2-3, Lua, R, node.js, Haskell, Kotlin, C ++ 11.

Thrax
fonte
KotH interessante, eu só tenho algumas perguntas: podemos escrever vários envios (um para cada tipo de navio, por exemplo)? Quando você está falando de AoE, é um quadrado ao redor da posição correta (atinge [x + 1; y + 1])?
21417
@Katenkyo Sim, você pode escrever vários envios. Sim, atinge 9 células:[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 16/05
então, o submarino surge automaticamente? em qual turno?
Destructible Lemon
também são revezadas simultaneamente?
Destructible Lemon
também o que é útil sobre a capacidade de memória RAM? (por que não apenas a atirar?)
Destrutível Lemon

Respostas:

3

RandomBot

Este é um exemplo de bot. Ele escolhe um navio, uma ação e uma célula alvo (se necessário) aleatoriamente.

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

Este é um exemplo de bot. Não faz nada.

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
fonte
3

PeaceMaker, Python 2 (encouraçado)

O PeaceMaker atira 3 vezes nos inimigos mais próximos (distância em espiral) e se move para frente e para trás em uma linha enquanto permanece a pelo menos 2 células das minas.

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
fonte
1
'PeaceMaker atira'. Você me perdeu lá.
Okx 17/05/19