UPDATE: isSuicidal () foi adicionado à classe de avião, permitindo verificar se um avião está em rota de colisão irreversível com as paredes !!
UPDATE: updateCoolDown () separado de simulateMove ()
UPDATE: invólucro de entrada não Java, escrito por Sparr , disponível para teste, ver comentários
ATUALIZAR A Zove Games Escreveu um incrível visualizador em 3D para este KOTH, aqui está um vídeo de merda do YouTube do PredictAndAVoid lutando contra o PredictAndAVoid.
A função simulateMove () da classe Plane foi ligeiramente modificada para que não atualize mais o resfriamento, use a nova função updateCoolDown () para isso, após o disparo. O novo isSuicidal () retorna true se um avião acabar morto, use-o para podar movimentos inimigos e evitar bater nas paredes. Para obter o código atualizado, basta substituir as classes Controller e Plane por aquelas no repositório do github.
Descrição
O objetivo deste desafio é codificar dois aviões de combate a cães que enfrentarão dois aviões por outro competidor. A cada turno você move um espaço e tem a oportunidade de atirar. É isso, é tão simples quanto isso.
Bem, quase...
Arena e movimentos possíveis
A arena é uma parede 14x14x14 no espaço. os aviões do competidor 1 iniciam nos locais (0,5,0) e (0,8,0) e os do competidor 2 em (13,5,13) e (13,8,13). Todos os aviões começam voando horizontalmente para longe das paredes verticais mais próximas.
Agora, como você está pilotando aviões e não helicópteros, você não pode simplesmente mudar de direção à vontade ou até parar de se mover; portanto, cada avião tem uma direção e moverá um ladrilho nessa direção a cada turno.
As direções possíveis são: Norte (N), Sul (S), Leste (E), Oeste (W), Acima (U) e Abaixo (D) e qualquer combinação lógica dessas seis. Onde o eixo NS corresponde ao eixo x, WE para ye DU para z. NW, SU e NED vêm à mente como possíveis exemplos de orientações; UD é um ótimo exemplo de uma combinação inválida.
É claro que você pode mudar a direção de seus aviões, mas há uma limitação: você só pode mudar de direção em no máximo 45 graus. Para visualizar isso, pegue o cubo de rubik (eu sei que você tem um) e imagine que todos os 26 cubos exteriores são as direções possíveis (direções de uma letra são faces, direção de duas letras são arestas e três direções de letras são cantos). Se você estiver indo em uma direção representada por um pequeno cubo, poderá mudar de direção para cada cubo que tocar o seu (tocar na diagonal conta, mas apenas tocando visivelmente, que não está tocando no cubo).
Depois que todos os planos indicaram para qual direção eles gostariam de mudar, eles o fazem e movem um bloco simultaneamente.
Você também pode optar por se mover em uma direção válida, mas continuar voando na direção em que estava indo, em vez de mudar sua direção na direção em que se mudou. Isso é análogo à diferença entre um carro dobrando uma esquina e um carro que troca de faixa.
Atirando e morrendo
Você pode atirar no máximo uma vez por rodada e isso deve ser decidido ao mesmo tempo em que você decide em qual direção voar e se deseja manter seu avião (e, por extensão, sua arma) apontado na mesma direção ou não. A bala é atingida logo após o seu avião se mover. Há um resfriamento de um turno após as filmagens; no terceiro turno, você pode ir novamente. Você só pode disparar na direção em que está voando. Uma bala é instantânea e voa em linha reta até atingir uma parede ou um avião.
Levando em conta a maneira como você pode mudar de direção e 'mudar de faixa', isso significa que você pode ameaçar uma coluna de até 3x3 linhas à sua frente, além de algumas linhas diagonais e únicas.
Se atingir um avião, esse avião morre e desaparece imediatamente do tabuleiro (porque explode totalmente ou algo assim). As balas podem atingir apenas um avião no máximo. As balas são disparadas simultaneamente, para que dois aviões possam disparar entre si. Duas balas não podem colidir no ar (triste, eu sei).
Dois aviões podem colidir no entanto (se eles acabarem no mesmo cubo e NÃO se eles se cruzarem sem terminar no mesmo plano), e isso resulta em ambos os planos morrendo (e explodindo totalmente). Você também pode voar contra a parede, o que fará com que o avião em questão morra e seja colocado no canto para pensar em suas ações. As colisões são tratadas antes do disparo.
Comunicação com o controlador
Aceito entradas em java, bem como em outros idiomas. Se sua entrada estiver em java, você obterá a entrada através do STDIN e a saída através do STDOUT.
Se sua entrada estiver em java, sua entrada deverá estender a seguinte 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();
}
A instância criada para essa classe persistirá durante toda a competição, para que você possa armazenar quaisquer dados que deseje armazenar em variáveis. Leia os comentários no código para obter mais informações.
Também forneci as seguintes classes auxiliares:
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) {}
}
Você pode criar instâncias dessas classes e usar qualquer uma de suas funções o quanto quiser. Você pode encontrar o código completo para essas classes auxiliares aqui .
Aqui está um exemplo de como sua entrada poderia ser (espero que você faça melhor do que eu, a maioria das partidas com esses aviões termina com eles voando contra uma parede, apesar de seus melhores esforços para evitar a parede.):
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?
}
}
O DumbPlanes participará do torneio juntamente com as outras entradas. Portanto, se você terminar por último, a culpa é sua por não se sair melhor do que o DumbPlanes.
Restrições
As restrições mencionadas no wiki KOTH se aplicam:
- Qualquer tentativa de mexer no controlador, no tempo de execução ou em outros envios será desqualificada. Todos os envios devem funcionar apenas com as entradas e armazenamento fornecidos.
- Os bots não devem ser escritos para vencer ou suportar outros bots específicos. (Isso pode ser desejável em casos raros, mas se esse não for um conceito central do desafio, é melhor descartá-lo.)
- Eu me reservo o direito de desqualificar envios que usam muito tempo ou memória para executar avaliações com uma quantidade razoável de recursos.
- Um bot não deve implementar exatamente a mesma estratégia que uma existente, intencional ou acidentalmente.
Testando seu envio
Faça o download do código do controlador aqui . Adicione seu envio como Something.java. Modifique Controller.java para incluir entradas para o seu avião nas entradas [] e nomes []. Compile tudo como um projeto Eclipse ou com e javac -d . *.java
, em seguida, execute o Controller com java Planes/Controller
. Um registro do concurso será publicado test.txt
, com um placar no final. Você também pode chamar matchUp()
diretamente com duas entradas como argumentos para testar apenas dois planos um contra o outro.
Vencendo a luta
O vencedor da luta é aquele que tem o último avião voando, se após 100 turnos ainda restar mais de 1 time, o time com mais aviões restantes vence. Se isso é igual, é um empate.
Pontuação e competição
O próximo torneio oficial será realizado quando a recompensa atual acabar.
Cada inscrição disputará todas as outras entradas (pelo menos) 100 vezes, o vencedor de cada partida será o que tiver mais vitórias dentre as 100 e receberá 2 pontos. Em caso de empate, ambas as entradas recebem 1 ponto.
O vencedor da competição é aquele com mais pontos. Em caso de empate, o vencedor é aquele que venceu a partida entre as entradas sorteadas.
Dependendo da quantidade de entradas, a quantidade de lutas entre as entradas pode ser aumentada significativamente. Também posso selecionar as 2-4 melhores entradas após o primeiro torneio e criar um torneio de elites entre as entradas com mais lutas (e possivelmente mais rodadas por luta)
(preliminar) Painel de avaliação
Temos uma nova entrada que firmemente ocupa o segundo lugar em mais um torneio emocionante , parece que o Crossfire é incrivelmente difícil de acertar para todos, exceto para o PredictAndAvoid. Observe que este torneio foi realizado com apenas 10 lutas entre cada conjunto de aviões e, portanto, não é uma representação totalmente precisa de como as coisas estão.
----------------------------
¦ 1. PredictAndAvoid: 14 ¦
¦ 2. Crossfire: 11 ¦
¦ 3. Weeeeeeeeeeee: 9 ¦
¦ 4. Whirligig: 8 ¦
¦ 4. MoveAndShootPlane: 8 ¦
¦ 6. StarFox: 4 ¦
¦ 6. EmoFockeWulf: 2 ¦
¦ 7. DumbPlanes: 0 ¦
----------------------------
Aqui está um exemplo de saída do wrapper não Java:
NEW CONTEST 14 20
indica que um novo concurso está começando, em uma arena de 14x14x14, e envolverá 20 turnos por luta.
NEW OPPONENT 10
indica que você está enfrentando um novo oponente e que lutará contra ele 10 vezes
NEW FIGHT 5 3 2
indica que uma nova luta contra o atual oponente está começando, que você já enfrentou esse oponente 5 vezes até agora, vencendo 3 e perdendo 2 lutas
ROUNDS LEFT 19
indica que há 19 rodadas restantes na luta atual
NEW TURN
indica que você está prestes a receber dados dos quatro aviões para esta rodada da luta
alive 13 8 13 N 0
alive 13 5 13 N 0
dead 0 0 0 N 0
alive 0 8 0 S 0
Essas quatro linhas indicam que ambos os seus aviões estão vivos, nas coordenadas [13,8,13] e [13,5,13] respectivamente, ambos voltados para o norte, ambos com zero de recarga. O primeiro avião inimigo está morto, e o segundo está vivo, a [0,8,0] e voltado para o sul com zero de recarga.
Nesse ponto, seu programa deve gerar duas linhas semelhantes à seguinte:
NW 0 1
SU 1 0
Isso indica que seu primeiro avião viajará para o noroeste, sem sair do rumo atual e disparar, se possível. Seu segundo avião viajará para o sul, virando-se para o sul, e não atirando.
Agora você é ROUNDS LEFT 18
seguido por NEW TURN
etc. Isso continua até que alguém vença ou o tempo limite da rodada, quando você obtém outra NEW FIGHT
linha com a contagem e as pontuações atualizadas, possivelmente precedidas por a NEW OPPONENT
.
fonte
Respostas:
Fogo cruzado
Minha idéia inicial era atirar em um avião inimigo com os meus dois aviões ao mesmo tempo, mas não consegui resolver ... Então, aqui está um avião que tenta ficar longe das paredes e fora do campo de tiro da aeronave. inimigo. Os aviões nunca devem colidir nem atirar em aviões amigáveis.
Edit: o método
possibleHits
sempre retornava 0, após corrigi-lo e adicionar várias pequenas melhorias, ele tem um desempenho melhor do que antes.fonte
fonte
Dogfight 3D Visualizer
Eu escrevi um visualizador pequeno e rápido para esse desafio. Os arquivos de código e jar estão no meu repositório do github: https://github.com/Hungary-Dude/DogfightVisualizer
Ele foi criado usando o libGDX ( http://libgdx.com ). No momento, a interface do usuário está muito ruim, eu montei isso rápido.
Estou apenas aprendendo a usar Git e Gradle, por favor, comente se fiz algo errado
Corra
dist/dogfight.bat
oudist/dogfight.sh
veja o DumbPlanes em ação!Para criar a partir do código-fonte, você precisará da integração Gradle ( http://gradle.org ) e Gradle para o seu IDE, se você tiver um. Em seguida, clone o repositório e execute
gradlew desktop:run
. Espero que o Gradle importe todas as bibliotecas necessárias. A classe principal ézove.koth.dogfight.desktop.DesktopLauncher
.Executando sem Importar
Copie qualquer arquivo de classe de plano para
dist/
. Em seguida, executedist/desktop-1.0.jar
com este comando:Eu atualizarei à medida que a fonte do controlador Planes for atualizada, mas para atualizar você mesmo, será necessário adicionar algum código a
Planes.Controller
. Veja o leiame do github para obter informações sobre isso.Aqui está uma captura de tela:
Se você tiver alguma dúvida ou sugestão, deixe um comentário abaixo!
fonte
plane.transform.setToTranslation(new Vector3(point3d.x-6.5f,point3d.y-6.5f,point3d.z-6.5f))
Não há aviões parecem ir fora dos limites por isso duvido que algo está erradoEmoFockeWulf
Ele voltou. Ele passou fome a 224 bytes. Ele não sabe como acabou assim.
fonte
Weeeeeeeeeeee - 344 bytes após remover o espaço em branco
Faz loops incríveis n coisas. Não pode perder se você estiver fazendo loops.
Edição: aparentemente, quando o meu avião começou como equipe 2, eles caíram imediatamente contra a parede. Eu acho que consertei isso agora. Esperançosamente.
fonte
new Type[]{item1, item2, ...}
portanto, neste caso você teriareturn new Move[]{new Move(d,z,a),new Move(d,z,a^=z)};
Avião Mover e Atirar
Evita paredes, encontrando quando está perto de uma parede e girando, atira quando pode.
Isenção de responsabilidade: Eu não sou um programador Java, por isso, se eu estraguei tudo, conserte-o!
fonte
you may only change your angle by 45 degrees
.new Direction(wayToGo.get(currentDirection))
não funcionará, pois esquece de transmitir para String. wayToGo.put após o campo também não é válido, coloque-o em um bloco {wayToGo.put (blá); blá;} ou no construtor.Whirligig
Ambos os aviões se dirigem para o centro (ish) e, em seguida, fazem um loop enquanto disparam o mais rápido possível. Um dos três eixos é escolhido por luta, e o par sempre gira em torno do mesmo eixo em direções opostas.
fonte
DumbPlanes
Os DumbPlanes tentam arduamente não voar contra as paredes, mas não são muito espertos e geralmente acabam atingindo as paredes de qualquer maneira. Eles também atiram ocasionalmente, se soubessem o que estão atirando.
fonte
Starfox (WIP - ainda não está funcionando):
Na verdade, ele não utiliza todos os movimentos disponíveis. Mas ele tenta derrubar inimigos e não colidir com paredes.
fonte
DangerZoner
Escrito em Python e fazendo interface com o invólucro de código não Java criado por Sparr.
Faz toda a sua matemática em Python puro e é completamente não otimizado. Um pouco lento.
Altamente configurável e extensível.
Faz muito bem contra envios anteriores. Ganha lutas 2: 1 por todos que perderem contra
Crossfire
ouPredictAndAvoid
e vence 98 +% de todas as lutas contra outros competidores.Inclui sua própria ferramenta de visualização opcional:
Fighting
Crossfire
/PredictAndAvoid
, com as classificações de zona de perigo de mesmo nome visualizadas no volume circundante:Visualizado usando o
nipy_spectral
mapa de cores dematplotlib
. As coordenadas mais perigosas são renderizadas usando cores mais próximas do vermelho / branco no espectro eletromagnético e são desenhadas com pontos maiores.Perigo: Azul <Verde <Amarelo <Vermelho <Cinza Claro
Atuação:
1000 rodadas com os oito principais algoritmos da tabela de classificação:
Código:
fonte