KOTH: Hit y hundido

12

Introducción

Para mi 5to KOTH, les presento un desafío basado en el conocido juego Battleship con algunos giros. Solo comandarás una nave, cuyo tipo podrás seleccionar entre las 5 clases "tradicionales", ¡pero podrás realizar múltiples acciones en cada turno, incluido el movimiento! Esto se juega como un FFA (Free For All) y su objetivo será ser el último barco en pie.

Principio

El juego está basado en turnos. Al comienzo del juego, tendrás que elegir la clase de tu nave. Luego, cada turno, los jugadores podrán ejecutar varias acciones dependiendo de su nave.

El juego se desarrolla en una cuadrícula 2D (X, Y) cuyo lado se define de esta manera:
X = 30 + numberOfPlayer
Y = 30 + numberOfPlayer
la posición inicial de cada barco es aleatoria.

El orden de juego es aleatorio cada turno, y no sabrás tu posición en la "cola" ni la cantidad de jugadores. El juego dura 100 turnos o hasta que solo quede un barco con vida.

Cada vez que golpeas un barco enemigo o te golpean, ganarás o perderás puntos. El jugador con la puntuacion mas alta gana. Se otorgará una recompensa al ganador (el valor depende del número de participantes).

El controlador le proporciona entrada a través de argumentos de comando, y su programa tiene que salir a través de stdout.

Sintaxis

Primer turno

Se llamará a su programa una vez sin ningún argumento. Tendrás que superar un número entero entre 1 y 5 (inclusive) para seleccionar tu nave:

1: Destructor [longitud: 2, movimientos / turno: 3, disparos / turno: 1, rango: 9, minas: 4]
Habilidad : Rotaciones de barco gratis (sin enfriamiento)

2: Submarino [longitud: 3, movimientos / turno: 2, disparos / turno: 1, rango: 5, minas: 4]
Habilidad : puede hundir / superficie (ver resultados). Mientras está bajo el agua, solo puede usar acciones de "Movimiento" y solo puede verse con un escaneo. No puedes ser alcanzado por un disparo, pero puedes recibir daño de las minas.

3: Crucero [longitud: 3, movimientos / turno: 1, disparos / turno: 2, alcance: 9, minas: 2]
Habilidad : Puede reparar (ver resultados)

4: Acorazado [longitud: 4, movimientos / turno: 1, disparos / turno: 3, rango: 7, minas: 1]
Habilidad : Escudo de latas (ver resultados)

5: Portador [longitud: 5, movimientos / turno: 1, disparos / turno: 1, rango: 7, minas: 3]
Habilidad : Los disparos infligen daño AOE (Área de efecto) al objetivo (1 daño de salpicadura de rango). Si el objetivo es alcanzado con el disparo, también se dañarán hasta 2 celdas de esta nave.

Vueltas

Entrada

Cada vez que se llama a su programa, recibirá argumentos en este formato:

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

Las rondas están indexadas en 1.

Entrada de ejemplo

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

Aquí, es la primera ronda, eres el jugador 8.
Tu nave está posicionada en (X = 1, Y = 12) y tu dirección es hacia la parte superior (0 = Superior, 1 = Derecha, 2 = Inferior, 3 = Izquierda )
Su casco no está dañado (su nave tiene una longitud de 3 y cada bit es verdadero [1 = OK, 0 = Dañado]). Puedes moverte 1 vez, disparar 2 veces, te quedan 2 minas y tu "habilidad" está disponible (tiempo de reutilización = 0).
No golpeaste nada, ni hundiste ningún barco y tampoco te golpearon.
No estás bajo el agua, tus escudos (si los hay) no están activados y tu escaneo tampoco.
Más en el mapa más tarde ...

Salida

Debe generar una Cadena que describa las acciones que ejecutará este turno. El orden de los caracteres en su Cadena de salida definirá el orden de las acciones. Puedes generar las mismas acciones varias veces si no supera los límites de tu nave. Si una o varias acciones no son válidas, cada una se considerará por separado W. Aquí está la lista de acciones disponibles:

