3D: Discrete Dogfighting Duel (Now open to non Java submissions)

/*
    PREDICT AND AVOID

    Rules of behavior:
    - Avoid hitting walls
    - Move, safely, to shoot at spaces our enemy might fly to
    - (contingent) Move to a safe space that aims closer to the enemy
    - Move to a safe space
    - Move, unsafely, to shoot at spaces our enemy might fly to
    - Move to any space (remember to avoid walls)

    Chooses randomly between equally prioritized moves

    contingent strategy is evaluated during early fights
*/

package Planes;

import java.util.Random;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;


public class PredictAndAvoid extends PlaneControl {

    public PredictAndAvoid(int arenaSize, int rounds) {
        super(arenaSize, rounds);
    }


    private int fightsPerMatch = 0;
    private int fightNum = 0;
    private int roundNum = 0;
    private boolean useHoming = true;
    private int homingScore = 0;
    private int[][][] enemyHistory = new int[arenaSize][arenaSize][arenaSize];

    // don't need to take roots here, waste of cpu cycles
    int distanceCubed(Point3D a, Point3D b) {
        return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) + (a.z-b.z)*(a.z-b.z);
    }

    // is this plane guaranteed to hit a wall, now or soon?
    boolean dangerZone(Plane icarus) {
        // outside the arena?
        // already dead
        // this should never happen for my planes
        if (!icarus.getPosition().isInArena(arenaSize)) {
            return true;
        }
        // adjacent to a wall?
        // directly facing the wall?
        // death next turn
        if (
            icarus.getDirection().getMainDirections().length==1 &&
            icarus.getDistanceFromWall(icarus.getDirection().getMainDirections()[0]) == 0
        ) {
                return true;
        }
        // on an edge?
        // 2d diagonal facing into that edge?
        // death next turn
        if (
            icarus.getDirection().getMainDirections().length==2 &&
            icarus.getDistanceFromWall(icarus.getDirection().getMainDirections()[0]) == 0 &&
            icarus.getDistanceFromWall(icarus.getDirection().getMainDirections()[1]) == 0
        ) {
                return true;
        }
        // near a corner?
        // 3d diagonal facing into that corner?
        // death in 1-2 turns
        if (
            icarus.getDirection().getMainDirections().length==3 &&
            icarus.getDistanceFromWall(icarus.getDirection().getMainDirections()[0]) < 2 &&
            icarus.getDistanceFromWall(icarus.getDirection().getMainDirections()[1]) < 2 &&
            icarus.getDistanceFromWall(icarus.getDirection().getMainDirections()[2]) < 2
        ) {
                return true;
        }
        // there's at least one way out of this position
        return false;
    }

    @Override
    public Move[] act() {
        Move[] moves = new Move[2];

        for (int i=0; i<2; i++) {
            Plane p = myPlanes[i];
            if (!p.isAlive()) {
                moves[i] = new Move(new Direction("N"), false, false); // If we're dead we just return something, it doesn't matter anyway.
                continue;
            }

            // a list of every move that doesn't commit us to running into a wall
            // or a collision with the previously moved friendly plane
            ArrayList<Move> potentialMoves = new ArrayList<Move>();
            for (Direction candidateDirection : p.getPossibleDirections()) {
                if (i==1 && myPlanes[0].simulateMove(moves[0]).getPosition().equals(myPlanes[1].simulateMove(new Move(candidateDirection,false,false)).getPosition())) {

                } else {                
                    Plane future = new Plane(arenaSize, 0, p.getDirection(), p.getPosition().add(candidateDirection.getAsPoint3D())); 
                    if (!dangerZone(future)) {
                                potentialMoves.add(new Move(candidateDirection, false, false));
                    }
                    future = new Plane(arenaSize, 0, candidateDirection, p.getPosition().add(candidateDirection.getAsPoint3D())); 
                    if (!dangerZone(future)) {
                            potentialMoves.add(new Move(candidateDirection, true, false));
                    }
                }
            }

            // everywhere our enemies might end up
            // including both directions they could be facing for each location
            ArrayList<Plane> futureEnemies = new ArrayList<Plane>();
            for (Plane e : enemyPlanes) {
                if (e.isAlive()) {
                    for (Direction candidateDirection : e.getPossibleDirections()) {
                        futureEnemies.add(new Plane(
                            arenaSize, 
                            e.getCoolDown(), 
                            candidateDirection, 
                            e.getPosition().add(candidateDirection.getAsPoint3D())
                            ));
                        // don't make a duplicate entry for forward moves
                        if (!candidateDirection.getAsPoint3D().equals(e.getDirection().getAsPoint3D())) {
                            futureEnemies.add(new Plane(
                                arenaSize, 
                                e.getCoolDown(), 
                                e.getDirection(), 
                                e.getPosition().add(candidateDirection.getAsPoint3D())
                                ));
                        }
                    }
                }
            }

            // a list of moves that are out of enemies' potential line of fire
            // also skipping potential collisions unless we are ahead on planes
            ArrayList<Move> safeMoves = new ArrayList<Move>();
            for (Move candidateMove : potentialMoves) {
                boolean safe = true;
                Point3D future = p.simulateMove(candidateMove).getPosition();
                for (Plane ec : futureEnemies) {
                    if (ec.getPosition().equals(future)) {
                        if (
                            (myPlanes[0].isAlive()?1:0) + (myPlanes[1].isAlive()?1:0)
                            <= 
                            (enemyPlanes[0].isAlive()?1:0) + (enemyPlanes[1].isAlive()?1:0)
                        ) {
                            safe = false;
                            break;
                        }
                    }
                    if (ec.isAlive() && ec.canShoot()) {
                        Point3D[] range = ec.getShootRange();
                        for (Point3D t : range) {
                            if (future.equals(t)) {
                                safe = false;
                                break;
                            }
                        }
                        if (safe == false) {
                            break;
                        }
                    }
                }
                if (safe == true) {
                    safeMoves.add(candidateMove);
                }
            }

            // a list of moves that let us attack a space an enemy might be in
            // ignore enemies committed to suicide vs a wall
            // TODO: don't shoot at friendly planes
            ArrayList<Move> attackMoves = new ArrayList<Move>();
            for (Move candidateMove : potentialMoves) {
                int attackCount = 0;
                Plane future = p.simulateMove(candidateMove);
                Point3D[] range = future.getShootRange();
                for (Plane ec : futureEnemies) {
                    for (Point3D t : range) {
                        if (ec.getPosition().equals(t)) {
                            if (!dangerZone(ec)) {
                                    attackMoves.add(new Move(candidateMove.direction, candidateMove.changeDirection, true));
                                    attackCount++;
                            }
                        }
                    }
                }
                if (attackCount > 0) {

                }
            }

            // find all attack moves that are also safe moves
            ArrayList<Move> safeAttackMoves = new ArrayList<Move>();
            for (Move safeCandidate : safeMoves) {
                for (Move attackCandidate : attackMoves) {
                    if (safeCandidate.direction == attackCandidate.direction) {
                        safeAttackMoves.add(attackCandidate);
                    }
                }
            }

            // choose the safe move that aims closest potential enemy positions
            int maxDistanceCubed = arenaSize*arenaSize*arenaSize*8;
            Move homingMove = null;
            int bestHomingMoveTotalDistancesCubed = maxDistanceCubed*1000;
            for (Move candidateMove : safeMoves) {
                int totalCandidateDistancesCubed = 0;
                for (Plane ec : futureEnemies) {
                    if (ec.isAlive()) {
                        int distThisEnemyCubed = maxDistanceCubed;
                        Point3D[] range = p.simulateMove(candidateMove).getShootRange();
                        for (Point3D t : range) {
                            int d1 = distanceCubed(t, ec.getPosition());
                            if (d1 < distThisEnemyCubed) {
                                distThisEnemyCubed = d1;
                            }
                        }
                        totalCandidateDistancesCubed += distThisEnemyCubed;
                    }
                }
                if (totalCandidateDistancesCubed < bestHomingMoveTotalDistancesCubed) {
                    bestHomingMoveTotalDistancesCubed = totalCandidateDistancesCubed;
                    homingMove = candidateMove;
                }
            }

            Random rng = new Random();
            // move to attack safely if possible
            // even if we can't shoot, this is good for chasing enemies
            if (safeAttackMoves.size() > 0) {
                moves[i] = safeAttackMoves.get(rng.nextInt(safeAttackMoves.size()));
                }
            // turn towards enemies if it's possible and safe
            // tests indicate value of this strategy varies significantly by opponent
            // useHoming changes based on outcome of early fights with[out] it
            // TODO: track enemy movement, aim for neighborhood
            else if (useHoming == true && homingMove != null) {
                moves[i] = homingMove;
                }
            // make random move, safe from attack
            else if (safeMoves.size() > 0) {
                moves[i] = safeMoves.get(rng.nextInt(safeMoves.size()));
                }
            // move to attack unsafely only if there are no safe moves
            else if (attackMoves.size() > 0 && p.canShoot()) {
                moves[i] = attackMoves.get(rng.nextInt(attackMoves.size()));
                }
            // make random move, safe from walls
            else if (potentialMoves.size() > 0) {
                moves[i] = potentialMoves.get(rng.nextInt(potentialMoves.size()));
                }
            // keep moving forward
            // this should never happen
            else {
                moves[i] = new Move(p.getDirection(), false, true);
                }
        }
        roundNum++;
        return moves;
    }

    @Override
    public void newFight(int fightsFought, int myScore, int enemyScore) {
        // try the homing strategy for 1/8 of the match
        // skip it for 1/8, then choose the winning option
        if (fightsFought == fightsPerMatch/8) {
            homingScore = myScore-enemyScore;
            useHoming = false;
        } else if (fightsFought == (fightsPerMatch/8)*2) {
            if (homingScore*2 > myScore-enemyScore) {
                useHoming = true;
            }
        }
        fightNum = fightsFought;
        roundNum = 0;
    }

    @Override
    public void newOpponent(int fights) {
        fightsPerMatch = fights;
    }
}

