public class Asteroid extends AbstractObject {
	public static final int MIN_ASTEROID_RADIUS = 5;
	public static final int MAX_ASTEROID_RADIUS = 15;
	public static final int MIN_ASTEROID_MASS = 2000;
	
	private double fuelProportion, waterProportion, metalsProportion;
	
    /**
     * Is the asteroid mineable?
     */
    boolean isMineable;
    
    /**
     * Is the asteroid gameable?
     */
    boolean isGameable;
    
    /**
     * Create an asteroid with proportions of resources (from config file)
     * 
     * @param location
     * @param mineable
     * @param gameable
     * @param radius
     * @param moveable
     * @param fuel
     * @param water
     * @param metals
     */
    public Asteroid(Position location, boolean mineable, boolean gameable, int radius, boolean moveable, double fuel, double water, double metals) {
		super(MIN_ASTEROID_MASS, radius, location);
		
		setDrawable(true);
		setAlive(true);
		isMineable = mineable;
		isGameable = gameable;
		graphic = new AsteroidGraphics(this);
		this.isMoveable = moveable;
		this.fuelProportion = fuel;
		this.waterProportion = water;
		this.metalsProportion = metals;
		
		// asteroids do not respawn
		super.setRespawn(false);
		
		if (isMineable) {
			resetResources();
		} 
		
		// reset the mass based on the created resources
		super.setMass(MIN_ASTEROID_MASS + resources.getMass());
	}

    /**
     * Create an asteroid with an initial resource pile (from a dead ship)
     * 
     * @param location
     * @param mineable
     * @param gameable
     * @param radius
     * @param moveable
     * @param initialResources
     */
    public Asteroid(Position location, boolean mineable, boolean gameable, int radius, boolean moveable, ResourcePile initialResources) {
		super(MIN_ASTEROID_MASS, radius, location);
		
		setDrawable(true);
		setAlive(true);
		isMineable = mineable;
		isGameable = gameable;
		graphic = new AsteroidGraphics(this);
		this.isMoveable = moveable;
		
    	resources.setResources(ResourceTypes.FUEL, initialResources.getResourceQuantity(ResourceTypes.FUEL));
    	resources.setResources(ResourceTypes.WATER, initialResources.getResourceQuantity(ResourceTypes.WATER));
    	resources.setResources(ResourceTypes.METALS, initialResources.getResourceQuantity(ResourceTypes.METALS));
		
    	double normalize = initialResources.getResourceQuantity(ResourceTypes.FUEL) + 
    			initialResources.getResourceQuantity(ResourceTypes.WATER) + initialResources.getResourceQuantity(ResourceTypes.METALS);
		this.fuelProportion = initialResources.getResourceQuantity(ResourceTypes.FUEL) / normalize;
		this.waterProportion = initialResources.getResourceQuantity(ResourceTypes.WATER) / normalize;
		this.metalsProportion = initialResources.getResourceQuantity(ResourceTypes.METALS) / normalize;

		// asteroids do not respawn
		super.setRespawn(false);
		
		// reset the mass based on the created resources
		super.setMass(MIN_ASTEROID_MASS + resources.getMass());
	}
    
    
    /**
     * Make a copy for security
     */
    public Asteroid deepClone() {
    	Asteroid newAsteroid = new Asteroid(getPosition().deepCopy(), isMineable, isGameable, radius, isMoveable, 
    			fuelProportion, waterProportion, metalsProportion);
    	newAsteroid.setAlive(isAlive);
    	newAsteroid.id = id;
    	return newAsteroid;
    }
    
    
    /**
     * Sets the resource value based on the radius
     */
    public void resetResources() {
    	resources.setResources(ResourceTypes.FUEL, ResourceFactory.getResourceQuantity(ResourceTypes.FUEL, radius));
    	resources.setResources(ResourceTypes.WATER, ResourceFactory.getResourceQuantity(ResourceTypes.WATER, radius));
    	resources.setResources(ResourceTypes.METALS, ResourceFactory.getResourceQuantity(ResourceTypes.METALS, radius));
    }
    
	/**
	 * @return the isMineable
	 */
	public boolean isMineable() {
		return isMineable;
	}

	/**
	 * @return the isGameable
	 */
	public boolean isGameable() {
		return isGameable;
	}
	
	/**
	 * Get the fuel proportion (used for graphics but maybe useful in other ways)
	 * @return the proportion of the asteroid dedicated to fuel
	 */
	public double getFuelProportion() {
		return fuelProportion;
	}

	/**
	 * Get the water proportion (used for graphics but maybe useful in other ways)
	 * @return the proportion of the asteroid dedicated to water
	 */
	public double getWaterProportion() {
		return waterProportion;
	}

	/**
	 * Get the metals proportion (used for graphics but maybe useful in other ways)
	 * @return the proportion of the asteroid dedicated to metals
	 */
	public double getMetalsProportion() {
		return metalsProportion;
	}
	
	/**
	 * Set an asteroid to be mineable or not (used with resource dropping with ships dying)
	 * 
	 * @param newMineable
	 */
	public void setMineable(boolean newMineable) {
		this.isMineable = newMineable;
	}