M: Mueve 1 celda en la dirección que estás mirando (consume 1 movimiento)
B: Retrocede 1 celda desde la dirección que estás mirando (consume 1 movimiento)
C: Gira tu nave en el sentido de las agujas del reloj (consume 1 movimiento / gratis para Destructores)
K: Rota tu nave en sentido contrario a las agujas del reloj (consume 1 movimiento / gratis para los Destructores)
A: Ramifica tu nave en la dirección que estás mirando (funciona solo si otra nave está ocupando la celda en la dirección que estás mirando / no mueve tu nave / consume todos los movimientos)
F: Dispara 1 disparo a una celda dentro del alcance (consume 1 disparo). Debe ser seguido por la célula targetted en este formato ([+ -] X [+ -]) Y / ejemplo: F+2-3)
N: Lugar 1 de minas a una celda adyacente a su nave (consumir todos los tiros y 1 de minas). Debe ser seguido por la célula targetted en este formato ([+ -] X [+ -]) Y / ejemplo: N+0+1)
S: Active su exploración para el próximo turno (consuma todos los disparos)
R: repare el casco dañado más cercano a la "cabeza" de su nave (consuma todos los disparos, enfriamiento = 3 turnos / Crucero solamente)
P: Zambullida / Superficie (consuma todos los disparos, tiempo de reutilización = 3 turnos, duración máxima = 5 turnos / solo submarino)
D: activa tu escudo para evitar el siguiente daño durante tu próximo turno (consume todos los disparos, tiempo de reutilización = 3 / solo acorazado)
W: espera (no hace nada)

Aclaración : "Consumir todos los movimientos / disparos" significa que solo puedes usar esta acción si no has usado ninguno de tus movimientos / disparos antes durante este turno.

Salida de ejemplo

MF+9-8CM : Mueve 1 celda, luego dispara en la celda cuya posición relativa a la "cabeza" de tu nave es (targetX = X + 9, targetY = Y - 8), gira en sentido horario y finalmente mueve 1 celda nuevamente.

Como se Juega

La cuadrícula

Aquí hay un ejemplo de cuadrícula (33 x 13) donde se colocan 3 jugadores:

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

Como podemos ver, también hay una mina Mjusto al lado del jugador 1.

Tomemos al jugador 2 para entender el posicionamiento y la dirección:

La posición del jugador 2 es X = 3, Y = 4, Dirección = 3. Dado que su dirección es "Inferior", el resto de sus "celdas de barco" se colocan "sobre" su "cabeza" (X = 3, Y = 3) & (X = 3, Y = 2)

Mapa del jugador

El último argumento que cada jugador recibe es su "propio" mapa. Por defecto, un barco detecta todo en un rango de 5 celdas , pero puede activar un Escaneo para aumentar este rango a 9 .

El argumento siempre tiene 361 (19 x 19) caracteres de longitud. Representa el cuadrado centrado alrededor de la "cabeza" de su nave, donde cada personaje corresponde a un elemento definido de esta manera:

.: Celda vacía
O: Tu nave
M: Minas
X: Muro (celdas fuera del mapa)
U: Desconocido (será revelado por un escaneo)
A: Célula no
Bdañada de la
Cnave enemiga: Célula dañada de la nave enemiga: Célula submarina no dañada de la nave enemiga (solo se ve con un escaneo)
D: Nave enemiga célula dañada bajo el agua (solo vista con un escaneo)
W: Restos (nave muerta)

La cadena está compuesta por 19 caracteres de la primera línea, seguidos por 19 caracteres de las segundas líneas ... hasta la línea 19.

Echemos un vistazo a lo que el jugador 2 recibe con y sin escaneo (saltos de línea para una mejor comprensión, pero no enviar a los jugadores):

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

Las minas se activan cuando un barco se mueve a una celda ocupada por una mina o cuando se dispara un tiro sobre la mina. Las minas no se pueden activar con la acción "Ram".

Las minas infligen daño AOE (daño de salpicadura de 1 rango) a todos, incluso a la persona que colocó la mina. Las minas pueden desencadenar explosiones en "cadena" si otra mina está en el radio de la explosión.

