MISE À JOUR: isSuicidal () a été ajouté à la classe d'avion, cela vous permet de vérifier si un avion est sur une trajectoire de collision irréversible avec les murs !!
UPDATE: updateCoolDown () séparé de simulateMove ()
MISE À JOUR: wrapper d'entrée non Java, écrit par Sparr , disponible pour les tests, voir les commentaires
MISE À JOUR Zove Games a écrit un visualiseur 3D génial pour ce KOTH, voici une vidéo youtube de merde de PredictAndAVoid combattant PredictAndAVoid.
La fonction simulateMove () de la classe Plane a été légèrement modifiée afin de ne plus mettre à jour le refroidissement, utilisez pour cela la nouvelle fonction updateCoolDown () après le tournage. Le nouveau isSuicidal () retourne vrai si un avion est lié à la mort, utilisez-le pour tailler les mouvements ennemis et éviter de heurter les murs. Pour obtenir le code mis à jour, remplacez simplement les classes Controller et Plane par celles du dépôt github.
La description
Le but de ce défi est de coder deux avions de combat qui s'affronteront contre deux avions par un autre concurrent. À chaque tour, vous vous déplacez d'une case et avez la possibilité de tirer. C'est ça, c'est aussi simple que ça.
Enfin, presque ...
Arène et mouvements possibles
L'arène est un mur de 14x14x14 dans l'espace. les avions du concurrent 1 partent aux endroits (0,5,0) et (0,8,0) et ceux du concurrent 2 aux (13,5,13) et (13,8,13). Tous les avions commencent par voler horizontalement loin des murs verticaux dont ils sont les plus proches.
Maintenant que vous pilotez des avions et non des hélicoptères, vous ne pouvez pas simplement changer de direction à volonté ou même arrêter de bouger, donc chaque avion a une direction et déplace une tuile dans cette direction à chaque tour.
Les directions possibles sont: Nord (N), Sud (S), Est (E), Ouest (W), Haut (U) et Bas (D) et toute combinaison logique de ces six. Où l'axe NS correspond à l'axe x, WE à y et DU à z. NW, SU et NED viennent à l'esprit comme exemples possibles de directions; UD est un excellent exemple d'une combinaison non valide.
Vous pouvez bien sûr changer la direction de vos avions, mais il y a une limitation, vous ne pouvez changer votre direction que de 45 degrés au maximum. Pour visualiser cela, saisissez votre cube de rubik (je sais que vous en avez un) et imaginez que les 26 petits cubes externes sont les directions possibles (les directions à une lettre sont des faces, les directions à deux lettres sont des bords et les directions à trois lettres sont des coins). Si vous vous dirigez dans une direction représentée par un petit cube, vous pouvez changer la direction de chaque cube qui touche le vôtre (toucher en diagonale compte, mais seulement toucher visiblement, ce n'est pas toucher à travers le cube).
Une fois que tous les avions ont indiqué dans quelle direction ils aimeraient changer, ils le font et déplacent une tuile simultanément.
Vous pouvez également choisir de vous déplacer dans une direction valide mais de continuer à voler dans la direction que vous alliez, au lieu de changer votre direction dans la direction vers laquelle vous vous êtes déplacé. Ceci est analogue à la différence entre une voiture qui passe un virage et une voiture qui change de voie.
Tirer et mourir
Vous pouvez tirer au maximum une fois par tour et cela doit être décidé en même temps que vous décidez dans quelle direction voler et si vous voulez garder votre avion (et par extension, votre arme) pointé dans la même direction ou non. La balle est tirée juste après le déplacement de votre avion. Il y a un refroidissement d'un tour après le tir, au troisième tour, vous êtes prêt à recommencer. Vous ne pouvez tirer que dans la direction dans laquelle vous volez. Une balle est instantanée et vole en ligne droite jusqu'à ce qu'elle heurte un mur ou un avion.
Compte tenu de la façon dont vous pouvez changer de direction ainsi que des `` changements de voie '', cela signifie que vous pouvez menacer une colonne de 3x3 lignes devant vous en plus de quelques lignes diagonales simples.
S'il frappe un avion, cet avion meurt et disparaît rapidement du plateau (car il explose totalement ou quelque chose). Les balles ne peuvent frapper qu'un seul avion au maximum. Les balles sont tirées simultanément, donc deux avions peuvent se tirer dessus. Cependant, deux balles ne peuvent pas entrer en collision (triste, je sais).
Cependant, deux avions peuvent entrer en collision (s'ils se retrouvent dans le même cube et NON s'ils se croisent sans se retrouver dans le même plan), ce qui entraîne la mort des deux avions (et une explosion totale). Vous pouvez également voler dans le mur, ce qui entraînera l'avion en question à mourir et à être mis dans le coin pour réfléchir à ses actions. Les collisions sont traitées avant le tournage.
Communication avec le contrôleur
J'accepterai les entrées en java ainsi que dans d'autres langues. Si votre entrée est en java, vous obtiendrez une entrée via STDIN et une sortie via STDOUT.
Si votre entrée est en java, votre entrée doit étendre la classe suivante:
package Planes;
//This is the base class players extend.
//It contains the arena size and 4 plane objects representing the planes in the arena.
public abstract class PlaneControl {
// note that these planes are just for your information, modifying these doesn't affect the actual plane instances,
// which are kept by the controller
protected Plane[] myPlanes = new Plane[2];
protected Plane[] enemyPlanes = new Plane[2];
protected int arenaSize;
protected int roundsLeft;
...
// Notifies you that a new fight is starting
// FightsFought tells you how many fights will be fought.
// the scores tell you how many fights each player has won.
public void newFight(int fightsFought, int myScore, int enemyScore) {}
// notifies you that you'll be fighting anew opponent.
// Fights is the amount of fights that will be fought against this opponent
public void newOpponent(int fights) {}
// This will be called once every round, you must return an array of two moves.
// The move at index 0 will be applied to your plane at index 0,
// The move at index1 will be applied to your plane at index1.
// Any further move will be ignored.
// A missing or invalid move will be treated as flying forward without shooting.
public abstract Move[] act();
}
L'instance créée de cette classe persistera pendant toute la compétition, vous pouvez donc stocker toutes les données que vous souhaitez stocker dans des variables. Lisez les commentaires dans le code pour plus d'informations.
Je vous ai également fourni les classes d'assistance suivantes:
package Planes;
//Objects of this class contain all relevant information about a plane
//as well as some helper functions.
public class Plane {
private Point3D position;
private Direction direction;
private int arenaSize;
private boolean alive = true;
private int coolDown = 0;
public Plane(int arenaSize, Direction direction, int x, int y, int z) {}
public Plane(int arenaSize, Direction direction, Point3D position) {}
// Returns the x coordinate of the plane
public int getX() {}
// Returns the y coordinate of the plane
public int getY() {}
// Returns the z coordinate of the plane
public int getZ() {}
// Returns the position as a Point3D.
public Point3D getPosition() {}
// Returns the distance between the plane and the specified wall,
// 0 means right next to it, 19 means at the opposite side.
// Returns -1 for invalid input.
public int getDistanceFromWall(char wall) {}
// Returns the direction of the plane.
public Direction getDirection() {}
// Returns all possible turning directions for the plane.
public Direction[] getPossibleDirections() {}
// Returns the cool down before the plane will be able to shoot,
// 0 means it is ready to shoot this turn.
public int getCoolDown() {}
public void setCoolDown(int coolDown) {}
// Returns true if the plane is ready to shoot
public boolean canShoot() {}
// Returns all positions this plane can shoot at (without first making a move).
public Point3D[] getShootRange() {}
// Returns all positions this plane can move to within one turn.
public Point3D[] getRange() {}
// Returns a plane that represents this plane after making a certain move,
// not taking into account other planes.
// Doesn't update cool down, see updateCoolDown() for that.
public Plane simulateMove(Move move) {}
// modifies this plane's cool down
public void updateCoolDown(boolean shot) {
coolDown = (shot && canShoot())?Controller.COOLDOWN:Math.max(0, coolDown - 1);
}
// Returns true if the plane is alive.
public boolean isAlive() {}
// Sets alive to the specified value.
public void setAlive(boolean alive) {}
// returns a copy of itself.
public Plane copy() {}
// Returns a string representing its status.
public String getAsString() {}
// Returns a string suitable for passing to a wrapped plane process
public String getDataString() {}
// Returns true if a plane is on an irreversable colision course with the wall.
// Use this along with simulateMove() to avoid hitting walls or prune possible emeny moves.
public boolean isSuicidal() {}
}
// A helper class for working with directions.
public class Direction {
// The three main directions, -1 means the first letter is in the direction, 1 means the second is, 0 means neither is.
private int NS, WE, DU;
// Creates a direction from 3 integers.
public Direction(int NSDir, int WEDir, int DUDir) {}
// Creates a direction from a directionstring.
public Direction(String direction) {}
// Returns this direction as a String.
public String getAsString() {}
// Returns The direction projected onto the NS-axis.
// -1 means heading north.
public int getNSDir() {}
// Returns The direction projected onto the WE-axis.
// -1 means heading west.
public int getWEDir() {}
// Returns The direction projected onto the DU-axis.
// -1 means heading down.
public int getDUDir() {}
// Returns a Point3D representing the direction.
public Point3D getAsPoint3D() {}
// Returns an array of chars representing the main directions.
public char[] getMainDirections() {}
// Returns all possible turning directions.
public Direction[] getPossibleDirections() {}
// Returns true if a direction is a valid direction to change to
public boolean isValidDirection(Direction direction) {}
}
public class Point3D {
public int x, y, z;
public Point3D(int x, int y, int z) {}
// Returns the sum of this Point3D and the one specified in the argument.
public Point3D add(Point3D point3D) {}
// Returns the product of this Point3D and a factor.
public Point3D multiply(int factor) {}
// Returns true if both Point3D are the same.
public boolean equals(Point3D point3D) {}
// Returns true if Point3D is within a 0-based arena of a specified size.
public boolean isInArena(int size) {}
}
public class Move {
public Direction direction;
public boolean changeDirection;
public boolean shoot;
public Move(Direction direction, boolean changeDirection, boolean shoot) {}
}
Vous pouvez créer des instances de ces classes et utiliser autant de fonctions que vous le souhaitez. Vous pouvez trouver le code complet de ces classes d'assistance ici .
Voici un exemple de ce à quoi votre entrée pourrait ressembler (j'espère que vous ferez mieux que moi, cependant, la plupart des matchs avec ces avions se terminent avec eux volant dans un mur, malgré leurs meilleurs efforts pour éviter le mur.):
package Planes;
public class DumbPlanes extends PlaneControl {
public DumbPlanes(int arenaSize, int rounds) {
super(arenaSize, rounds);
}
@Override
public Move[] act() {
Move[] moves = new Move[2];
for (int i=0; i<2; i++) {
if (!myPlanes[i].isAlive()) {
moves[i] = new Move(new Direction("N"), false, false); // If we're dead we just return something, it doesn't matter anyway.
continue;
}
Direction[] possibleDirections = myPlanes[i].getPossibleDirections(); // Let's see where we can go.
for (int j=0; j<possibleDirections.length*3; j++) {
int random = (int) Math.floor((Math.random()*possibleDirections.length)); // We don't want to be predictable, so we pick a random direction out of the possible ones.
if (myPlanes[i].getPosition().add(possibleDirections[random].getAsPoint3D()).isInArena(arenaSize)) { // We'll try not to fly directly into a wall.
moves[i] = new Move(possibleDirections[random], Math.random()>0.5, myPlanes[i].canShoot() && Math.random()>0.2);
continue; // I'm happy with this move for this plane.
}
// Uh oh.
random = (int) Math.floor((Math.random()*possibleDirections.length));
moves[i] = new Move(possibleDirections[random], Math.random()>0.5, myPlanes[i].canShoot() && Math.random()>0.2);
}
}
return moves;
}
@Override
public void newFight(int fightsFought, int myScore, int enemyScore) {
// Using information is for schmucks.
}
@Override
public void newOpponent(int fights) {
// What did I just say about information?
}
}
DumbPlanes rejoindra le tournoi avec les autres entrées, donc si vous finissez dernier, c'est de votre faute pour au moins faire mieux que DumbPlanes.
Les restrictions
Les restrictions mentionnées dans le wiki KOTH s'appliquent:
- Toute tentative de bricolage avec le contrôleur, le temps d'exécution ou d'autres soumissions sera disqualifiée. Toutes les soumissions ne devraient fonctionner qu'avec les entrées et le stockage qui leur sont fournis.
- Les robots ne doivent pas être écrits pour battre ou soutenir d'autres robots. (Cela peut être souhaitable dans de rares cas, mais s'il ne s'agit pas d'un concept central du défi, il est préférable de l'exclure.)
- Je me réserve le droit de disqualifier les soumissions qui utilisent trop de temps ou de mémoire pour exécuter des essais avec une quantité raisonnable de ressources.
- Un bot ne doit pas implémenter la même stratégie exacte qu'une stratégie existante, intentionnellement ou accidentellement.
Tester votre soumission
Téléchargez le code du contrôleur ici . Ajoutez votre soumission en tant que Something.java. Modifiez Controller.java pour inclure les entrées de votre avion dans les entrées [] et les noms []. Compilez tout en tant que projet Eclipse ou avec javac -d . *.java
, puis exécutez le contrôleur avec java Planes/Controller
. Un journal du concours sera disponible test.txt
, avec un tableau de bord à la fin. Vous pouvez également appeler matchUp()
directement avec deux entrées comme arguments pour simplement tester deux plans l'un contre l'autre.
Gagner le combat
Le vainqueur du combat est celui qui a le dernier avion volant, si après 100 tours, il reste encore plus d'une équipe, l'équipe avec le plus d'avions restants gagne. Si c'est égal, c'est un match nul.
Scoring et compétition
Le prochain tournoi officiel aura lieu lorsque la prime actuelle sera épuisée.
Chaque entrée combattra chaque autre entrée (au moins) 100 fois, le vainqueur de chaque match sera celui qui aura le plus de victoires sur 100 et recevra 2 points. En cas d'égalité, les deux participations obtiennent 1 point.
Le vainqueur de la compétition est celui qui a le plus de points. En cas d'égalité, le vainqueur est celui qui a gagné dans un match up entre les entrées qui ont fait match nul.
Selon le nombre d'entrées, le nombre de combats entre les entrées pourrait être considérablement augmenté, je pourrais également sélectionner les 2-4 meilleures entrées après le premier tournoi et mettre en place un tournoi d'élite entre ces entrées avec plus de combats (et éventuellement plus de tours par bats toi)
(préliminaire) Tableau de bord
Nous avons une nouvelle entrée qui prend fermement la deuxième place dans un autre tournoi passionnant , il semble que Crossfire soit incroyablement difficile à tirer pour tout le monde, sauf pour PredictAndAvoid. Notez que ce tournoi a été organisé avec seulement 10 combats entre chaque ensemble d'avions et n'est donc pas une représentation entièrement exacte de la situation.
----------------------------
¦ 1. PredictAndAvoid: 14 ¦
¦ 2. Crossfire: 11 ¦
¦ 3. Weeeeeeeeeeee: 9 ¦
¦ 4. Whirligig: 8 ¦
¦ 4. MoveAndShootPlane: 8 ¦
¦ 6. StarFox: 4 ¦
¦ 6. EmoFockeWulf: 2 ¦
¦ 7. DumbPlanes: 0 ¦
----------------------------
Voici un exemple de sortie du wrapper non Java:
NEW CONTEST 14 20
indique qu'un nouveau concours commence, dans une arène 14x14x14, et qu'il impliquera 20 tours par combat.
NEW OPPONENT 10
indique que vous faites face à un nouvel adversaire et que vous combattrez cet adversaire 10 fois
NEW FIGHT 5 3 2
indique qu'un nouveau combat contre l'adversaire actuel commence, que vous avez combattu cet adversaire 5 fois jusqu'à présent, en gagnant 3 et en perdant 2 combats
ROUNDS LEFT 19
indique qu'il reste 19 rounds dans le combat en cours
NEW TURN
indique que vous êtes sur le point de recevoir des données pour les quatre avions pour ce tour de combat
alive 13 8 13 N 0
alive 13 5 13 N 0
dead 0 0 0 N 0
alive 0 8 0 S 0
Ces quatre lignes indiquent que vos deux avions sont vivants, aux coordonnées [13,8,13] et [13,5,13] respectivement, tous deux orientés vers le nord, tous les deux avec un temps de recharge nul. Le premier avion ennemi est mort, et le second est vivant, à [0,8,0] et face au sud avec zéro temps de recharge.
À ce stade, votre programme doit produire deux lignes similaires à la suivante:
NW 0 1
SU 1 0
Cela indique que votre premier avion voyagera vers le nord-ouest, sans se détourner de son cap actuel, et tirera si possible. Votre deuxième avion voyagera vers SouthUp, se tournant pour faire face à SouthUp, sans tirer.
Maintenant, vous êtes ROUNDS LEFT 18
suivi par NEW TURN
etc. Cela continue jusqu'à ce que quelqu'un gagne ou que le round expire, auquel cas vous obtenez une autre NEW FIGHT
ligne avec le compte de combat et les scores mis à jour, éventuellement précédé d'un NEW OPPONENT
.