KOTH: Frappé et coulé

12

introduction

Pour mon 5ème KOTH, je vous présente un défi basé sur le célèbre jeu Battleship avec quelques rebondissements. Vous ne commanderez qu'un seul navire, dont vous pourrez choisir le type parmi les 5 classes "traditionnelles", mais vous pourrez entreprendre plusieurs actions à chaque tour, y compris en vous déplaçant! Ceci est joué comme un FFA (Free For All) et votre objectif sera d'être le dernier navire debout.

Principe

Le jeu est au tour par tour. Au début du jeu, vous devrez choisir la classe de votre vaisseau. Ensuite, à chaque tour, les joueurs pourront exécuter plusieurs actions en fonction de leur vaisseau.

Le jeu se déroule sur une grille 2D (X, Y) dont le côté est ainsi défini:
X = 30 + numberOfPlayer
Y = 30 + numberOfPlayer
La position de départ de chaque vaisseau est aléatoire.

L'ordre de jeu est aléatoire à chaque tour, et vous ne connaîtrez pas votre position dans la «file d'attente» ni le nombre de joueurs. Le jeu dure 100 tours ou jusqu'à ce qu'il ne reste qu'un seul vaisseau en vie.

Chaque fois que vous frappez un navire ennemi ou que vous vous faites toucher, vous gagnez ou perdez des points. Le joueur avec le score le plus élevé gagne. Une prime sera remise au gagnant (valeur en fonction du nombre de participants).

Le contrôleur vous fournit une entrée via des arguments de commande, et votre programme doit sortir via stdout.

Syntaxe

Premier tour

Votre programme sera appelé une fois sans aucun argument. Vous devrez sortir un entier entre 1 et 5 (inclus) pour sélectionner votre navire:

1: Destroyer [durée: 2, coups / tour: 3, coups / tour: 1, portée: 9, mines: 4]
Compétence : Rotations libres des navires (pas de temps de recharge)

2: Sous-marin [longueur: 3, coups / tour: 2, coups / tour: 1, portée: 5, mines: 4]
Compétence : Peut plonger / Surface (voir sorties). Sous l'eau, vous ne pouvez utiliser que des actions "Mouvement" et ne pouvez être vu qu'avec un scan. Vous ne pouvez pas être touché par un tir, mais vous pouvez subir des dégâts des mines.

3: Cruiser [longueur: 3, coups / tour: 1, coups / tour: 2, portée: 9, mines: 2]
Compétence : Peut réparer (voir sorties)

4: Cuirassé [durée: 4, coups / tour: 1, coups / tour: 3, portée: 7, mines: 1]
Compétence : Peut se protéger (voir les sorties)

5: Porteur [longueur: 5, coups / tour: 1, coups / tour: 1, portée: 7, mines: 3]
Compétence : Les coups infligent des dégâts AOE (zone d'effet) à la cible (1 dégâts d'éclaboussures de portée). Si la cible est touchée par le tir, jusqu'à 2 cellules de ce vaisseau seront également endommagées.

Se tourne

Contribution

Chaque fois que votre programme est appelé, il recevra des arguments dans ce format:

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

Les tours sont indexés 1.

Exemple d'entrée

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

Ici, c'est le 1er tour, vous êtes le joueur 8.
Votre vaisseau est positionné sur (X = 1, Y = 12) et votre direction est vers le haut (0 = Haut, 1 = Droite, 2 = Bas, 3 = Gauche ).
Votre coque n'est pas endommagée (votre navire a une longueur de 3 et chaque bit est vrai [1 ​​= OK, 0 = Endommagé]). Vous pouvez vous déplacer 1 fois, tirer 2 fois, il vous reste 2 mines et votre "compétence" est disponible (temps de recharge = 0).
Vous n'avez rien touché, vous n'avez coulé aucun navire et vous n'avez pas été touché non plus.
Vous n'êtes pas sous l'eau, vos boucliers (le cas échéant) ne sont pas activés et votre scan ne l'est pas non plus.
Plus sur la carte plus tard ...

Production

Vous devez produire une chaîne décrivant les actions que vous exécuterez ce tour. L'ordre des caractères dans votre chaîne de sortie définira les ordres des actions. Vous pouvez générer plusieurs fois les mêmes actions si elles ne dépassent pas les limites de votre vaisseau. Si une ou plusieurs actions ne sont pas valides, chacune sera considérée séparément comme W. Voici la liste des actions disponibles:

M: Déplacez 1 cellule dans la direction vers laquelle vous faites face (consommez 1 mouvement)
B: reculez de 1 cellule dans la direction dans laquelle vous êtes face (consommez 1 mouvement)
C: tournez votre vaisseau dans le sens des aiguilles d'une montre (consommez 1 mouvement / libre pour les destroyers)
K: faites pivoter votre vaisseau dans le sens inverse des aiguilles d'une montre (consommer 1 mouvement / gratuit pour les destroyers)
A: enfoncez votre navire dans la direction vers laquelle vous faites face (ne fonctionne que si un autre navire occupe la cellule dans la direction dans laquelle vous êtes face / ne déplace pas votre navire / consomme tous les mouvements)
F: Tirez 1 tir sur une cellule à portée (consommez 1 tir). Doit être suivi par la cellule ciblée dans ce format ([+ -] X [+ -]) Y / exemple: F+2-3)
N: Placer 1 mine une cellule adjacente à votre vaisseau (consommer tous les coups de feu et 1 mien). Doit être suivi par la cellule ciblée dans ce format ([+ -] X [+ -]) Y / exemple: N+0+1)
S: Activez votre scan pour le prochain tour (consommez tous les tirs)
R: Réparez la coque endommagée la plus proche de la "tête" de votre vaisseau (consommez tous les tirs, temps de recharge = 3 tours / Cruiser uniquement)
P: Plongeon / Surface (consommez tous les tirs, temps de recharge = 3 tours, durée maximale = 5 tours / sous-marin uniquement)
D: activez votre bouclier pour éviter les prochains dégâts lors de votre prochain tour (consommez tous les coups, temps de recharge = 3 / cuirassé uniquement)
W: Attendez (ne fait rien)

Clarification : "Consommez tous les coups / coups" signifie que vous ne pouvez utiliser cette action que si vous n'avez utilisé aucun de vos coups / coups avant pendant ce tour.

Exemple de sortie

MF+9-8CM : Déplace 1 cellule, puis tire sur la cellule dont la position relative par rapport à la "tête" de votre vaisseau est (targetX = X + 9, targetY = Y - 8), tourne dans le sens des aiguilles d'une montre et finalement déplace à nouveau 1 cellule.

Gameplay

La grille

Voici un exemple de grille (33 x 13) où 3 joueurs sont placés:

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

Comme nous pouvons le voir, il y a aussi une Mine Mjuste à côté du joueur 1.

Prenons le joueur 2 pour comprendre le positionnement et la direction:

La position du joueur 2 est X = 3, Y = 4, Direction = 3. Puisque sa direction est "Bas", le reste de ses "cellules de vaisseau" sont positionnées "sur" sa "tête" (X = 3, Y = 3) & (X = 3, Y = 2)

Carte du joueur

Le dernier argument que chaque joueur reçoit est sa "propre" carte. Par défaut, un navire détecte tout dans une plage de 5 cellules , mais il peut activer un scan pour augmenter cette plage à 9 .

L'argument comporte toujours 361 (19 x 19) caractères. Il représente le carré centré autour de la "tête" de votre vaisseau, où chaque personnage correspond à un élément ainsi défini:

.: Cellule vide
O: Votre vaisseau
M: Mines
X: Mur (cellules hors de la carte)
U: Inconnu (sera révélé par un scan)
A: Cellule
Bennemie non endommagée : Cellule ennemie endommagée
C: Cellule ennemie sous-marine non endommagée (uniquement visible avec un scan)
D: Cellule sous-marine endommagée du navire ennemi (visible uniquement avec un scan)
W: Épave (navire mort)

La chaîne est composée de 19 caractères de la première ligne, suivis de 19 caractères des secondes lignes ... jusqu'à la 19e ligne.

Jetons un œil à ce que le joueur 2 reçoit avec et sans scan (sauts de ligne pour une meilleure compréhension, mais pas à envoyer aux joueurs):

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

Mines

Les mines sont déclenchées lorsqu'un navire se déplace vers une cellule occupée par une mine ou lorsqu'un coup de feu est tiré sur la mine. Les mines ne peuvent pas être déclenchées avec l'action "Ram".