Rotaciones

Las rotaciones son simetrías centrales centradas en la "cabeza" del barco. Las rotaciones solo activarán una mina si se coloca en la "posición de destino" (no activará minas en un arco.

Area de efecto

El daño por salpicadura de 1 rango (para minas y disparos de Carrier) se define mediante un cuadrado de 3x3 (9 celdas) centrado en el disparo / explosión inicial (x, y). Golpea esas 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]

Puntuación

La puntuación se define mediante esta fórmula:
Score = Hits + (Sunken x 5) - Damage taken - (Alive ? 0 : 10)

donde::
hitsnúmero de golpes en la nave enemiga, ya sea por explosión de Ram, Disparo o Mina (1 golpe por la célula del barco enemigo dañado, incluidas las explosiones de la cadena)
sunken: número de "último golpe" en una nave enemiga que causó su hundimiento
damage: número de Impactos recibidos (no disminuidos por Reparación, pero evitados por Escudo)
alive: comprueba si su nave está viva al final (al menos 1 celda del casco sin daños)

Controlador

Puedes encontrar el controlador en GitHub . También contiene dos robots de muestra, escritos en Java. Para que se ejecute, consulte el proyecto y ábralo en su IDE de Java. El punto de entrada en el método principal de la clase Juego. Se requiere Java 8.

Para agregar bots, primero necesita la versión compilada para Java (archivos .class) o las fuentes para los idiomas interpretados. Colóquelos en la carpeta raíz del proyecto. Luego, crea una nueva clase Java en el paquete de jugadores (puedes tomar un ejemplo en los bots ya existentes). Esta clase debe implementar Player para anular el método String getCmd (). La cadena devuelta es el comando de shell para ejecutar sus bots. Por ejemplo, puede hacer que un bot de Ruby funcione con este comando: devuelva "C: \ Ruby \ bin \ ruby.exe MyBot.rb" ;. Finalmente, agregue el bot en la matriz de jugadores en la parte superior de la clase Juego.

Reglas

  • Los bots no deben escribirse para vencer o admitir otros bots específicos.
  • Se permite escribir en archivos. Escribe a "yoursubmissionname.txt", la carpeta se vaciará antes de que comience el juego. Otros recursos externos están prohibidos.
  • Su envío tiene 1 segundo para responder.
  • Proporcione comandos para compilar y ejecutar sus envíos.
  • Puedes escribir múltiples envíos

Idiomas soportados

Intentaré admitir todos los idiomas, pero debe estar disponible en línea de forma gratuita. Proporcione instrucciones para la instalación si no está utilizando un lenguaje "convencional".

A partir de ahora, puedo ejecutar: Java 6-7-8, PHP, Ruby, Perl, Python 2-3, Lua, R, node.js, Haskell, Kotlin, C ++ 11.

Thrax
fuente
Interesante KotH, solo tengo algunas preguntas: ¿podemos escribir múltiples presentaciones (una para cada tipo de barco, por ejemplo)? Cuando habla de AoE, ¿es un cuadrado alrededor de la posición correcta (golpea [x + 1; y + 1])?
Katenkyo
@Katenkyo Sí, puedes escribir múltiples envíos. Sí, golpea 9 celdas:[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
Entonces, ¿el submarino emerge automáticamente? en qué turno?
Destructible Lemon
¿También se toman turnos simultáneamente?
Destructible Lemon
También, ¿qué es útil sobre la capacidad de ram? (¿por qué no simplemente disparar?)
Destructible Lemon

Respuestas:

3

RandomBot

Este es un ejemplo de bot. Elige una nave, una acción y una celda objetivo (si es necesario) al azar.

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

}

PasivoBot

Este es un ejemplo de bot. No hace 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
fuente
3

PeaceMaker, Python 2 (acorazado)

PeaceMaker dispara 3 veces a los enemigos más cercanos (distancia en espiral) y se mueve hacia adelante y hacia atrás en una línea mientras se mantiene al menos a 2 celdas de las 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
fuente
1
'PeaceMaker dispara'. Me perdiste allí.
Okx