AGGIORNAMENTO: isSuicidal () è stato aggiunto alla classe del piano, questo consente di verificare se un piano è in rotta di collisione irreversibile con le pareti !!
AGGIORNAMENTO: updateCoolDown () separato da simulateMove ()
AGGIORNAMENTO: wrapper di voce non Java, scritto da Sparr , disponibile per il test, vedere i commenti
AGGIORNAMENTO Zove Games ha scritto un fantastico visualizzatore 3D per questo KOTH, ecco un video di YouTube di merda di PredictAndAVoid che combatte contro PredictAndAVoid.
La funzione simulateMove () della classe Plane è stata leggermente modificata in modo da non aggiornare più il raffreddamento, utilizzare la nuova funzione updateCoolDown (), dopo lo scatto. Il nuovo isSuicidal () ritorna vero se un aereo è destinato a finire morto, usarlo per potare le mosse nemiche ed evitare di colpire i muri. Per ottenere il codice aggiornato, è sufficiente sostituire le classi Controller e Plane con quelle nel repository github.
Descrizione
L'obiettivo di questa sfida è quello di codificare due aerei da combattimento che affronteranno due aerei di un altro concorrente. Ad ogni turno muovi uno spazio e hai l'opportunità di sparare. Ecco fatto, è così semplice.
Be 'quasi...
Arena e possibili mosse
L'arena è un 14x14x14 recintato nello spazio. gli aerei del concorrente 1 iniziano nelle posizioni (0,5,0) e (0,8,0) e quelli del concorrente 2 in (13,5,13) e (13,8,13). Tutti gli aerei iniziano volando orizzontalmente lontano dalle pareti verticali a cui sono più vicini.
Ora dal momento che stai pilotando aerei e non elicotteri, non puoi semplicemente cambiare direzione a piacimento o addirittura smettere di muoverti, quindi ogni piano ha una direzione e muoverà una tessera in quella direzione ogni giro.
Le direzioni possibili sono: Nord (N), Sud (S), Est (E), Ovest (W), Su (U) e Giù (D) e qualsiasi combinazione logica di quei sei. Dove l'asse NS corrisponde all'asse x, WE a y e DU a z. NW, SU e NED vengono in mente come possibili esempi di direzioni; UD è un ottimo esempio di combinazione non valida.
Ovviamente puoi cambiare la direzione dei tuoi aerei, ma c'è una limitazione, puoi cambiare direzione solo al massimo di 45 gradi. Per visualizzarlo, prendi il cubo del tuo rubik (so che ne hai uno) e immagina che tutti i 26 cubetti esterni siano le direzioni possibili (le direzioni di una lettera sono facce, la direzione di due lettere sono i bordi e le direzioni di tre lettere sono gli angoli). Se stai andando in una direzione rappresentata da un piccolo cubo, puoi cambiare direzione per ogni cubo che tocca il tuo (toccando diagonalmente i conteggi, ma toccando solo visibilmente, che non sta toccando attraverso il cubo).
Dopo che tutti gli aerei hanno indicato in quale direzione vorrebbero cambiare, lo fanno e muovono contemporaneamente una tessera.
Puoi anche scegliere di muoverti in una direzione valida ma continuare a volare nella direzione in cui stavi andando, invece di cambiare la direzione nella direzione in cui ti sei spostato. Ciò è analogo alla differenza tra un'auto che gira dietro un angolo e un'auto che cambia corsia.
Sparare e morire
Puoi sparare al massimo una volta per round e questo deve essere deciso nello stesso momento in cui decidi in quale direzione volare e se vuoi mantenere il tuo aereo (e per estensione, la tua pistola) puntato nella stessa direzione o meno. Il proiettile viene sparato subito dopo che l'aereo si muove. Dopo il tiro c'è un raffreddamento di un turno, al terzo turno sei pronto per ripartire. Puoi sparare solo nella direzione in cui stai volando. Un proiettile è istantaneo e vola in linea retta fino a quando non colpisce un muro o un piano.
Tenendo conto del modo in cui è possibile cambiare direzione e "cambiare corsia", ciò significa che è possibile minacciare una colonna di un massimo di 3x3 linee davanti a te in aggiunta ad alcune linee diagonali singole.
Se colpisce un piano, questo piano muore e scompare prontamente dal tabellone (perché esplode totalmente o qualcosa del genere). I proiettili possono colpire al massimo un solo aereo. I proiettili vengono sparati contemporaneamente, quindi due aerei possono spararsi l'un l'altro. Tuttavia, due proiettili non possono scontrarsi in aria (triste, lo so).
Tuttavia, due piani possono scontrarsi (se finiscono nello stesso cubo e NON se si incrociano senza finire sullo stesso piano), e ciò provoca la morte di entrambi i piani (e l'esplosione totale). Puoi anche volare nel muro, il che causerà la morte dell'aereo in questione e l'essere messo in un angolo per pensare alle sue azioni. Le collisioni vengono gestite prima delle riprese.
Comunicazione con il controller
Accetterò iscrizioni in Java e in altre lingue. Se la tua voce è in java, otterrai l'input tramite STDIN e l'output tramite STDOUT.
Se la tua voce è in java, la tua voce deve estendere la seguente classe:
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'istanza creata di quella classe persisterà per tutta la competizione, quindi puoi archiviare tutti i dati che desideri memorizzare in variabili. Leggi i commenti nel codice per ulteriori informazioni.
Ti ho anche fornito le seguenti classi di supporto:
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) {}
}
Puoi creare istanze di queste classi e utilizzare una qualsiasi delle loro funzioni come preferisci. Puoi trovare il codice completo per queste classi di supporto qui .
Ecco un esempio di come potrebbe apparire la tua voce (Speriamo che tu faccia meglio di me, però, la maggior parte delle partite con questi aerei finiscono con loro che volano contro un muro, nonostante i loro migliori sforzi per evitare il muro.):
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 si unirà al torneo insieme alle altre voci, quindi se si finisce per ultimo, è colpa tua per non aver fatto almeno meglio di DumbPlanes.
restrizioni
Si applicano le restrizioni menzionate nella wiki di KOTH :
- Qualsiasi tentativo di armeggiare con il controller, il runtime o altri invii verrà squalificato. Tutti gli invii dovrebbero funzionare solo con gli input e lo spazio di archiviazione forniti.
- I bot non devono essere scritti per battere o supportare altri bot specifici. (Ciò potrebbe essere auspicabile in rari casi, ma se questo non è un concetto chiave della sfida, è meglio escluderlo.)
- Mi riservo il diritto di squalificare i contributi che utilizzano troppo tempo o memoria per eseguire prove con una quantità ragionevole di risorse.
- Un bot non deve implementare esattamente la stessa strategia esistente, intenzionalmente o accidentalmente.
Verifica la tua richiesta
Scarica il codice del controller da qui . Aggiungi la tua richiesta come Something.java. Modifica Controller.java per includere le voci per il tuo aereo nelle voci [] e nei nomi []. Compilare tutto come un progetto Eclipse o con javac -d . *.java
, quindi eseguire il controller con java Planes/Controller
. Verrà inserito un registro del concorso test.txt
, con un tabellone alla fine. Puoi anche chiamare matchUp()
direttamente con due voci come argomenti per testare solo due piani uno contro l'altro.
Vincere la lotta
Il vincitore del combattimento è colui che ha l'ultimo aereo in volo, se dopo 100 turni rimane ancora più di 1 squadra, vince la squadra con il maggior numero di aerei rimasti. Se questo è uguale, è un pareggio.
Punteggio e competizione
Il prossimo torneo ufficiale si svolgerà allo scadere dell'attuale taglia.
Ogni iscrizione combatterà ogni altra iscrizione (almeno) 100 volte, il vincitore di ogni incontro è quello con il maggior numero di vittorie su 100 e riceverà 2 punti. In caso di pareggio, ad entrambe le iscrizioni viene assegnato 1 punto.
Il vincitore della competizione è quello con più punti. In caso di pareggio, il vincitore è colui che ha vinto in una partita tra le iscrizioni che hanno pareggiato.
A seconda della quantità di voci, La quantità di combattimenti tra le voci potrebbe essere aumentata in modo significativo, potrei anche selezionare le 2-4 migliori voci dopo il primo torneo e impostare un torneo di élite tra quelle voci con più combattimenti (e forse più round per combattimento)
Quadro di valutazione (preliminare)
Abbiamo una nuova voce che ha fermamente preso il secondo posto in un altro emozionante torneo , sembra che Crossfire sia incredibilmente difficile da girare per tutti tranne che per PredictAndAvoid. Si noti che questo torneo è stato condotto con solo 10 combattimenti tra ogni serie di aerei e quindi non è una rappresentazione del tutto precisa di come stanno le cose.
----------------------------
¦ 1. PredictAndAvoid: 14 ¦
¦ 2. Crossfire: 11 ¦
¦ 3. Weeeeeeeeeeee: 9 ¦
¦ 4. Whirligig: 8 ¦
¦ 4. MoveAndShootPlane: 8 ¦
¦ 6. StarFox: 4 ¦
¦ 6. EmoFockeWulf: 2 ¦
¦ 7. DumbPlanes: 0 ¦
----------------------------
Ecco un esempio di output dal wrapper non Java:
NEW CONTEST 14 20
indica che sta iniziando un nuovo contest, in un'arena 14x14x14, e coinvolgerà 20 turni per combattimento.
NEW OPPONENT 10
indica che stai affrontando un nuovo avversario e che combatterai per 10 volte
NEW FIGHT 5 3 2
indica che sta iniziando un nuovo combattimento contro l'attuale avversario, che hai combattuto questo avversario 5 volte finora, vincendo 3 e perdendo 2 combattimenti
ROUNDS LEFT 19
indica che sono rimasti 19 round nel combattimento attuale
NEW TURN
indica che stai per ricevere i dati per tutti e quattro gli aerei per questo round di combattimento
alive 13 8 13 N 0
alive 13 5 13 N 0
dead 0 0 0 N 0
alive 0 8 0 S 0
Queste quattro linee indicano che entrambi i tuoi piani sono vivi, rispettivamente alle coordinate [13,8,13] e [13,5,13], entrambi rivolti a nord, entrambi con zero di ricarica. Il primo aereo nemico è morto e il secondo è vivo, a [0,8,0] e esposto a sud con zero ricarica.
A questo punto il tuo programma dovrebbe generare due righe simili alle seguenti:
NW 0 1
SU 1 0
Questo indica che il tuo primo aereo viaggerà verso Nord-Ovest, senza girare dalla sua rotta attuale e sparare se possibile. Il tuo secondo aereo viaggerà verso sud, girandosi verso sud, senza sparare.
Ora vieni ROUNDS LEFT 18
seguito da NEW TURN
ecc. Questo continua fino a quando qualcuno vince o il round termina, a quel punto ottieni un'altra NEW FIGHT
linea con il conteggio e i punteggi di combattimento aggiornati, possibilmente preceduti da un NEW OPPONENT
.