Les mines infligent des dégâts AOE (1 dégâts d'éclaboussures de portée) à tout le monde, même à la personne qui a placé la mine. Les mines peuvent déclencher des explosions "en chaîne" si une autre mine se trouve dans le rayon de l'explosion.

Rotations

Les rotations sont des symétries centrales centrées sur la «tête» du navire. Les rotations ne déclencheront une mine que si elle est placée sur la "position de destination" (vous ne déclencherez pas de mines dans un arc.

Zone d'effet

1 dégâts d'éclaboussures de portée (pour les mines et les tirs du transporteur) sont définis par un carré de 3x3 (9 cellules) centré sur le tir / explosion initial (x, y). Il atteint ces coordonnées:[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]

Notation

La notation est définie par cette formule:
Score = Hits + (Sunken x 5) - Damage taken - (Alive ? 0 : 10)

où::
hitsnombre de coups sur le navire ennemi, soit par explosion Ram, Shot ou Mine (1 coup par cellule du navire ennemi endommagé, y compris les explosions de chaîne)
sunken: nombre de "dernier coup" sur un navire ennemi qui l'a fait couler
damage: nombre de coups reçus (non diminués par Réparation, mais empêchés par Bouclier)
alive: vérifie si votre navire est vivant à la fin (au moins 1 cellule de coque en bon état)

Manette

Vous pouvez trouver le contrôleur sur GitHub . Il contient également deux exemples de robots, écrits en Java. Pour le faire fonctionner, consultez le projet et ouvrez-le dans votre IDE Java. Le point d'entrée dans la méthode principale du jeu de classe. Java 8 requis.

Pour ajouter des bots, vous avez d'abord besoin de la version compilée pour Java (fichiers .class) ou des sources des langages interprétés. Placez-les dans le dossier racine du projet. Ensuite, créez une nouvelle classe Java dans le package Players (vous pouvez prendre exemple sur les bots déjà existants). Cette classe doit implémenter Player pour remplacer la méthode String getCmd (). La chaîne renvoyée est la commande shell pour exécuter vos bots. Vous pouvez par exemple faire fonctionner un bot Ruby avec cette commande: return "C: \ Ruby \ bin \ ruby.exe MyBot.rb" ;. Enfin, ajoutez le bot dans le tableau des joueurs en haut de la classe Game.

Règles

  • Les bots ne doivent pas être écrits pour battre ou soutenir d'autres bots spécifiques.
  • L'écriture dans des fichiers est autorisée. Veuillez écrire à "votrenomdemission.txt", le dossier sera vidé avant le début du jeu. D'autres ressources externes sont interdites.
  • Votre soumission a 1 seconde pour répondre.
  • Fournissez des commandes pour compiler et exécuter vos soumissions.
  • Vous pouvez rédiger plusieurs soumissions

Langues prises en charge

Je vais essayer de prendre en charge toutes les langues, mais elles doivent être disponibles en ligne gratuitement. Veuillez fournir des instructions pour l'installation si vous n'utilisez pas un langage "grand public".

Pour l'instant, je peux exécuter: Java 6-7-8, PHP, Ruby, Perl, Python 2-3, Lua, R, node.js, Haskell, Kotlin, C ++ 11.

Thrax
la source
Intéressant KotH, j'ai juste quelques questions: pouvons-nous rédiger plusieurs soumissions (une pour chaque type de navire par exemple)? Quand vous parlez d'AoE, c'est un carré autour de la position à droite (il frappe [x + 1; y + 1])?
Katenkyo
@Katenkyo Oui, vous pouvez rédiger plusieurs soumissions. Oui, il frappe 9 cellules:[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
alors, le sous-marin refait surface automatiquement? à quel tour?
Destructible Lemon
les virages sont-ils également pris simultanément?
Destructible Lemon
aussi ce qui est utile sur la capacité de RAM (pourquoi ne pas simplement tourner?)
Destructible Lemon

Réponses:

3

RandomBot

Ceci est un exemple de bot. Il choisit un vaisseau, une action et une cellule cible (si besoin) au hasard.

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

Ceci est un exemple de bot. Ça ne fait rien.

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
la source
3

PeaceMaker, Python 2 (cuirassé)

PeaceMaker tire 3 fois sur les ennemis les plus proches (distance en spirale) et se déplace d'avant en arrière en ligne tout en restant à au moins 2 cellules des mines.

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
la source
1
«PeaceMaker tire». Tu m'as perdu là-bas.
Okx