Dogfight 3D Visualizer

I wrote a small, quick visualizer for this challenge. Code and jar files are on my github repo: https://github.com/Hungary-Dude/DogfightVisualizer
It's made using libGDX (http://libgdx.com). Right now the UI is pretty bad, I did put this together kind of fast.

I'm just learning how to use Git and Gradle so please comment if I did something wrong

Run dist/dogfight.bat or dist/dogfight.sh to see DumbPlanes in action!

To build from source, you'll need Gradle (http://gradle.org) and Gradle integration for your IDE, if you have one. Then clone the repo and run gradlew desktop:run. Hopefully Gradle will import all the libraries required. The main class is zove.koth.dogfight.desktop.DesktopLauncher.

Running without importing

Copy any plane class files into dist/. Then, run dist/desktop-1.0.jar with this command:

java -cp your-class-folder/;desktop-1.0.jar;Planes.jar zove.koth.dogfight.desktop.DesktopLauncher package.YourPlaneController1 package.YourPlaneController2 ...

I will update as the source for the Planes controller is updated, but to update yourself, you'll need to add in some code to Planes.Controller. See the github readme for info on this.

Here's a screenshot: Screenshot

If you have any questions or suggestions, leave a comment below!


EmoFockeWulf

He's back. He's starved himself to 224 bytes. He doesn't know how he ended up like this.

package Planes;public class EmoFockeWulf extends PlaneControl{public EmoFockeWulf(int s, int r){super(s,r);}public Move[] act(){Move[] m=new Move[2];m[0]=new Move(myPlanes[0].getDirection(),false,false);m[1]=m[0];return m;}}