UPDATE: isSuicidal () wurde der Flugzeugklasse hinzugefügt, mit der Sie überprüfen können, ob sich ein Flugzeug auf einem irreversiblen Kollisionskurs mit den Wänden befindet !!
UPDATE: updateCoolDown () getrennt von simulateMove ()
UPDATE: Nicht-Java-Wrapper, geschrieben von Sparr , zum Testen verfügbar, siehe Kommentare
UPDATE Zove Games Hat einen fantastischen 3D-Visualizer für diese KOTH geschrieben, hier ist ein beschissenes Youtube-Video von PredictAndAVoid, das gegen PredictAndAVoid kämpft.
Die simulateMove () -Funktion der Plane-Klasse wurde geringfügig geändert, sodass das Abkühlen nicht mehr aktualisiert wird. Verwenden Sie dazu nach dem Aufnehmen die neue updateCoolDown () -Funktion. Das neue isSuicidal () gibt true zurück, wenn ein Flugzeug tot enden muss. Verwenden Sie es, um feindliche Bewegungen zu beschneiden und Mauern zu vermeiden. Um den aktualisierten Code zu erhalten, ersetzen Sie einfach die Controller- und Plane-Klassen durch die im Github-Repo.
Beschreibung
Das Ziel dieser Herausforderung ist es, zwei zu codieren Luftkampfflugzeuge , die von einem anderen Teilnehmer gegen zwei Flugzeuge antreten. In jeder Runde bewegst du dich um ein Feld und hast die Möglichkeit zu schießen. So einfach ist das.
Naja fast...
Arena und mögliche Züge
Die Arena ist 14x14x14 im Raum ummauert. Die Flugzeuge von Teilnehmer 1 starten an den Orten (0,5,0) und (0,8,0) und die von Teilnehmer 2 an den Orten (13,5,13) und (13,8,13). Alle Flugzeuge fliegen zunächst horizontal von den vertikalen Wänden weg, denen sie am nächsten sind.
Jetzt, da Sie Flugzeuge und keine Hubschrauber fliegen, können Sie die Richtung nicht einfach nach Belieben ändern oder sogar aufhören, sich zu bewegen. Jedes Flugzeug hat also eine Richtung und bewegt in jeder Runde ein Plättchen in diese Richtung.
Die möglichen Richtungen sind: Nord (N), Süd (S), Ost (E), West (W), Auf (U) und Ab (D) und jede logische Kombination dieser sechs Richtungen. Wo die NS-Achse der x-Achse entspricht, sind WE bis y und DU bis z. NW, SU und NED kommen als mögliche Richtungsbeispiele in den Sinn; UD ist ein großartiges Beispiel für eine ungültige Kombination.
Sie können natürlich die Richtung Ihrer Flugzeuge ändern, aber es gibt eine Einschränkung, Sie können Ihre Richtung nur um höchstens 45 Grad ändern. Um dies zu veranschaulichen, nehmen Sie den Würfel Ihres Rubiks (ich weiß, dass Sie einen haben) und stellen Sie sich vor, dass alle 26 äußeren kleinen Würfel die möglichen Richtungen sind (Richtungen mit einem Buchstaben sind Flächen, Richtungen mit zwei Buchstaben sind Kanten und Richtungen mit drei Buchstaben sind Ecken). Wenn Sie sich in eine Richtung bewegen, die durch einen kleinen Würfel dargestellt wird, können Sie die Richtung für jeden Würfel ändern, der Ihren berührt (diagonal berühren, aber nur sichtbar berühren, dh nicht durch den Würfel berühren).
Nachdem alle Flugzeuge angegeben haben, in welche Richtung sie wechseln möchten, bewegen sie sich gleichzeitig um ein Feld.
Sie können sich auch dafür entscheiden, sich in eine gültige Richtung zu bewegen, aber weiterhin in die Richtung fliegen, in die Sie sich bewegt haben, anstatt Ihre Richtung in die Richtung zu ändern, in die Sie sich bewegt haben. Dies ist analog zu dem Unterschied zwischen einem Auto, das um eine Ecke fährt, und einem Auto, das die Spur wechselt.
Schießen und sterben
Du kannst höchstens einmal pro Runde schießen und dies muss entschieden werden, während du entscheidest, in welche Richtung du fliegen möchtest und ob du dein Flugzeug (und damit deine Waffe) in die gleiche Richtung halten willst oder nicht. Die Kugel wird direkt nach der Bewegung Ihres Flugzeugs abgeschossen. Nach dem Schießen gibt es eine Abkühlung von einer Runde, in der dritten Runde kann es wieder losgehen. Sie können nur in die Richtung schießen, in die Sie fliegen. Eine Kugel ist sofort und fliegt in einer geraden Linie, bis sie auf eine Wand oder ein Flugzeug trifft.
In Anbetracht der Art und Weise, wie Sie die Richtung ändern sowie die Spur wechseln, bedeutet dies, dass Sie zusätzlich zu einigen diagonalen, einzelnen Zeilen eine Spalte mit bis zu 3 x 3 Zeilen vor sich bedrohen können.
Wenn es auf ein Flugzeug trifft, stirbt dieses Flugzeug und verschwindet sofort von der Tafel (weil es explodiert oder so). Kugeln können maximal ein Flugzeug treffen. Kugeln werden gleichzeitig abgefeuert, so dass zwei Flugzeuge aufeinander schießen können. Zwei Kugeln können jedoch nicht in der Luft kollidieren (traurig, ich weiß).
Zwei Ebenen können jedoch kollidieren (wenn sie im selben Würfel landen und NICHT, wenn sie sich kreuzen, ohne in derselben Ebene zu landen), und dies führt dazu, dass beide Ebenen sterben (und vollständig explodieren). Sie können auch in die Wand fliegen, was dazu führt, dass das betreffende Flugzeug stirbt und in die Ecke gestellt wird, um über seine Aktionen nachzudenken. Kollisionen werden vor dem Schießen behandelt.
Kommunikation mit der Steuerung
Ich akzeptiere Einträge in Java sowie in anderen Sprachen. Wenn Ihr Eintrag in Java ist, werden Sie über STDIN eingegeben und über STDOUT ausgegeben.
Wenn Ihr Eintrag in Java ist, muss Ihr Eintrag die folgende Klasse erweitern:
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();
}
Die von dieser Klasse erstellte Instanz bleibt während des gesamten Wettbewerbs bestehen, sodass Sie alle Daten, die Sie speichern möchten, in Variablen speichern können. Lesen Sie die Kommentare im Code, um weitere Informationen zu erhalten.
Ich habe Ihnen auch die folgenden Hilfsklassen zur Verfügung gestellt:
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) {}
}
Sie können Instanzen dieser Klassen erstellen und jede ihrer Funktionen so oft verwenden, wie Sie möchten. Den vollständigen Code für diese Hilfsklassen finden Sie hier .
Hier ist ein Beispiel, wie Ihr Eintrag aussehen könnte (Hoffentlich machen Sie es besser als ich, aber die meisten Spiele mit diesen Flugzeugen enden damit, dass sie gegen eine Mauer fliegen, obwohl sie sich nach besten Kräften bemühen, die Mauer zu umgehen.):
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 nimmt zusammen mit den anderen Teilnehmern am Turnier teil. Wenn Sie also als Letzter abschließen, liegt es an Ihnen, dass Sie nicht mindestens besser abschneiden als DumbPlanes.
Beschränkungen
Es gelten die im KOTH-Wiki genannten Einschränkungen :
- Jeder Versuch, mit dem Controller, der Laufzeit oder anderen Einsendungen zu basteln, wird disqualifiziert. Alle Einsendungen sollten nur mit den angegebenen Eingaben und Speichern funktionieren.
- Bots sollten nicht geschrieben werden, um bestimmte andere Bots zu schlagen oder zu unterstützen. (Dies mag in seltenen Fällen wünschenswert sein, aber wenn dies kein Kernkonzept der Herausforderung ist, ist dies besser ausgeschlossen.)
- Ich behalte mir das Recht vor, Einsendungen zu disqualifizieren, die zu viel Zeit oder Speicherplatz benötigen, um Tests mit einer angemessenen Menge an Ressourcen durchzuführen.
- Ein Bot darf nicht absichtlich oder versehentlich die exakt gleiche Strategie wie ein vorhandener implementieren.
Testen Sie Ihre Einreichung
Laden Sie den Controller-Code hier herunter . Fügen Sie Ihren Beitrag als Something.java hinzu. Ändern Sie Controller.java, um Einträge für Ihr Flugzeug in die Einträge [] und Namen [] aufzunehmen. Kompilieren Sie alles als Eclipse-Projekt oder mit javac -d . *.java
, und führen Sie dann den Controller mit aus java Planes/Controller
. Es wird ein Protokoll des Wettbewerbs test.txt
mit einer Anzeigetafel am Ende eingereicht. Sie können auch anrufenmatchUp()
direkt mit zwei Einträgen als Argumente um nur zwei Ebenen gegeneinander zu testen.
Den Kampf gewinnen
Der Gewinner des Kampfes ist derjenige, der das letzte fliegende Flugzeug hat. Wenn nach 100 Runden noch mehr als 1 Team übrig ist, gewinnt das Team mit den meisten verbleibenden Flugzeugen. Wenn dies gleich ist, ist es ein Unentschieden.
Wertung und die Konkurrenz
Das nächste offizielle Turnier findet statt, wenn das aktuelle Kopfgeld aufgebraucht ist.
Jeder Beitrag kämpft (mindestens) 100 Mal gegen jeden anderen Beitrag. Der Gewinner jedes Match-Ups ist derjenige mit den meisten Gewinnen unter den 100 und erhält 2 Punkte. Bei einem Unentschieden erhalten beide Einsendungen 1 Punkt.
Der Gewinner des Wettbewerbs ist der mit den meisten Punkten. Im Falle eines Unentschieden ist der Gewinner derjenige, der in einem Match zwischen den Einsendungen, die gezogen wurden, gewonnen hat.
Abhängig von der Anzahl der Einsendungen könnte sich die Anzahl der Kämpfe zwischen den Einsendungen erheblich erhöhen. Ich könnte auch die 2-4 besten Einsendungen nach dem ersten Turnier auswählen und ein Eliteturnier zwischen diesen Einsendungen mit mehr Kämpfen (und möglicherweise mehr Runden pro Einsendung) einrichten Kampf)
(vorläufiger) Anzeiger
Wir haben einen neuen Eintrag, der den zweiten Platz in einem weiteren aufregenden Turnier fest einnimmt. Es scheint, dass Crossfire unglaublich schwer für alle außer PredictAndAvoid zu schießen ist. Beachten Sie, dass dieses Turnier mit nur 10 Kämpfen zwischen den einzelnen Flugzeugen ausgetragen wurde und daher keine vollständig genaue Darstellung des Standes der Dinge ist.
----------------------------
¦ 1. PredictAndAvoid: 14 ¦
¦ 2. Crossfire: 11 ¦
¦ 3. Weeeeeeeeeeee: 9 ¦
¦ 4. Whirligig: 8 ¦
¦ 4. MoveAndShootPlane: 8 ¦
¦ 6. StarFox: 4 ¦
¦ 6. EmoFockeWulf: 2 ¦
¦ 7. DumbPlanes: 0 ¦
----------------------------
Hier ist ein Beispiel für die Ausgabe des Nicht-Java-Wrappers:
NEW CONTEST 14 20
Zeigt an, dass ein neuer Wettbewerb in einer 14x14x14 Arena startet und 20 Runden pro Kampf beinhaltet.
NEW OPPONENT 10
zeigt an, dass du einem neuen Gegner gegenüber stehst und diesen zehnmal bekämpfst
NEW FIGHT 5 3 2
zeigt an, dass ein neuer Kampf gegen den aktuellen Gegner beginnt, dass Sie diesen Gegner bisher 5 Mal bekämpft haben, 3 Kämpfe gewonnen und 2 Kämpfe verloren haben
ROUNDS LEFT 19
zeigt an, dass im aktuellen Kampf noch 19 Runden übrig sind
NEW TURN
zeigt an, dass Sie im Begriff sind, Daten für alle vier Flugzeuge für diese Kampfrunde zu erhalten
alive 13 8 13 N 0
alive 13 5 13 N 0
dead 0 0 0 N 0
alive 0 8 0 S 0
Diese vier Linien zeigen an, dass beide Ebenen an den Koordinaten [13,8,13] und [13,5,13] nach Norden zeigen und beide keine Abklingzeit haben. Das erste feindliche Flugzeug ist tot und das zweite ist am Leben, bei [0,8,0] und mit null Abklingzeit nach Süden ausgerichtet.
Zu diesem Zeitpunkt sollte Ihr Programm zwei Zeilen ähnlich der folgenden ausgeben:
NW 0 1
SU 1 0
Dies bedeutet, dass Ihr erstes Flugzeug nach Nordwesten fliegt, ohne sich von der aktuellen Flugrichtung abzuwenden, und wenn möglich schießt. Ihr zweites Flugzeug fliegt nach Süden, dreht sich nach Süden und schießt nicht.
Jetzt werden Sie ROUNDS LEFT 18
von NEW TURN
usw. verfolgt . Dies wird fortgesetzt, bis jemand gewinnt oder die Runde abgelaufen ist. An diesem Punkt erhalten Sie eine weitere NEW FIGHT
Zeile mit der aktualisierten Kampfanzahl und den Ergebnissen, möglicherweise mit einem vorangestellten NEW OPPONENT
.