	public String toString() {
		String str = "Asteroid id " + super.id + " mass " + mass + " resources " + resources;
		return str;
		
	}
	
} public class Base extends AbstractActionableObject {
    public static final int BASE_RADIUS = 10;
    public static final int BASE_MASS = 1000;
    public static final int INITIAL_BASE_ENERGY = 5000;
    public static final int INITIAL_ENERGY_HEALING_INCREMENT = 1;
    
    
	/**
	 * The color of this team
	 */
	Color teamColor;

	/**
	 * The team that owns this base
	 */
	Team team;
	
	/**
	 * true if this is a home base for a team (which therefore can't be killed) or false if it is a supplementary
	 * base.
	 */
	boolean isHomeBase;
	
	/**
	 * How many units of energy heal at each time step (can be changed with power ups)
	 */
	int healingIncrement;
	
	
	public Base(Position location, String teamName, Team team, boolean isHomeBase) {
		super(BASE_MASS, BASE_RADIUS, location);
		this.teamName = teamName;
		teamColor = team.getTeamColor();
		graphic = new BaseGraphics(this, teamColor);
		energy = INITIAL_BASE_ENERGY;
		setAlive(true);
		setDrawable(true);
		this.isMoveable = false;
		this.team = team;
		this.isHomeBase = isHomeBase;
		this.maxEnergy = INITIAL_BASE_ENERGY;
		healingIncrement = INITIAL_ENERGY_HEALING_INCREMENT;
		resources = new ResourcePile();
		killTagTeam = null;
		assistTagTeam = null;
		healthAtKillTag = 0;
		healthAtAssistTag = 0;
	}

	/**
	 * Makes a deep copy (for security)
	 */
	public Base deepClone() {
		Base newBase = new Base(getPosition().deepCopy(), teamName, team.deepCopy(), isHomeBase);
		
		newBase.energy = energy;
		newBase.setAlive(isAlive);
		newBase.id = id;
		newBase.maxEnergy = maxEnergy;
		newBase.currentPowerups = new LinkedHashSet<SpaceSettlersPowerupEnum>(currentPowerups);
		newBase.weaponCapacity = weaponCapacity;
		newBase.healingIncrement = healingIncrement;
		newBase.resources = new ResourcePile();
		newBase.resources.add(resources);
		newBase.numFlags = numFlags;
		newBase.isShielded = isShielded;
		newBase.numCores = numCores;
		
		if (this.killTagTeam != null) {
			newBase.killTagTeam = killTagTeam.deepClone();
		} else {
			newBase.killTagTeam = null;
		}

		if (this.assistTagTeam != null) {
			newBase.assistTagTeam = assistTagTeam.deepClone();
		} else {
			newBase.assistTagTeam = null;
		}
		newBase.healthAtAssistTag = healthAtAssistTag;
		newBase.healthAtKillTag = healthAtKillTag;
		
		return newBase;
	}

	/**
	 * Get the team
	 * @return
	 */
	public Team getTeam() {
		return team;
	}
	
	/**
	 * Increments the number of cores collected by this base by the number provided.
	 * @param numCores
	 */
	public void incrementCores(int numCores) { 
		super.incrementCores(numCores);
		team.incrementCoresCollected(numCores);
	}
	
	/**
	 * Return half of the base's energy
	 * @return the healingEnergy
	 */
	public int getHealingEnergy() {
		return (energy / 2);
	}
	
	/**
	 * Returns true if this is a home base for a team and false if it is a secondary base
	 * @return
	 */
	public boolean isHomeBase() {
		return isHomeBase;
	}
	
	/**
	 * Get the speed at which this base heals (per time step)
	 * @return
	 */
	public int getHealingIncrement() {
		return healingIncrement;
	}

	/**
	 * Set the speed at which this base heals (done in the power up)
	 * @param healingIncrement
	 */
	public void setHealingIncrement(int healingIncrement) {
		this.healingIncrement = healingIncrement;
	}

	/**
	 * Change the resourcesAvailable amount by the specified amount.  Resources live
	 * at a base but are available to the whole team once they arrive at a base.  
	 * Method is overridden to add things to the team also.
	 * 
	 * @param difference
	 */
	public void addResources(ResourcePile newResources) {
		super.addResources(newResources);
		
		// and increment the resources for the entire team
		team.incrementTotalResources(newResources);
		team.incrementAvailableResources(newResources);
	}
	
	/**
	 * Add a flag to this team.  The flag itself is then killed so it can regenerate.
	 * 
	 * @param flag
	 */
	public void addFlag(Flag flag) {
		flag.depositFlag();
		super.incrementFlags();
		team.incrementTotalFlagsCollected();
	}
	

	/**
	 * Change the healing energy (from a collision or just time healing it)
	 * @param difference
	 */
	public void updateEnergy(int difference) {
		energy += difference;
		
		if (energy < 0) {
			energy = 0;
			if (!isHomeBase) {
				setAlive(false);
			}
		}
		
		if (energy > maxEnergy) {
			energy = maxEnergy;
		}
	}

	public String toString() {
		String str = "Base id " + super.id + " team " + super.teamName + " at " + position + " resources " + resources;
		return str;
	}

} public class Drone extends AbstractActionableObject {
	public static final int DRONE_RADIUS = 8;
	public static final int DRONE_MASS = 20;
	public static final int DRONE_INITIAL_ENERGY = 500;
	public static final int DRONE_MAX_ENERGY = 500;

	/**
	 * The action the drone is currently executing
	 */
	AbstractAction currentAction;

	/**
	 * The color for this team
	 */
	Color teamColor;
	
//	/**
//	 * The team that owns this drone
//	 */
//	Team team;
	
	/**
	 * Does the drone currently have a flag?
	 */
	boolean carryingFlag;
	
	/**
	 * Reference to the flag the drone has (if it has one)
	 */
	Flag flag;

	/**
	 * Reference to the team for this Drone
	 */
	Team team;

	
	
	/**
	 * Make a new drone for the specified team.  
	 * @param teamName
	 */
	public Drone(String teamName, Color teamColor, Team team, Position location, ResourcePile resources) {
		super(DRONE_MASS, DRONE_RADIUS, location);
		this.position = location;
		this.team = team;
		setDrawable(true);
		setAlive(true);
		this.teamName = teamName;
		graphic = new DroneGraphics(this, teamColor);
		this.isControllable = true;
		this.isMoveable = true;
		energy = DRONE_INITIAL_ENERGY;
		this.resources = new ResourcePile(resources); //herr0861 edit
		this.teamColor = teamColor;
		maxEnergy = DRONE_MAX_ENERGY;
		this.carryingFlag = false;
		this.flag = null;
		this.numFlags = 0;
		this.currentAction = null;
		killTagTeam = null;
		assistTagTeam = null;
		healthAtKillTag = 0;
		healthAtAssistTag = 0;
	}

	/**
	 * Deep copy of a drone (used for security)
	 * @return
	 */
	public Drone deepClone() {
		Drone newDrone = new Drone(teamName, teamColor, team, getPosition().deepCopy(), resources);

		newDrone.setAlive(isAlive);
		newDrone.resources = new ResourcePile();
		newDrone.addResources(resources);
		newDrone.energy = energy;
		newDrone.graphic = new DroneGraphics(newDrone, teamColor);
		newDrone.currentAction = currentAction;
		newDrone.id = id;
		newDrone.maxEnergy = maxEnergy;
		newDrone.weaponCapacity = weaponCapacity;
		newDrone.hitsInflicted = hitsInflicted;
		newDrone.killsInflicted = killsInflicted;
		newDrone.killsReceived = killsReceived;
		newDrone.damageInflicted = damageInflicted;
		newDrone.carryingFlag = carryingFlag;
		newDrone.numFlags = numFlags;
		newDrone.isShielded = isShielded;
		
		if (this.killTagTeam != null) {
			newDrone.killTagTeam = killTagTeam.deepClone();
		} else {
			newDrone.killTagTeam = null;
		}

		if (this.assistTagTeam != null) {
			newDrone.assistTagTeam = assistTagTeam.deepClone();
		} else {
			newDrone.assistTagTeam = null;
		}
		newDrone.healthAtAssistTag = healthAtAssistTag;
		newDrone.healthAtKillTag = healthAtKillTag;
		if (this.flag != null){
			newDrone.flag = flag.deepClone();
		}
		return newDrone;
	}


	/**
	 * Resets the drone's energy to the initial level
	 */
	public void resetEnergy() {
		energy = DRONE_INITIAL_ENERGY;
	}

	/**
	 * Is the drone carrying a flag right now?  True if so.  False otherwise.
	 * 
	 * @return
	 */
	public boolean isCarryingFlag() {
		return carryingFlag;
	}
	
	/**
	 * Get the flag (this is a deep clone from the client point of view) of the flag the drone is carrying
	 * @return
	 */
	public Flag getFlag() {
		return flag;
	}

	/**
	 * Add the flag to the drone's inventory
	 * 
	 * @param flag
	 */
	public void addFlag(Flag flag) {
		this.flag = flag;
		this.carryingFlag = true;
		this.incrementFlags();
	}
	
	/**
	 * Get the color of this team
	 * @return
	 */
	public Color getTeamColor() {
		return teamColor;
	}

	/**
	 * Get the team
	 * @return
	 */
	public Team getTeam() {
		return team;
	}

	/**
	 * Set the drone to dead and properly handle dropping the flag
	 * 
	 * @param rand
	 * @param space
	 */
	public void setDeadAndDropObjects(Random rand, Toroidal2DPhysics space) {
		resetResources();
		resetPowerups();
		resetAiCores(); 
		if (carryingFlag) {
			dropFlag(rand, space);
		}
		super.setAlive(false);

	}
	
	
	/**
	 * Drones need to behave slightly differently when set to dead or respawned 
	 * so this is an override of the abstract class.
	 */
	public void setAlive(boolean value) {
		if (value == false) {
			resetResources();
			resetPowerups();
			resetAiCores(); 

		} else {
			resetEnergy();
		}

		super.setAlive(value);
	}
	
	/**
	 * Drop all the cores by resetting to 0
	 * Will also need code inside physics sim to drop all AiCores
	 */
	public void resetAiCores() {
		//Just erase the core count, as we are not currently tracking the specific cores held by a ship.
		numCores = 0;
		/*
		 * From Josiah: In the future the drone may drop any AiCores it is holding, however I don't currently
		 * have a good strategy for releasing the cores of a craft holding a very large number of cores.
		 * For example, if a drone is holding 8 cores and dies, spawning those cores leads to a bunch of
		 * interesting collisions and several of the cores tend to end up destroyed.
		 * Perhaps limit a craft to carrying a certain number of cores?
		 */
	}
	
	/**
	 * A drone has received cores.
	 */
	public void setCores(int numCores) {
		
		super.incrementCores(numCores);//herr0861 edit
	}
	
	/**
	 * A drone drops the flag when it dies
	 */
	private void dropFlag(Random rand, Toroidal2DPhysics space) {
		carryingFlag = false;
		flag.dropFlag(rand, space);
		flag = null;
	}
	
	/**
	 * Returns the current action
	 * @return
	 */
	public AbstractAction getCurrentAction() {
		return currentAction;
	}
	
	/**
	 * This method takes a Toroidal2DPhysics object and calculates the default drone action.
	 * 
	 * This will tell it to return to the nearest friendly base if it is holding any resources, cores, or flags.
	 * If it is not, it will fly to the nearest friendly ship.
	 * 
	 * TODO: Add "allowsDrone" property to Ship object allowing drone to be rejected by the allied ship not engaged
	 * with collecting resources or to allow clever use of one drone to ferry resources from multiple ships.
	 * @param space
	 * @return
	 */
	public AbstractAction getDroneAction(Toroidal2DPhysics space) {
		//TODO Make this method call setCurrentAction with space and returning the currentAction afterwards.
		AbstractAction tempAction = null;
		
	
		if (this.resources.getTotal() > 0 || this.carryingFlag || this.getNumCores() > 0) { //if we are carrying resources, a flag, or cores
			//Return to the closest friendly base.
			Base tempBase = null;
			double shortestDistance = Double.POSITIVE_INFINITY;
			for (Base xBase : space.getBases()) {
				if (xBase.getTeamName().equalsIgnoreCase(this.getTeamName())) { //if the base is friendly
					double distance = space.findShortestDistance(xBase.getPosition(), this.getPosition());
					if (distance < shortestDistance) { //and if the base is closer than any previously found friendly base, choose this base
						shortestDistance = distance;
						tempBase = xBase;
					}
				}
			} //end baseSearch
			
			if (tempBase != null) {
				tempAction = new MoveToObjectAction(space, this.getPosition(), tempBase, tempBase.getPosition().getTranslationalVelocity());
				
			} else {
					System.out.println("Drone is null");//herr0861DELETE
			}
			
			//System.out.println("Going to base");
			
			
		} else { //If we are not carrying anything, return to the nearest friendly ship.
			
			double minDistance = Double.POSITIVE_INFINITY;
			Ship nearestShip = null;
			for (Ship otherShip : space.getShips()) {

				if (otherShip.getTeamName().equalsIgnoreCase(this.getTeamName())) {//Determine if this ship is on our team or not
					nearestShip = otherShip;
					double distance = space.findShortestDistance(this.getPosition(), otherShip.getPosition());
					if (distance < minDistance) {
						minDistance = distance;
						nearestShip = otherShip;
					}//end if statement comparing distances

				}//End if statement comparing teams
				
				
			}//End for loop going through all ships
			
			if (nearestShip != null) {
				tempAction = new MoveToObjectAction(space, this.getPosition(), nearestShip, nearestShip.getPosition().getTranslationalVelocity());
			} else {
					//System.out.println("Drone is null");//herr0861DELETE
			}
		}
		
		return tempAction;
		
	}//end getDroneAction
	
	/**
	 * This method takes a Toroidal2DPhysics object and calculates the default drone action.
	 * It then sets it as this drone's current action.
	 * 
	 * This will tell it to return to the nearest friendly base if it is holding any resources, cores, or flags.
	 * If it is not, it will fly to the nearest friendly ship.
	 * 
	 * @return
	 */
	public void setCurrentAction(Toroidal2DPhysics space) {
		//TODO Make this method call the 
		
		if (this.resources.getTotal() > 0 || this.carryingFlag || this.getNumCores() > 0) { //if we are carrying resources, a flag, or cores
			//return to nearest friendly base
			Base tempBase = null;
			double shortestDistance = Double.POSITIVE_INFINITY;
			for (Base xBase : space.getBases()) {
				if (xBase.getTeamName().equalsIgnoreCase(this.getTeamName())) { //if the base is friendly
					double distance = space.findShortestDistance(xBase.getPosition(), this.getPosition());
					if (distance < shortestDistance) { //and if the base is closer than any previously found friendly base, choose this base
						shortestDistance = distance;
						tempBase = xBase;
					}
				}
			} //end baseSearch
			if (tempBase != null) {
				currentAction = new MoveToObjectAction(space, this.getPosition(), tempBase, tempBase.getPosition().getTranslationalVelocity());
				
			} else {
					System.out.println("Ship is null");
			}
			
		} else { //If we are not carrying anything, return to the nearest friendly ship.
			
			double minDistance = Double.POSITIVE_INFINITY;
			Ship nearestShip = null;
			for (Ship otherShip : space.getShips()) {

				if (otherShip.getTeamName().equalsIgnoreCase(this.getTeamName())) {//Determine if this ship is on our team or not
					nearestShip = otherShip;
					double distance = space.findShortestDistance(this.getPosition(), otherShip.getPosition());
					if (distance < minDistance) {
						minDistance = distance;
						nearestShip = otherShip;
					}//end if statement comparing distances
				}//End if statement comparing teams
			}//End for loop going through all ships
			
			if (nearestShip != null) {
				currentAction = new MoveToObjectAction(space, this.getPosition(), nearestShip, nearestShip.getPosition().getTranslationalVelocity());
			} else {
					System.out.println("Ship is null");
			}	
		}
	}//end set action

	/**
	 * Set the action the drone is currently executing
	 * Note, this should only be done within the simulator and not
	 * within the team client (where it will be ignored)
	 * 
	 * @param currentAction
	 */
	public void setCurrentAction(AbstractAction currentAction) {
		this.currentAction = currentAction;
	}

	public String toString() {
		String str = "Drone id " + id + " team " + teamName + " at " + position + " resources " + resources + 
				" flags: " + numFlags + " cores: " + numCores;
		return str;
	}

	/**
	 * Drones cannot respawn.
	 */
	public boolean canRespawn() {
		return false;

	}


	/**
	 * Drone has to call its own set alive when it updates its energy
	 */
	@Override
	public void updateEnergy(int difference) {
		energy += difference;

		if (energy > maxEnergy) {
			energy = maxEnergy;
		}
	}

	/**
	 * Deposit the flag by setting the ship to not carry it
	 */
	public void depositFlag() {
		this.carryingFlag = false;
		flag.depositFlag();
		flag = null;
	}		

}
 public class Flag extends AbstractObject {
    public static final int FLAG_RADIUS = 10;
    public static final int FLAG_MASS = 100;

    /**
	 * The name of the team that this flag belongs to 
	 */
	String teamName;
	
	/**
	 * The color of this team
	 */
	Color teamColor;
	
	/**
	 * The team that owns this base
	 */
	Team team;
	
	/**
	 * The list of starting locations for the flag
	 */
	Position[] startingLocations;

	/**
	 * Is the flag being carried?  Changes its graphics
	 */
	boolean beingCarried;
	
	/**
	 * Reference to the ship/drone carrying the flag (if it is being carried)
	 */
	AbstractObject carryingShipOrDrone;
	
	/**
	 * Create a new flag
	 * 
	 * @param position
	 * @param teamName
	 * @param team
	 * @param startingLocations
	 */
	public Flag(Position position, String teamName, Team team, Position[] startingLocations) {
		super(FLAG_MASS, FLAG_RADIUS, position);
		this.teamName = teamName;
		this.team = team;
		teamColor = team.getTeamColor();
		graphic = new FlagGraphics(this, teamColor);
		setAlive(true);
		setDrawable(true);
		this.isMoveable = true;
		// copy the locations to avoid exploits
		this.startingLocations = new Position[startingLocations.length];
		for (int i = 0; i < startingLocations.length; i++) { 
			this.startingLocations[i] = startingLocations[i].deepCopy();
		}
		this.beingCarried = false;
		this.carryingShipOrDrone = null;
	}

	@Override
	/**
	 * Make a deep copy of the current flag's state
	 */
	public Flag deepClone() {
		Flag newFlag = new Flag(this.position.deepCopy(), this.teamName, team, this.startingLocations);
		newFlag.setAlive(isAlive);
		newFlag.setDrawable(isDrawable);
		newFlag.beingCarried = this.beingCarried;
		if (newFlag.carryingShipOrDrone != null) {
			newFlag.carryingShipOrDrone = this.carryingShipOrDrone.deepClone();
		}
		newFlag.id = id;
		return newFlag;
	}

	/**
	 * Return the team that this flag belongs to
	 * 
	 * @return
	 */
	public String getTeamName() {
		return teamName;
	}

	/**
	 * Is the flag being carried?
	 * 
	 * @return
	 */
	public boolean isBeingCarried() {
		return beingCarried;
	}
	
	/**
	 * Set the flag to being carried by this drone
	 * Doing so removes it from any ship that may be carrying it.
	 * 
	 * @param ship
	 */
	public void pickupFlag(Drone drone) {
		this.beingCarried = true;
		this.carryingShipOrDrone = drone;
		setPosition(drone.getPosition().deepCopy());
		this.setDrawable(false);
		this.setRespawn(false);
		this.setAlive(false);
		
	}
	
	/**
	 * Set the flag to being carried by this ship
	 * 
	 * @param ship
	 */
	public void pickupFlag(Ship ship) {
		this.beingCarried = true;
		this.carryingShipOrDrone = ship;
		setPosition(ship.getPosition().deepCopy());
		this.setDrawable(false);
		this.setRespawn(false);
		this.setAlive(false);
		
	}
	
	/**
	 * Drop the flag (likely the ship died)
	 */
	public void dropFlag(Random rand, Toroidal2DPhysics space) {
		//System.out.println("Flag being dropped at " + carryingShip.getPosition());
		this.beingCarried = false;
		this.setDrawable(true);
		this.setAlive(true);
		this.setRespawn(false);
		
		Position newPosition = space.getRandomFreeLocationInRegion(rand, this.getRadius(), 
				(int) carryingShipOrDrone.getPosition().getX(), 
				(int) carryingShipOrDrone.getPosition().getY(), 200);
		newPosition.setAngularVelocity(carryingShipOrDrone.getPosition().getAngularVelocity());
		newPosition.setTranslationalVelocity(carryingShipOrDrone.getPosition().getTranslationalVelocity());
		this.setPosition(newPosition);
		this.carryingShipOrDrone = null;
	}

	/**
	 * Deposit a flag at the base.  For now, that kills a flag
	 * and sets it to not being carried.  It will regenerate on the next timestep.
	 */
	public void depositFlag() {
		this.beingCarried = false;
		this.setAlive(false);
		this.setDrawable(false);
		this.setRespawn(true);
	}
	
	/**
	 * Get a new random Starting location
	 * @param random
	 * @return
	 */
	public Position getNewStartingPosition(Random random) {
		return startingLocations[random.nextInt(startingLocations.length)].deepCopy();
	}

	/**
	 * Get the starting locations for the flag
	 * 
	 * @return
	 */
	public Position[] getStartingLocations() {
		return startingLocations;
	}
	
	
	

} public class Ship extends AbstractActionableObject {
	public static final int SHIP_RADIUS = 15;
	public static final int SHIP_MASS = 200;
	public static final int SHIP_INITIAL_ENERGY = 5000;
	public static final int RESPAWN_INCREMENT = 2;
	public static final int MAX_RESPAWN_INTERVAL = 1000;
	public static final int SHIP_MAX_ENERGY = 5000;

	/**
	 * The action the ship is currently executing
	 */
	AbstractAction currentAction;

	/**
	 * The game search action the ship is currently executing
	 */
	AbstractGameAgent currentGameAgent;

	/**
	 * Time checks left until the ship can respawn
	 */
	int respawnCounter;

	/**
	 * Number of beacons picked up by this ship
	 */
	int numBeacons;

	/**
	 * Last time the respawn counter was used
	 */
	int lastRespawnCounter;

	/**
	 * The color for this team
	 */
	Color teamColor;

	/**
	 * Number of weapons in the air (this is reset by the sim
	 * when bullets explode or reach their target)
	 */
	int numWeaponsInAir;
	
	/**
	 * Does the ship currently have a flag?
	 */
	boolean carryingFlag;
	
	/**
	 * Reference to the flag the ship has (if it has one)
	 */
	Flag flag;
	
	/**
	 * Make a new ship for the specified team.  
	 * @param teamName
	 */
	public Ship(String teamName, Color teamColor, Position location) {
		super(SHIP_MASS, SHIP_RADIUS, location);
		setDrawable(true);
		setAlive(true);
		this.teamName = teamName;
		graphic = new ShipGraphics(this, teamColor);
		this.isControllable = true;
		this.isMoveable = true;
		respawnCounter = 0;
		numBeacons = 0;
		energy = SHIP_INITIAL_ENERGY;
		lastRespawnCounter = 16;
		resources = new ResourcePile();
		this.teamColor = teamColor;
		numWeaponsInAir = 0;
		maxEnergy = SHIP_MAX_ENERGY;
		currentPowerups.add(SpaceSettlersPowerupEnum.FIRE_MISSILE);
		this.carryingFlag = false;
		this.flag = null;
		this.numFlags = 0;
		this.numCores = 0;
		killTagTeam = null;
		assistTagTeam = null;
		healthAtKillTag = 0;
		healthAtAssistTag = 0;
	}

	/**
	 * Deep copy of a ship (used for security)
	 * @return
	 */
	public Ship deepClone() {
		Ship newShip = new Ship(teamName, teamColor, getPosition().deepCopy());

		newShip.setAlive(isAlive);
		newShip.resources = new ResourcePile();
		newShip.addResources(resources);
		newShip.lastRespawnCounter = lastRespawnCounter;
		newShip.numBeacons = numBeacons;
		newShip.energy = energy;
		newShip.respawnCounter = respawnCounter;
		newShip.graphic = new ShipGraphics(newShip, teamColor);
		newShip.currentAction = currentAction;
		newShip.currentGameAgent = currentGameAgent;
		newShip.numWeaponsInAir = numWeaponsInAir;
		newShip.id = id;
		newShip.maxEnergy = maxEnergy;
		newShip.currentPowerups = new LinkedHashSet<SpaceSettlersPowerupEnum>(currentPowerups);
		newShip.weaponCapacity = weaponCapacity;
		newShip.hitsInflicted = hitsInflicted;
		newShip.killsInflicted = killsInflicted;
		newShip.killsReceived = killsReceived;
		newShip.damageInflicted = damageInflicted;
		newShip.damageReceived = damageReceived;
		newShip.carryingFlag = carryingFlag;
		newShip.numFlags = numFlags;
		newShip.isShielded = isShielded;
		newShip.numCores = numCores; 
		newShip.killTagTeam = null;
		newShip.assistTagTeam = null;
		
		if (this.killTagTeam != null) {
			newShip.killTagTeam = killTagTeam.deepCloneNoTags();
		}

		if (this.assistTagTeam != null) {
			newShip.assistTagTeam = assistTagTeam.deepCloneNoTags();
		}
		
		newShip.healthAtAssistTag = healthAtAssistTag;
		newShip.healthAtKillTag = healthAtKillTag;
		
		if (this.flag != null){
			newShip.flag = flag.deepClone();
		}
		return newShip;
	}

	/**
	 * Deep copy of a ship (used for security)
	 * @return
	 */
	public Ship deepCloneNoTags() {
		Ship newShip = new Ship(teamName, teamColor, getPosition().deepCopy());

		newShip.setAlive(isAlive);
		newShip.resources = new ResourcePile();
		newShip.addResources(resources);
		newShip.lastRespawnCounter = lastRespawnCounter;
		newShip.numBeacons = numBeacons;
		newShip.energy = energy;
		newShip.respawnCounter = respawnCounter;
		newShip.graphic = new ShipGraphics(newShip, teamColor);
		newShip.currentAction = currentAction;
		newShip.currentGameAgent = currentGameAgent;
		newShip.numWeaponsInAir = numWeaponsInAir;
		newShip.id = id;
		newShip.maxEnergy = maxEnergy;
		newShip.currentPowerups = new LinkedHashSet<SpaceSettlersPowerupEnum>(currentPowerups);
		newShip.weaponCapacity = weaponCapacity;
		newShip.hitsInflicted = hitsInflicted;
		newShip.killsInflicted = killsInflicted;
		newShip.killsReceived = killsReceived;
		newShip.damageInflicted = damageInflicted;
		newShip.damageReceived = damageReceived;
		newShip.carryingFlag = carryingFlag;
		newShip.numFlags = numFlags;
		newShip.isShielded = isShielded;
		newShip.numCores = numCores; 
		newShip.killTagTeam = null;
		newShip.assistTagTeam = null;
		newShip.healthAtAssistTag = healthAtAssistTag;
		newShip.healthAtKillTag = healthAtKillTag;
		
		if (this.flag != null){
			newShip.flag = flag.deepClone();
		}
		return newShip;
	}


	/**
	 * Resets the ship energy to the initial level
	 */
	public void resetEnergy() {
		energy = SHIP_INITIAL_ENERGY;
	}


	/**
	 * Is the ship carrying a flag right now?  True if so.  False otherwise.
	 * 
	 * @return
	 */
	public boolean isCarryingFlag() {
		return carryingFlag;
	}
	
	/**
	 * Get the flag (this is a deep clone from the client point of view) of the flag the ship is carrying
	 * @return
	 */
	public Flag getFlag() {
		return flag;
	}

	/**
	 * Add the flag to the ship's inventory
	 * 
	 * @param flag
	 */
	public void addFlag(Flag flag) {
		this.flag = flag;
		this.carryingFlag = true;
		this.incrementFlags();
		//System.out.println("Ship " + this + " has a flag now");
	}
	
	/**
	 * Get the color of this team
	 * @return
	 */
	public Color getTeamColor() {
		return teamColor;
	}

	/**
	 * Returns a new weapon of the requested type if the ship is allowed to
	 * fire a new one.  Otherwise returns null.
	 * @param weaponType weapon type to fire
	 * 
	 * @return a valid weapon or null if the ship is out of weapons at the moment
	 */
	public AbstractWeapon getNewWeapon(SpaceSettlersPowerupEnum weaponType) {
		if (numWeaponsInAir < weaponCapacity) {
			Position weaponPosition = getPosition().deepCopy();
			if (weaponType == SpaceSettlersPowerupEnum.FIRE_MISSILE) {
				return new Missile(weaponPosition, this);
			} else if (weaponType == SpaceSettlersPowerupEnum.FIRE_EMP) {
				return new EMP(weaponPosition, this);
			}
		} 
		return null;
	}

	/**
	 * Increment the weapons in play
	 */
	public void incrementWeaponCount(){
		numWeaponsInAir++;
	}

	/**
	 * Decrease the weapon count by one
	 */
	public void decrementWeaponCount() {
		numWeaponsInAir--;
	}

	/**
	 * Set to dead and drop flags and cores in a random location near the ship
	 * 
	 * @param space
	 */
	public void setDeadAndDropObjects(Random rand, Toroidal2DPhysics space) {
		respawnCounter = Math.min(lastRespawnCounter * RESPAWN_INCREMENT, MAX_RESPAWN_INTERVAL);
		lastRespawnCounter = respawnCounter; 
		resetResources();
		resetPowerups();
		if (carryingFlag) {
			dropFlag(rand, space);
		}
		resetAiCores(); 
		super.setAlive(false);
	}


	/**
	 * Ships need to behave slightly differently when set to dead or respawned 
	 * so this is an override of the abstract class
	 */
	public void setAlive(boolean value) {
		if (value == false) {
			respawnCounter = Math.min(lastRespawnCounter * RESPAWN_INCREMENT, MAX_RESPAWN_INTERVAL);
			lastRespawnCounter = respawnCounter; 
			resetResources();
			resetPowerups();
			resetAiCores(); 
		} else {
			resetEnergy();
		}

		super.setAlive(value);
	}
	
	/**
	 * Drop all the cores by resetting to 0
	 * Will also need code inside physics sim to drop all AiCores
	 */
	public void resetAiCores() {
		//Just erase the core count, as we are not currently tracking the specific cores held by a ship.
		numCores = 0;
		/*
		 * From Josiah: In the future the ship may drop any AiCores it is holding, however I don't currently
		 * have a good strategy for releasing the cores of a ship holding a very large number of cores.
		 * For example, if a ship is holding 8 cores and dies, spawning those cores leads to a bunch of
		 * interesting collisions and several of the cores tend to end up destroyed.
		 * Perhaps limit a ship to carrying a certain number of cores?
		 */
	}
	
	/**
	 * A ship has collided with a core and collected it.
	 */
	public void incrementCores() {
		super.incrementCores(1);//herr0861 edit
	}
	
	/**
	 * A ship drops the flag when it dies
	 */
	private void dropFlag(Random rand, Toroidal2DPhysics space) {
		//System.out.println("Dropping flag from ship");
		carryingFlag = false;
		flag.dropFlag(rand, space);
		flag = null;
	}
	

	/**
	 * When an item dies, its power ups disappear
	 */
	public void resetPowerups() {
		super.resetPowerups();
		currentPowerups.add(SpaceSettlersPowerupEnum.FIRE_MISSILE);
	}


	/**
	 * Return the number of beacons picked up by this ship
	 * @return
	 */
	public int getNumBeacons() {
		return numBeacons;
	}

	/**
	 * Increment the number of beacons for this ship
	 */
	public void incrementBeaconCount() {
		numBeacons++;
	}

	/**
	 * Get the action the ship is currently executing
	 * @return
	 */
	public AbstractAction getCurrentAction() {
		return currentAction;
	}

	/**
	 * Set the action the ship is currently executing
	 * Note, this should only be done within the simulator and not
	 * within the team client (where it will be ignored)
	 * 
	 * @param currentAction
	 */
	public void setCurrentAction(AbstractAction currentAction) {
		this.currentAction = currentAction;
	}

	/**
	 * Set the game approach the ship could use if it hits a gameable asteroid
	 * Note, this should only be done within the simulator and not
	 * within the team client (where it will be ignored)
	 * 
	 * @param currentAction
	 */
	public void setCurrentGameAgent(AbstractGameAgent currentAgent) {
		this.currentGameAgent = currentAgent;
	}
	
	/**
	 * Return the current game search action
	 * 
	 * @return
	 */
	public AbstractGameAgent getCurrentGameAgent() {
		return currentGameAgent;
	}

	
	public String toString() {
		String str = "Ship id " + id + " team " + teamName + " at " + position + " resources " + resources + 
				" flags: " + numFlags;
		return str;
	}

	/**
	 * Ships have to wait a certain amount of time until they can respawn
	 */
	public boolean canRespawn() {
		respawnCounter--;

		if (respawnCounter <= 0) {
			return true;
		} else {
			return false;
		}

	}

	/**
	 * Ship has to call its own set alive when it updates its energy
	 */
	@Override
	public void updateEnergy(int difference) {
		energy += difference;

		if (energy > maxEnergy) {
			energy = maxEnergy;
		}
	}

	/**
	 * Deposit the flag by setting the ship to not carry it
	 */
	public void depositFlag() {
		this.carryingFlag = false;
		flag.depositFlag();
		flag = null;
	}
	
} public abstract class AbstractGame {
	 public static int player1 = 1;
	 public static int player2 = 2;
	 
	 int heuristicPlayer;
	
	/** 
	 * Is the game over?
	 *  
	 * @return true if the game is over and false otherwise
	 */
	public abstract boolean isGameOver();

	/**
	 * Return true if it is the true player's turn and false otherwise
	 * @return
	 */
	public abstract boolean getTurn();
	
	/**
	 * Play the action for the current player
	 */
	public abstract void playAction(AbstractGameAction action);
	
	/**
	 * Return the winning player (remember players are true and false)
	 * 
	 * @return
	 */
	public abstract int getWinner();
	
	/**
	 * Get the game board
	 * @return
	 */
	public abstract AbstractGameBoard getBoard();

	/**
	 * Return the heuristic player (set inside the actual game initialization)
	 * @return
	 */
	public int getHeuristicPlayer() {
		return heuristicPlayer;
	}
	
} public class AbstractGameAction {

} public abstract class AbstractGameAgent {
	int player;

	/**
	 * All game agents must be able to return a next move
	 * @param game
	 * @return
	 */
	public abstract AbstractGameAction getNextMove(AbstractGame game);

	
	public int getPlayer() {
		return player;
	}

	public void setPlayer(int player) {
		this.player = player;
	}
	

} public class GameFactory {
	public static AbstractGame generateNewGame() {
		return new TicTacToe3D();
	}
} public class HeuristicTicTacToe3DGameAgent extends AbstractGameAgent {
	public HeuristicTicTacToe3DGameAgent(int player) {
		this.player = player;
	}

	/**
	 * First see if we can win in one and then otherwise take the first available 
	 * (this will be improved over time)
	 * 
	 * @param game
	 * @return
	 */
	public AbstractGameAction getNextMove(AbstractGame game) {
		TicTacToe3DBoard board = (TicTacToe3DBoard) game.getBoard();
		
		// check to see if the center is free
		if (board.board[1][1][1] == board.empty) {
			return new TicTacToe3DAction(1, 1, 1);
		}
		
		// check to see if the other two centers are free
		if (board.board[1][1][0] == board.empty) {
			return new TicTacToe3DAction(1, 1, 0);
		}

		if (board.board[1][1][2] == board.empty) {
			return new TicTacToe3DAction(1, 1, 2);
		}

		// check to see if we can win in one
		for (int i = 0; i < 3; i++) {
			for (int j = 0; j < 3; j++) {
				for (int k = 0; k < 3; k++) {
					if (board.board[i][j][k] == board.empty) {
						TicTacToe3DAction action = new TicTacToe3DAction(i, j, k);
						board.makeMove(action, this.player);
						if (board.getWinningPlayer() == this.player) {
							return action;
						}
						// unmake the move
						board.board[i][j][k] = board.empty;
					}
				}
			}
		}
		
		
		// otherwise play the first available move
		for (int i = 0; i < 3; i++) {
			for (int j = 0; j < 3; j++) {
				for (int k = 0; k < 3; k++) {
					if (board.board[i][j][k] == board.empty) {
						return new TicTacToe3DAction(i, j, k);
					}
				}
			}
			
		}
		return null;
	}

} abstract public class AbstractObject {
	/**
	 * Position of the object in the simulator space 
	 */
	protected Position position;
	
	/**
	 * The radius of the object
	 */
	protected int radius;
	
	/**
	 * The mass of the object
	 */
	protected int mass, originalMass;
	
	/**
	 * Is the object alive?  Some objects can never die but some can.
	 */
	protected boolean isAlive;
	
	/**
	 * Is the object currently drawable on the GUI?
	 */
	boolean isDrawable;
	
	/**
	 * Is the object moveable?  Some objects can be flagged as not moveable and then
	 * they will not move even if things bounce into them.
	 */
	protected boolean isMoveable;
	
	/**
	 * The resources available from this object (if it is mineable) or that this object is holding (ship and base).
	 * Ignored for objects that don't use this (such as beacons).
	 */
	protected ResourcePile resources;
	
	/**
	 * Number of flags this ship or base has collected
	 */
	protected int numFlags;
	
	/**
	 * Number of cores this ship or base has collected
	 */
	protected int numCores;
	
	/**
	 * The graphics for this object in the GUI
	 */
	protected SpacewarGraphics graphic;
	
	/**
	 * Is the object controlled through actions?  Ships and teams are.
	 */
	protected boolean isControllable;

	/**
	 * Id to track over cloning
	 */
	protected UUID id;
	
	/**
	 * flag to allow an object to respawn or not
	 */
	protected boolean respawn;
	
	/**
	 * All objects start at rest
	 */
	public AbstractObject(int mass, int radius) {
		this.mass = mass;
		this.originalMass = mass;
		this.radius = radius;
		position = new Position(0,0);
		this.id = UUID.randomUUID();
		resources = new ResourcePile();
		this.numFlags = 0;
		this.respawn = true;
		this.numCores = 0;
	}

	/**
	 * All objects start at rest
	 */
	public AbstractObject(int mass, int radius, Position position) {
		this.mass = mass;
		this.originalMass = mass;
		this.radius = radius;
		this.position = position;
		this.id = UUID.randomUUID();
		resources = new ResourcePile();
		this.numFlags = 0;
		this.respawn = true;
		this.numCores = 0;
	}

	
	/**
	 * @param isAlive set to true if the object is alive
	 */
	public void setAlive(boolean isAlive) {
		this.isAlive = isAlive;
	}

	/**
	 * @param isDrawable set to true if the object is drawable. Note it is only 
	 * drawn if it is alive as well.
	 */
	public void setDrawable(boolean isDrawable) {
		this.isDrawable = isDrawable;
	}
	
	/**
	 * Is this object alive?
	 * 
	 * @return the isAlive
	 */
	public boolean isAlive() {
		return isAlive;
	}

	/**
	 * Is this object drawable?
	 * 
	 * @return the isDrawable
	 */
	public boolean isDrawable() {
		return isDrawable;
	}

	/**
	 * @return the location
	 */
	public Position getPosition() {
		return position;
	}
	
	/**
	 * Move the object (should only be called inside the simulator)
	 * 
	 * @param location
	 */
	public void setPosition(Position location) {
		this.position = location;
	}

	/**
	 * Return the radius of the object
	 * @return
	 */
	public int getRadius() {
		return radius;
	}
	
	/**
	 * Get the mass of the object
	 * @return the mass of the object
	 */
	public int getMass() {
		return mass;
	}

	/**
	 * Change the mass of the object to a new value
	 * 
	 * @param mass the new mass
	 */
	public void setMass(int mass) {
		this.mass = mass;
	}

	/**
	 * Add a list of new resources to the ship/base cargo bay
	 * 
	 * @param newResources new list of AbstractResource to add to the cargo bay
	 */
	public void addResources(ResourcePile newResources) {
		resources.add(newResources);
		setMass((int) (ResourceFactory.REFINED_RESOURCE_DENSITY_MULTIPLIER * resources.getMass() + originalMass));
	}
	
	/**
	 * Get the current available resources
	 * 
	 * @return the ResourcePile of resources held by this object
	 */
	public ResourcePile getResources() {
		return resources;
	}

	/**
	 * Reset the list of resources (probably because the ship died)
	 */
	public void resetResources() {
		resources.reset();
		setMass(originalMass);
	}
	

	/**
	 * @return the graphic, which is what is drawn in the graphics window
	 */
	public SpacewarGraphics getGraphic() {
		return graphic;
	}

	/**
	 * Is this object controlled by an external client?
	 * @return
	 */
	public boolean isControllable() {
		return isControllable;
	}

	/**
	 * is the object moveable? 
	 * @return
	 */
	public boolean isMoveable() {
		return isMoveable;
	}
	
	
	/**
	 * Return true if this object can respawn now and false otherwise.
	 * Assumes you can respawn immediately unless you override.
	 */
	public boolean canRespawn() {
		return respawn;
	}
	
	/**
	 * Set the respawn flag
	 * 
	 * @param newRespawn
	 * @return
	 */
	public void setRespawn(boolean newRespawn) {
		this.respawn = newRespawn;
	}
	
	/**
	 * Gets the unique id for this object
	 * @return
	 */
	public UUID getId() {
		return id;
	}

	
	/**
	 * Hash on the UUID
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	/**
	 * Equals on the UUID
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		AbstractObject other = (AbstractObject) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

	/**
	 * Make a deep copy of this object (deeply copying all pointers to avoid
	 * exploitation within the simulator)
	 * 
	 * @return
	 */
	abstract public AbstractObject deepClone();

	@Override
	public String toString() {
		return "AbstractObject at " + position;
	}
	
	/**
	 * Resets the UUID of the object (for respawning)
	 */
	public void resetId() {
		id = UUID.randomUUID();
	}

	/**
	 * Get the total number of flags owned by this object (ship or base)
	 * @return
	 */
	public int getNumFlags() {
		return numFlags;
	}
	
	/**
	 * Increment the number of flags held by this object
	 */
	public void incrementFlags() {
		numFlags++;
	}

	/**
	 * Get the total number of cores owned by this object (ship or base)
	 * @return
	 */
	public int getNumCores() {
		return numCores;
	}
	
	/**
	 * Increment the number of cores held by this object
	 */
	public void incrementCores(int number) {
		numCores+= number;
	}

} public class AiCore extends AbstractObject{
	
	/**
	 * The radius of an AiCore
	 */
	public static final int CORE_RADIUS = 8;
	
	public static final int CORE_MASS = 25;
	
	public static final int CORE_MAX_ENERGY = 500;

	/**
	 * The color of the team that owns this AiCore
	 */
	Color teamColor;
	
	/**
	 * The name of the team who owns this AiCore
	 */
	String teamName;
	
	/**
	 * The energy of the core
	 */
	int energy;

	
	public AiCore(Position location, String teamNameIn, Color teamColorIn) {
		super(CORE_MASS, CORE_RADIUS, location);
		this.teamName = teamNameIn;
		this.teamColor = teamColorIn;
		this.energy = CORE_MAX_ENERGY;
		setDrawable(true);
		setAlive(true);
		this.isMoveable = true;
		graphic = new CoreGraphics(this, teamColorIn);
	}
	
	/**
	 * Makes a copy used for security
	 */
	public AiCore deepClone() {
		AiCore newCore = new AiCore(getPosition().deepCopy(),this.teamName, this.teamColor);
		newCore.setAlive(isAlive);
		newCore.energy = energy;
		newCore.id = id;
		return newCore;
	}
	
	/**
	 * Resets the energy back to full for the core
	 */
	public void resetCoreEnergy() {
		this.energy = AiCore.CORE_MAX_ENERGY;
	}
	
	/**
	 * Update the energy of the core and kill it energy is depleted
	 */
	public void updateEnergy(int change) {
		this.energy += change;
		if (this.energy <= 0) {
			this.setAlive(false);
		}
	}
	
	/**
	 * Returns the current energy of the core.
	 * Can help avoid pursuing cores that are likely to die before the ship reaches them.
	 * @return
	 */
	public int getCoreEnergy() {
		return this.energy;
	}
	
	/**
	 * Returns the name of the team that owns this AiCore.
	 * @return
	 */
	public String getTeamName() {
		return teamName;
	}

	
	public String toString() {
		String str = "Core id: [" + id + "], Core Team: [" + this.teamName + "]";
		return str;
	}

}
 public final class Missile extends AbstractWeapon {
	public static final int MISSILE_DAMAGE = -200;
	public static final int MISSILE_COST = -50;
	public static final int MISSILE_RADIUS = 3;
	public static final int MISSILE_MASS = 1;
	public static final int INITIAL_VELOCITY = 100;
	
	/**
	 * A new bullet with the specified position and shift
	 * Needs to be shifted so it doesn't hit the ship it is firing from
	 */
	public Missile(Position position, Ship firingShip) {
		super(MISSILE_MASS, MISSILE_RADIUS, position, MISSILE_DAMAGE, MISSILE_COST,firingShip);
		super.shiftWeaponFiringLocation(Math.abs(firingShip.getPosition().getTotalTranslationalVelocity()) + INITIAL_VELOCITY);
		graphic = new MissileGraphics(this);
		setDrawable(true);
		setAlive(true);
		this.isControllable = false;
		this.isMoveable = true;
	}

	/**
	 * Copy the bullet
	 */
	public Missile deepClone() {
		Missile newBullet = new Missile(position.deepCopy(), firingShip.deepClone());
		newBullet.setAlive(isAlive);
		newBullet.id = id;
		newBullet.position = position.deepCopy();
		return newBullet;
	}

	/**
	 * Bullets do not respawn
	 */
	public boolean canRespawn() {
		return false;
	}

	/**
	 * Mostly a placeholder for when power ups work
	 */
	public SpaceSettlersPowerupEnum getType() {
		return SpaceSettlersPowerupEnum.FIRE_MISSILE;
	}

	/**
	 * Apply the power up.  For a bullet,
	 * the means unshielding the ship if it is shielded and updating the energy of the ship.
	 */
	@Override
	public void applyPowerup(AbstractActionableObject actionableObject) {
		// can't fire with shields up so automatically drop them
		actionableObject.setShielded(false);
		
		// only ships fire bullets (TODO: fix this when/if bases can)
		Ship ship = (Ship) actionableObject;
		ship.updateEnergy(getCostToUse());
		ship.incrementWeaponCount();
	}


} public class Position {
	double x, y, orientation, angularVelocity;
	Vector2D velocity;

	public Position(double x, double y) {
		super();
		this.x = x;
		this.y = y;
		velocity = new Vector2D();
	}

	public Position(double x, double y, double orientation) {
		super();
		this.x = x;
		this.y = y;
		this.orientation = orientation;
		velocity = new Vector2D();
	}
	
	public Position(Vector2D vec) {
		super();
		this.x = vec.getXValue();
		this.y = vec.getYValue();
		orientation = 0;
		velocity = new Vector2D();
	}
	
	public Position deepCopy() {
		Position newPosition = new Position(x, y, orientation);
		newPosition.velocity = new Vector2D(velocity);
		newPosition.angularVelocity = angularVelocity;
		
		return newPosition;
	}

	public double getX() {
		return x;
	}

	public double getY() {
		return y;
	}

	public double getOrientation() {
		return orientation;
	}
	
	public void setX(double x) {
		this.x = x;
	}

	public void setY(double y) {
		this.y = y;
	}

	public double getTotalTranslationalVelocity() {
		return velocity.getTotal();
	}
	
	public double getTranslationalVelocityX() {
		return velocity.getXValue();
	}
	
	public double getTranslationalVelocityY() {
		return velocity.getYValue();
	}
	
	public Vector2D getTranslationalVelocity() {
		return velocity;
	}
	
	public void setTranslationalVelocity(Vector2D newVel) {
		this.velocity = newVel;
	}
	
	public double getAngularVelocity() {
		return angularVelocity;
	}
	
	public void setOrientation(double orientation) {
		this.orientation = orientation;
	}
	
	public double getxVelocity() {
		return velocity.getXValue();
	}

	public double getyVelocity() {
		return velocity.getYValue();
	}

	public void setAngularVelocity(double angularVelocity) {
		this.angularVelocity = angularVelocity;
	}

	public String toString() {
		String str = "(" + x + " , " + y + ", " + orientation + ") velocity: " + velocity + ", " + angularVelocity;
		return str;
	}
	
	/**
	 * Compares positions on location (x,y) only and ignores orientation and velocities
	 * 
	 * @param newPosition
	 * @return
	 */
	public boolean equalsLocationOnly(Position newPosition) {
		if (newPosition.getX() == x && newPosition.getY() == y) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Verifies that all components of the position are finite and not NaN
	 * @return true if the position is valid (finite and a number, doesn't check world size) and false otherwise 
	 */
	public boolean isValid() {
		if (Double.isFinite(x) && Double.isFinite(y) && 
				Double.isFinite(angularVelocity) && Double.isFinite(orientation)  &&
				velocity.isValid()) {
			return true;
		} else {
			return false;
		}
	}
	
} @SuppressWarnings("serial")
public final class Vector2D implements Serializable, Cloneable {
    private final double x, y;

    public static final Vector2D ZERO_VECTOR = new Vector2D();
    public static final Vector2D X_UNIT_VECTOR = new Vector2D(1, 0);
    public static final Vector2D X_NEG_UNIT_VECTOR = new Vector2D(-1, 0);
    public static final Vector2D Y_UNIT_VECTOR = new Vector2D(0, 1);
    public static final Vector2D Y_NEG_UNIT_VECTOR = new Vector2D(0, -1);

    public static final double HALFPI = 0.5 * Math.PI;
    public static final double THREEHALFPI = 1.5 * Math.PI;
    public static final double TWOPI = 2.0 * Math.PI;
    
    /**
     * Cached (calculated on demand)
     */
    double magnitude;

    /**
     * Create a vector with the given x and y values.
     * @param x
     * @param y
     */
    public Vector2D(final double x, final double y) {
        this.x = x;
        this.y = y;
        magnitude = Double.NaN;
    }

    /**
     * Create a new vector from an old one;
     */
    public Vector2D(final Vector2D b){
    	x = b.x;
    	y = b.y;
        magnitude = Double.NaN;
    }
    
    /**
     * Initialize from a position
     * @param position
     */
    public Vector2D(Position position) {
    	x = position.getX();
    	y = position.getY();
        magnitude = Double.NaN;
    }

    /**
     * Create the zero vector
     */
    public Vector2D() {
        this(0, 0);
    }
    
    /**
     * Cloned copy for the Cloneable interface
     */
    protected Object clone() {
    	return new Vector2D(this);
    }

    /**
     * Create a vector from the given angle (in radians) and magnitude
     * @param angle In radians
     * @param magnitude
     * @return A new Vector2D
     */
    public static Vector2D fromAngle(double angle, double magnitude) {
        return new Vector2D((float) Math.cos(angle) * magnitude, (float) Math.sin(angle) * magnitude);
    }

    /**
     * Create a random vector with a magnitude no greater than specified
     * @param rand The source of randomness to use.
     * @param maxMagnitude
     * @return A new random Vector2D
     */
    public static Vector2D getRandom(Random rand, double maxMagnitude) {
        final double max = maxMagnitude * maxMagnitude;

        final double x2 = rand.nextDouble() * max;
        final double y2 = rand.nextDouble() * (max - x2);

        final double x = rand.nextBoolean() ? (double)Math.sqrt(x2) : -(double)Math.sqrt(x2);
        final double y = rand.nextBoolean() ? (double)Math.sqrt(y2) : -(double)Math.sqrt(y2);

        return new Vector2D(x, y);
    }

    /**
     * The X value of the vector.
     * @return The X value of the vector.
     */
    public double getXValue() {
        return x;
    }

    /**
     * The Y value of the vector.
     * @return The Y value of the vector.
     */
    public double getYValue() {
        return y;
    }
    
    /**
     * return the unit vector
     * @return
     */
    public Vector2D getUnitVector() {
    	return this.divide(getMagnitude());
    } 
    
    /**
     * The magnitude of the vector.  
     * @return The magnitude of the vector.
     */
    public double getMagnitude() {
    	magnitude = Math.sqrt(x*x + y*y);
    	return magnitude;
    }

    /**
     * Get the sum of the vector components
     * @return
     */
    public double getTotal() {
    	return x + y;
    }
    
    /**
     * The angle of the vector
     * @return The angle of the vector.
     */
    public double getAngle() {
        return Math.atan2(y, x);
    }

    /**
     * The angle (in radians) between this vector and the given vector.
     * The angle is positive if v is to the left of this vector, and
     * negative if v is to the right of this vector (right hand coords)
     *
     * @param v A given vector.
     * @return The angle between the two vectors in radians.
     */
    public double angleBetween(final Vector2D v) {
        double num, den, angle;

        num = (x * v.x + y * v.y);
        den = (getMagnitude() * v.getMagnitude());

        if(den == 0 ) {
            return 0;
        }

        if (Math.abs(num) > Math.abs(den)) {
            if(num > den) {
                num = den;
            } else {
                num = -den;
            }
        }

        angle = (float) Math.acos(num / den);

        return (cross(v) >= 0) ? angle : -angle;
    }

    /**
     * The unit vector derived from this vector, or an arbitrary unit vector if this is the zero vector
     * @return A unit vector with the same orientation as this vector.
     */
    public final Vector2D unit() {
    	double magnitude = getMagnitude();
        if (magnitude == 0)
            return X_UNIT_VECTOR;

        return divide(magnitude);
    }

    /**
     * Reverse this vector.
     * @return The reverse of this vector.
     */
    public final Vector2D negate() {
        return new Vector2D(-x, -y);
    }

    /**
     * Add these two vectors together.
     * @param vector2d Vector to add
     * @return The sum of the vectors.
     */
    public final Vector2D add(final Vector2D vector2d) {
        return new Vector2D(x + vector2d.x, y + vector2d.y);
    }

    /**
     * Subtract the other vector from this vector.
     * @param v
     * @return The vector resulting from subtracting the other vector from this vector.
     */
    public final Vector2D subtract(final Vector2D v) {
        return new Vector2D(x - v.x, y - v.y);
    }

    /**
     * Multiply this vector by the given scalar.
     * @param f
     * @return The scaled vector.
     */
    public final Vector2D multiply(double f) {
        return new Vector2D(x * f, y * f);
    }

    /**
     * Divide this vector by the given scalar.
     * @param f
     * @return The scaled vector
     */
    public final Vector2D divide(double f) {
        return new Vector2D(x / f, y / f);
    }

    /**
     * Get the dot product of the two vectors.
     * @param v
     * @return The dot product
     */
    public final double dot(final Vector2D v) {
        return x * v.x + y * v.y;
    }

    /**
     * Get the cross product of the two vectors.
     * @param v
     * @return The cross product.
     */
    public final double cross(final Vector2D v) {
        return x * v.y - y * v.x;
    }

    /**
     * Rotate this vector using the given sine and cosine values.
     * @param cos
     * @param sin
     * @return The rotated vector.
     */
    public final Vector2D fastRotate(final float cos, final float sin) {
        return new Vector2D(x * cos - y * sin, x * sin + y * cos);
    }

    /**
     * Subtract the other vector from this vector and rotate the result using the
     * given sine and cosine values.
     * @param v
     * @param cos
     * @param sin
     * @return The subtracted and rotated vector.
     */
    public final Vector2D subtractAndRotate(final Vector2D v, final float cos, final float sin) {
        final double x2 = x - v.x, y2 = y - v.y;
        return new Vector2D(x2 * cos - y2 * sin, x2 * sin + y2 * cos);
    }

    /**
     * Rotate this vector by the specified angle (in radians)
     * @param f
     * @return the rotated vector
     */
    public final Vector2D rotate(double f) {
        final double cos = Math.cos(f);
        final double sin =  Math.sin(f);
        return new Vector2D(x * cos - y * sin, x * sin + y * cos);
    }

    /**
     * Project the given vector onto this vector. 
     * 
     *  Math from:
     *  http://en.wikipedia.org/wiki/Vector_projection
     *
     * @param v
     * @return The scalar projection of the other vector onto this one
     */
    public double scalarProject(Vector2D other) {
        double a1 = (this.dot(other)) / (other.getMagnitude());
        return a1;
    }
    
    /**
     * Project the given vector onto this one
     * 
     * Math from:
     * 
     * http://en.wikipedia.org/wiki/Vector_projection
     * 
     * @param other
     * @return the vector project of the other vector onto this one
     */
    public Vector2D vectorProject(Vector2D other) {
    	double num = this.dot(other);
    	double den = other.dot(other);
    	
    	Vector2D a1 = other.multiply(num / den);

    	return a1;
    }
    
    /**
     * Project the given vector onto this one
     * 
     * Math from:
     * 
     * http://en.wikipedia.org/wiki/Vector_projection
     * 
     * @param other
     * @return the vector project of the other vector onto this one
     */
    public Vector2D vectorRejection(Vector2D other) {
    	Vector2D a2 = this.subtract(this.vectorProject(other));

    	return a2;
    }

    /**
     * Determine if two vectors are equal (have the same components)
     * @param v
     * @return True if the components match, false otherwise.
     */
    public final boolean equals(final Vector2D v) {
        return x == v.x && y == v.y;
    }

    /**
     * Compare the vectors on the basis of magnitude.
     * @param other
     * @return -1 if this is smaller than, 0 if equal, 1 if this is greater than
     */
    public int magnitudeCompareTo(final Vector2D other) {
        return ((Double)getMagnitude()).compareTo(other.getMagnitude());
    }

    /**
     * Equals comparison partially generated by eclipse.  Checks if the x/y are the same
     */
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Vector2D other = (Vector2D) obj;
		return this.equals(other);
	}

	/**
     * String representation of the vector for the user
     */
    public String toString() {
        final String str = x + " " + y;
        return str;
    }

    /**
     * Ensures all components of the vector are valid (finite and not NaN)
     * 
	 * @return true if the vector is valid (finite and a number, doesn't check world size) and false otherwise 
     */
	public boolean isValid() {
		if (Double.isFinite(x) && Double.isFinite(y)) {
			return true;
		} else {
			return false;
		}
	}

}
import spacesettlers.objects.Asteroid;
import spacesettlers.objects.Base;
import spacesettlers.objects.Beacon;
import spacesettlers.objects.Drone;
import spacesettlers.objects.Flag;
import spacesettlers.objects.Ship;
import java.util.concurrent.ThreadLocalRandom;
import spacesettlers.game.AbstractGame;
import spacesettlers.game.AbstractGameAction;
import spacesettlers.game.AbstractGameAgent;
import spacesettlers.game.GameFactory;
import spacesettlers.game.HeuristicTicTacToe3DGameAgent;
import spacesettlers.objects.AbstractObject;
import spacesettlers.objects.AiCore;
import spacesettlers.objects.weapons.EMP;
import spacesettlers.objects.weapons.Missile;
import spacesettlers.utilities.Position;
import spacesettlers.utilities.Vector2D;
	/**
	 * Give the ship an energy penalty for running into the object
	 * 
	 * @param ship
	 */
	public void shipCollision(Ship ship) {
		double penalty = -Math.abs(COLLISION_PENALTY * ship.getPosition().getTotalTranslationalVelocity());
		ship.updateEnergy((int)(penalty));
		ship.incrementDamageReceived((int)(penalty));
	}
	
	/**
	 * Give the drone an energy penalty for running into the object.
	 * 
	 * @param drone
	 */
	public void droneCollision(Drone drone) {
		double penalty = -Math.abs((COLLISION_PENALTY / 3) * drone.getPosition().getTotalTranslationalVelocity()); //The drone is made of materials that can withstand low mass impacts much better than ships. Less of a penalty for colliding.
		drone.updateEnergy((int)(penalty));
		//Currently no need to track how much damage the drone took for now, but  we should implement it into damage done for aggressive clients.
		drone.incrementDamageReceived((int)(penalty));
	}

	/**
	 * Collide with a beacon
	 * @param beacon
	 * @param object
	 */
	public void beaconCollision(Beacon beacon, AbstractObject object) {
		// beacons die when they are touched (respawned elsewhere)
		beacon.setAlive(false);

		if (object.getClass() == Ship.class) {
			Ship ship = (Ship) object;
			ship.incrementBeaconCount();
			ship.updateEnergy(Beacon.BEACON_ENERGY_BOOST);
		} else if(object.getClass() == Drone.class) {//herr0861
			Drone drone = (Drone) object;
			drone.updateEnergy(Beacon.BEACON_ENERGY_BOOST);
		}
	}
	
	/**
	 * Collide into the base
	 * 
	 * @param base
	 * @param object
	 */
	public void baseCollision(Base base, AbstractObject object) {
		if (object.getClass() == Ship.class) {
			Ship ship = (Ship) object;
			
			if (ship.getTeamName().equalsIgnoreCase(base.getTeamName())) {
				// deposit the resources
				base.addResources(ship.getResources());
				ship.resetResources();
				
				// deposit the flag (if there is one)
				if (ship.isCarryingFlag()) {
					base.addFlag(ship.getFlag());
					ship.depositFlag();
				}

				// deposit any AI Cores
				base.incrementCores(ship.getNumCores());
				ship.resetAiCores();

				// heal the ship 
				double origEnergy = ship.getEnergy();
				ship.updateEnergy(base.getHealingEnergy());
				double energyChange = ship.getEnergy() - origEnergy;
				base.updateEnergy(-(int)energyChange);
				//System.out.println("ship " + ship.getTeamName() + ship.getId() + " left resourcesAvailable at base and now has resourcesAvailable " + ship.getMoney());
			}
		} else if(object.getClass() == Drone.class) {//herr0861 edit
			Drone drone = (Drone) object;
			if (drone.getTeamName().equalsIgnoreCase(base.getTeamName())) {
				base.addResources(drone.getResources());
				drone.resetResources();
				
				if (drone.isCarryingFlag()) {
					base.addFlag(drone.getFlag());
					drone.depositFlag();
				}
				
				//deposit AI Cores
				base.incrementCores(drone.getNumCores());
				drone.resetAiCores();
				
				//heal the drone
				double origEnergy = drone.getEnergy();
				drone.updateEnergy(base.getHealingEnergy());
				double energyChange = drone.getEnergy() - origEnergy;
				base.updateEnergy(-(int)(energyChange));
			}
		}
	}
	

	
	/**
	 * Elastically collide all objects following the vector formulation found at:
	 * 
	 * http://www.vobarian.com/collisions/2dcollisions2.pdf
	 * 
	 * @param object1 first object in the collision
	 * @param object2 second object in the collision
	 * @param space handle to space for distance calculations
	 */
	private void elasticCollision2D(AbstractObject object1,
			AbstractObject object2, Toroidal2DPhysics space) {

		// handle overlapping objects
		adjustCentersAtCollision(object1, object2, space);
		
		// get the masses
		double m1 = object1.getMass();
		double m2 = object2.getMass();
		
		// now get the vector from the first to the second, get the unit normal and tangent
		Vector2D distanceVec = space.findShortestDistanceVector(object1.getPosition(), object2.getPosition());