abstract public class AbstractAction {
	/**
	 * The weapon for this time step
	 */
	AbstractWeapon weapon;
	
	/**
	 * The game search (used only for the gaming asteroids)
	 */
	AbstractGameAgent gameSearch;

	/**
	 * All actions must return a movement in (x,y) and orientation space.
	 * If the ship is not moving, simply return DoNothingAction.
	 *   
	 * @return
	 */
	abstract public Movement getMovement(Toroidal2DPhysics space, Ship ship);
	
	/**
	 * All actions must return a movement in (x,y) and orientation space.
	 * If the ship is not moving, simply return DoNothingAction.
	 *   
	 * @return
	 */
	abstract public Movement getMovement(Toroidal2DPhysics space, Drone drone);
	
	/**
	 * Calls the abstract getMovement with supplied timeout for response. If
	 * nothing is returned in that time, simple return DoNothingAction (new Movement())
	 *   
	 * @return
	 */
	public Movement getMovement(Toroidal2DPhysics space, Ship ship, int timeout){
		ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<Movement> future = executor.submit(new SpacewarActionCallable(this, space, ship));
        
        Movement movement = null;
        try {
            //start
        	movement = future.get(timeout, TimeUnit.MILLISECONDS);
            //finished in time
        } catch (TimeoutException e) {
            //was terminated
        	//return no movement
        	movement = new Movement();
        } catch (InterruptedException e) {
        	//we were interrupted (should not happen but lets be good programmers) 
        	//return no movement
        	movement = new Movement();
			e.printStackTrace();
		} catch (ExecutionException e) {
			//the executor threw and exception (should not happen but lets be good programmers) 
			//return no movement
        	movement = new Movement();
		}

        executor.shutdownNow();
        
        return movement;
	}
	
	/**
	 * Calls the abstract getMovement with supplied timeout for response. If
	 * nothing is returned in that time, simple return DoNothingAction (new Movement())
	 *   
	 * @return
	 */
	public Movement getMovement(Toroidal2DPhysics space, Drone drone, int timeout){
		ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<Movement> future = executor.submit(new SpacewarActionCallable(this, space, drone));
        
        Movement movement = null;
        try {
            //start
        	movement = future.get(timeout, TimeUnit.MILLISECONDS);
            //finished in time
        } catch (TimeoutException e) {
            //was terminated
        	//return no movement
        	movement = new Movement();
        } catch (InterruptedException e) {
        	//we were interrupted (should not happen but lets be good programmers) 
        	//return no movement
        	movement = new Movement();
			e.printStackTrace();
		} catch (ExecutionException e) {
			//the executor threw and exception (should not happen but lets be good programmers) 
			//return no movement
        	movement = new Movement();
		}

        executor.shutdownNow();
        
        return movement;
	}
	
	/**
	 * Each action returns true when it completes.  Some actions
	 * take more than one timestep to complete.  Others only take one.
	 * @return
	 */
	abstract public boolean isMovementFinished(Toroidal2DPhysics space);

	/**
	 * Inner class used to help ensure no one goes over on time
	 * @author amy
	 *
	 */
	class SpacewarActionCallable implements  Callable<Movement>{
		private AbstractAction action;
		private Toroidal2DPhysics space;
		private Ship ship;
		private Drone drone;
		
		SpacewarActionCallable(AbstractAction action, Toroidal2DPhysics space, Ship ship){
			this.action = action;
			this.space = space;
			this.ship = ship;
		}
		SpacewarActionCallable(AbstractAction action, Toroidal2DPhysics space, Drone drone){//herr0861 edit - Overload to handle multiple object types
			this.action = action;
			this.space = space;
			this.drone = drone;
		}
		
		public Movement call() throws Exception {
			if(this.action != null && this.ship != null && this.space != null){
				return this.action.getMovement(this.space, this.ship);
			} else if (this.action != null && this.drone != null && this.space != null) {//herr0861edit
				return this.action.getMovement(this.space, this.drone);
			} else {
				//something went wrong...lets return no movement
				System.out.println("Error, no movement");
				return new Movement();
			}
			
		}
	}
} public class DoNothingAction extends AbstractAction {
	/**
	 * This action never moves the ship
	 */
	public Movement getMovement(Toroidal2DPhysics space, Ship ship) {
		return new Movement();
	}
	
	/**
	 * This action never moves the drone
	 * Herr0861 edit
	 * This is an overloaded method to allow handling of an additional class. In this case, Drone.
	 */
	public Movement getMovement(Toroidal2DPhysics space, Drone drone) {
		return new Movement();
	}

	/**
	 * This action never moves the ship so it is always done immediately
	 */
	public boolean isMovementFinished(Toroidal2DPhysics space) {
		return true;
	}

} public class PurchaseCosts {
	Map<PurchaseTypes, ResourcePile> costs;

	/**
	 * Create an empty purchase cost object (costs set to fixed initial values)
	 */
	public PurchaseCosts() {
		super();
		costs = new HashMap<PurchaseTypes, ResourcePile>();
		reset();
	}

	/**
	 * Put the costs back to their initial values
	 */
	public void reset() {
		costs.put(PurchaseTypes.BASE, new ResourcePile(250, 300, 450));
		costs.put(PurchaseTypes.SHIP, new ResourcePile(500, 1000, 500));
		costs.put(PurchaseTypes.CORE, new ResourcePile(400, 850, 400));//herr0861 edit (see below comment)
		costs.put(PurchaseTypes.DRONE, new ResourcePile(100,100,100));
		costs.put(PurchaseTypes.NOTHING, new ResourcePile(0,0,0));
		costs.put(PurchaseTypes.POWERUP_DOUBLE_BASE_HEALING_SPEED, new ResourcePile(1000,1000,500));
		costs.put(PurchaseTypes.POWERUP_DOUBLE_MAX_ENERGY, new ResourcePile(750, 1000, 500));
		costs.put(PurchaseTypes.POWERUP_DOUBLE_WEAPON_CAPACITY, new ResourcePile(250, 1000, 1000));
		costs.put(PurchaseTypes.POWERUP_EMP_LAUNCHER, new ResourcePile(0, 750, 250));
		costs.put(PurchaseTypes.POWERUP_SHIELD, new ResourcePile(0,1500,500));
		
		/*
		 * The major expense of the drone should be the core. Compete gets lots of cores, but this serves little purpose there.
		 * The Core plus the drone costs about as much as a ship. I honestly think it might be too expensive, but we can play with the value.
		 */
	}
	
	/**
	 * Make a copy of the current costs (for security)
	 * 
	 * @return a copy of the current costs
	 */
	public PurchaseCosts deepCopy() {
		PurchaseCosts newCosts = new PurchaseCosts();
		
		for (PurchaseTypes type : costs.keySet()) {
			newCosts.costs.put(type, new ResourcePile(costs.get(type)));
		}
		return newCosts;
	}
	
	
	/**
	 * Get the cost of the specified type of item
	 * @param type the type of item
	 * @return the cost in resources
	 */
	public ResourcePile getCost(PurchaseTypes type) {
		return costs.get(type);
	}
	
	/**
	 * Double the cost of the specified item (team made a purchase)
	 * 
	 * @param type
	 */
	public void doubleCosts(PurchaseTypes type) {
		costs.get(type).doubleCosts();
	}
	
	/**
	 * Checks if the specified item type can be afforded to be purchased
	 * 
	 * @param type the type of item to purchase
	 * @param resources the current pile of resources
	 * @return true if the item can be afforded and false otherwise
	 */
	public boolean canAfford(PurchaseTypes type, ResourcePile resources) {
		if (resources.greaterThan(costs.get(type))) {
			return true;
		} else {
			return false;
		}
	}

} public enum PurchaseTypes {
	BASE(),
	SHIP(),
	POWERUP_SHIELD(SpaceSettlersPowerupEnum.TOGGLE_SHIELD),
	POWERUP_EMP_LAUNCHER(SpaceSettlersPowerupEnum.FIRE_EMP),
	POWERUP_DOUBLE_BASE_HEALING_SPEED(SpaceSettlersPowerupEnum.DOUBLE_BASE_HEALING_SPEED),
	POWERUP_DOUBLE_MAX_ENERGY(SpaceSettlersPowerupEnum.DOUBLE_MAX_ENERGY),
	POWERUP_DOUBLE_WEAPON_CAPACITY(SpaceSettlersPowerupEnum.DOUBLE_WEAPON_CAPACITY),
	CORE(),
	DRONE(),
	NOTHING();

	/**
	 * Map to the power up (if it exists)
	 */
	SpaceSettlersPowerupEnum powerupMap;
	
	PurchaseTypes() {
		this.powerupMap = null;
	}

	PurchaseTypes(SpaceSettlersPowerupEnum powerupMap) {
		this.powerupMap = powerupMap;
	}

	public SpaceSettlersPowerupEnum getPowerupMap() {
		return powerupMap;
	}
} 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;
	}
	

} abstract public class AbstractActionableObject extends AbstractObject {
	/**
	 * Maximum number of bullets (or other weapons) simultaneously in the simulator.  
	 * This keeps ships from simply firing at every step 
	 */
	public static final int INITIAL_WEAPON_CAPACITY = 5;
	
	/** Maximum energy for any tags (if it goes above this we drop any tags) */
	public static final int TAG_MAX_ENERGY = 1500;

	/**
	 * Does the item have a shield and is it using it?
	 */
	boolean isShielded;
	
	/**
	 * The set of current power ups for this agent
	 */
	Set<SpaceSettlersPowerupEnum> currentPowerups;
	
    /**
     * Actionable objects all have energy (which power ups and moving depletes)
     */
    int energy, maxEnergy;
    
    /**
     * Actionable objects have a weapon capacity (which can be changed using power ups)
     */
    int weaponCapacity;
    
    /**
     * Number of steps left that this ship is frozen (not frozen if less than or equal to 0)
     */
    int freezeCount;
    
	/**
	 * The name of the team this ship belongs to
	 */
	String teamName;
	
	/**
	 * Hits and killsInflicted and damageInflicted for this object
	 */
	int hitsInflicted, killsInflicted, damageInflicted, damageReceived, killsReceived, assistsInflicted;

	/**
	 * Reference to the last two teams that shot this ship (keep one for kill and one for assist)
	 * These tags drop when the health goes back up and are only added if health is low enough.
	 * 
	 */
	Ship killTagTeam;
	Ship assistTagTeam;
	int healthAtKillTag;
	int healthAtAssistTag;


	/**
	 * Call the super constructor on objects
	 * @param mass
	 * @param radius
	 * @param position
	 */
	public AbstractActionableObject(int mass, int radius, Position position) {
		super(mass, radius, position);
		currentPowerups = new LinkedHashSet<SpaceSettlersPowerupEnum>();
		weaponCapacity = INITIAL_WEAPON_CAPACITY;
		hitsInflicted = killsInflicted = damageInflicted = damageReceived = killsReceived = assistsInflicted = 0;
	}

	/**
	 * Call the super constructor on objects
	 * @param mass
	 * @param radius
	 */
	public AbstractActionableObject(int mass, int radius) {
		super(mass, radius);
		currentPowerups = new LinkedHashSet<SpaceSettlersPowerupEnum>();
	}
	
	/**
	 * Get the current weapon capacity
	 * @return
	 */
	public int getWeaponCapacity() {
		return weaponCapacity;
	}

	/**
	 * Set the new one (called inside power ups)
	 * @param weaponCapacity
	 */
	public void setWeaponCapacity(int weaponCapacity) {
		this.weaponCapacity = weaponCapacity;
	}

	/**
	 * Is this a valid power up on this actionable object?
	 * 
	 * @param powerup
	 * @return
	 */
	public boolean isValidPowerup(SpaceSettlersPowerupEnum powerup) {
		if (currentPowerups.contains(powerup)) {
			return true;
		} else {
			return false;
		}
	}
	
	

	/**
	 * Add the power up to the current list of things this item can use
	 * @param powerup
	 */
	public void addPowerup(SpaceSettlersPowerupEnum powerup) {
		currentPowerups.add(powerup);
	}
	
	/**
	 * When an item dies, its power ups disappear
	 */
	public void resetPowerups() {
		currentPowerups.clear();
	}
	
	/**
	 * Remove the current power up from the set of allowed ones
	 * @param powerup
	 */
	public void removePowerup(SpaceSettlersPowerupEnum powerup) {
		currentPowerups.remove(powerup);
	}
	
	/**
	 * Is the object currently shielded
	 * @return
	 */
	public boolean isShielded() {
		return isShielded;
	}

	/**
	 * Set the shielding
	 * @param isShielded
	 */
	public void setShielded(boolean isShielded) {
		this.isShielded = isShielded;
	}

	/**
	 * Return the amount of energy
	 * @return
	 */
	public double getEnergy() {
		return energy;
	}
	
	/**
	 * Return the maximum energy this base can hold
	 * @return
	 */
	public int getMaxEnergy() {
		return maxEnergy;
	}
	
	/**
	 * Set the max energy (used by power ups)
	 * @param maxEnergy
	 */
	public void setMaxEnergy(int maxEnergy) {
		this.maxEnergy = maxEnergy;
	}

	/**
	 * Update the energy of the ship.  If it falls below 0, mark the ship as dead
	 * 
	 * @param difference
	 */
	abstract public void updateEnergy(int difference);

	/**
	 * Get the number of steps it is frozen.  Not frozen
	 * if less than or equal to 0.
	 * @return
	 */
	public int getFreezeCount() {
		return freezeCount;
	}

	/**
	 * Set the freeze count 
	 * @param freezeCount
	 */
	public void setFreezeCount(int freezeCount) {
		this.freezeCount = freezeCount;
		if (freezeCount > 0) {
			super.isControllable = false;
		}
	}
	
	/**
	 * Decrement the freeze count (if needed)
	 * and set the ship back to controllable when it gets to 0
	 */
	public void decrementFreezeCount() {
		freezeCount--;
		if (freezeCount <= 0) {
			super.isControllable = true;
		}
	}

	/**
	 * @param teamName the teamName to set
	 */
	public void setTeamName(String teamName) {
		this.teamName = teamName;
	}


	/**
	 * @return the teamName
	 */
	public String getTeamName() {
		return teamName;
	}

	/**
	 * Get the hitsInflicted this object has made
	 * @return the hitsInflicted this object has made
	 */
	public int getHitsInflicted() {
		return hitsInflicted;
	}
	
	/**
	 * increment the hitsInflicted for this ship
	 */
	public void incrementHitsInflicted() {
		this.hitsInflicted++;
	}

	/**
	 * Get the killsInflicted this object has made
	 * @return the killsInflicted this object has made
	 */
	public int getKillsInflicted() {
		return killsInflicted;
	}
	
	/**
	 * Return the total assists this object did
	 * @return
	 */
	public int getTotalAssistsInflicted() {
		return assistsInflicted;
	}

	/**
	 * Get the killsReceived this object has made
	 * @return the killsReceived this object has made
	 */
	public int getKillsReceived() {
		return killsReceived;
	}

	/**
	 * increment the kill for this object
	 */
	public void incrementKillsInflicted() {
		this.killsInflicted++;
	}

	/**
	 * increment the assists for this object
	 */
	public void incrementAssistsInflicted() {
		this.assistsInflicted++;
	}

	/**
	 * increment the kill received for this object
	 */
	public void incrementKillsReceived() {
		this.killsReceived++;
	}

	/**
	 * Get the damageInflicted this object has made
	 * @return the damageInflicted this object has made
	 */
	public int getDamageInflicted() {
		return damageInflicted;
	}

	/**
	 * increment the damageReceived for this object
	 */
	public void incrementDamageReceived(int increment) {
		this.damageReceived += increment;
	}
	
	/**
	 * Get the damageReceived this object has made
	 * @return the damageReceived this object has gotten
	 */
	public int getDamageReceived() {
		return damageReceived;
	}

	/**
	 * increment the damageInflicted for this ship
	 */
	public void incrementDamageInflicted(int increment) {
		this.damageInflicted += increment;
	}
	

	/**
	 * Get the current set of powerups for the object
	 * 
	 * @return current powerups (as a set, there is no order)
	 */
	public Set<SpaceSettlersPowerupEnum> getCurrentPowerups() {
		return currentPowerups;
	}
	
	/**
	 * Tag the current shooter in case of kill 
	 * 
	 * @param teamName
	 */
	public void tagShooter(Ship firingShip) {
		if (killTagTeam != null) {
			// if there was already a kill tag team, move it to assist
			assistTagTeam = killTagTeam;
			healthAtAssistTag = healthAtKillTag;
		}
		// current team that shot becomes the kill team
		killTagTeam = firingShip;
		healthAtKillTag = energy;
	}

	public Ship getKillTagTeam() {
		return killTagTeam;
	}

	public Ship getAssistTagTeam() {
		return assistTagTeam;
	}		
	
	public void updateTags() {
		if (this.getEnergy() > TAG_MAX_ENERGY) {
			this.killTagTeam = null;
			this.assistTagTeam = 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 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 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 enum SpaceSettlersPowerupEnum {
	TOGGLE_SHIELD,
	FIRE_EMP,
	LAY_MINE,
	FIRE_TURRET,
	FIRE_MISSILE,
	FIRE_HEAT_SEEKING_MISSILE,
	DOUBLE_BASE_HEALING_SPEED,
	DOUBLE_MAX_ENERGY,
	DOUBLE_WEAPON_CAPACITY,
	DRONE, //herr0861 edit
	CORE,
} public class Toroidal2DPhysics {
	/**
	 * Height and width of the simulation
	 */
	int height, width;

	float halfHeight, halfWidth;

	/**
	 * All objects in the space
	 */
	Set<AbstractObject> allObjects;

	/**
	 * The list of beacons
	 */
	Set<Beacon> beacons;

	/**
	 * The list of asteroids
	 */
	Set<Asteroid> asteroids;

	/**
	 * The list of current AI Cores
	 */
	Set<AiCore> cores;

	/**
	 * The list of bases
	 */
	Set<Base> bases;

	/**
	 * The list of ships
	 */
	Set<Ship> ships;

	/**
	 * The list of drones
	 */
	Set<Drone> drones;

	/**
	 * The ets of flags
	 */
	Set<Flag> flags;

	/**
	 * List of all weapons currently in play
	 */
	Set<AbstractWeapon> weapons;

	/**
	 * A hashmap of objects by their ID
	 */
	HashMap<UUID, AbstractObject> objectsById;

	/**
	 * The timestep used for simulation of physics
	 */
	double timeStep;

	/**
	 * The current timestep iteration
	 */
	int currentTimeStep;

	/**
	 * Maximum velocities (to keep things from going nuts)
	 */
	public static final double MAX_TRANSLATIONAL_VELOCITY = 200;
	public static final double MAX_ANGULAR_VELOCITY = Math.PI;
	public static final double ENERGY_PENALTY = 0.0005;

	/**
	 * Handles collisions between spacewar objects
	 */
	CollisionHandler collisionHandler;

	/**
	 * Maximum time step
	 */
	int maxTime;

	/**
	 * Information on all of the teams for sharing (set each time step)
	 */
	Set<ImmutableTeamInfo> teamInfo;

	/**
	 * Constructor for the regular game
	 * 
	 * @param simConfig
	 */
	public Toroidal2DPhysics(SpaceSettlersConfig simConfig) {
		height = simConfig.getHeight();
		width = simConfig.getWidth();
		halfHeight = height / 2.0f;
		halfWidth = width / 2.0f;
		allObjects = new LinkedHashSet<AbstractObject>();
		timeStep = simConfig.getSimulationTimeStep();
		collisionHandler = new CollisionHandler();
		beacons = new LinkedHashSet<Beacon>();
		asteroids = new LinkedHashSet<Asteroid>();
		bases = new LinkedHashSet<Base>();
		ships = new LinkedHashSet<Ship>();
		drones = new LinkedHashSet<Drone>(); // herr0861 edit
		cores = new LinkedHashSet<AiCore>();
		flags = new LinkedHashSet<Flag>();
		weapons = new LinkedHashSet<AbstractWeapon>();
		objectsById = new HashMap<UUID, AbstractObject>();
		maxTime = simConfig.getSimulationSteps();
		teamInfo = new LinkedHashSet<ImmutableTeamInfo>();
	}

	/**
	 * Constructor for unit tests
	 * 
	 * @param height
	 * @param width
	 * @param timeStep
	 */
	public Toroidal2DPhysics(int height, int width, double timeStep) {
		super();
		this.height = height;
		this.width = width;
		this.timeStep = timeStep;
		halfHeight = height / 2.0f;
		halfWidth = width / 2.0f;
		allObjects = new LinkedHashSet<AbstractObject>();
		collisionHandler = new CollisionHandler();
		beacons = new LinkedHashSet<Beacon>();
		asteroids = new LinkedHashSet<Asteroid>();
		bases = new LinkedHashSet<Base>();
		ships = new LinkedHashSet<Ship>();
		drones = new LinkedHashSet<Drone>(); // herr0861 edit
		flags = new LinkedHashSet<Flag>();
		cores = new LinkedHashSet<AiCore>();
		weapons = new LinkedHashSet<AbstractWeapon>();
		objectsById = new HashMap<UUID, AbstractObject>();
		teamInfo = new LinkedHashSet<ImmutableTeamInfo>();
	}

	/**
	 * Make a shallow copy of the space with just the settings copied over and new
	 * array lists created.
	 *
	 * This is used by the cloning (and should not be called otherwise)
	 * 
	 * @param other
	 */
	private Toroidal2DPhysics(Toroidal2DPhysics other) {
		super();
		this.height = other.height;
		this.width = other.width;
		this.timeStep = other.timeStep;
		this.currentTimeStep = other.currentTimeStep;
		halfHeight = height / 2.0f;
		halfWidth = width / 2.0f;
		allObjects = new LinkedHashSet<AbstractObject>();
		collisionHandler = new CollisionHandler();
		beacons = new LinkedHashSet<Beacon>();
		asteroids = new LinkedHashSet<Asteroid>();
		bases = new LinkedHashSet<Base>();
		ships = new LinkedHashSet<Ship>();
		drones = new LinkedHashSet<Drone>(); // herr0861 edit
		flags = new LinkedHashSet<Flag>();
		cores = new LinkedHashSet<AiCore>();
		weapons = new LinkedHashSet<AbstractWeapon>();
		objectsById = new HashMap<UUID, AbstractObject>();
		maxTime = other.maxTime;
		teamInfo = new LinkedHashSet<ImmutableTeamInfo>(other.teamInfo);
	}

	/**
	 * Add an object to the physics simulation
	 * 
	 * @param obj
	 */
	public void addObject(AbstractObject obj) {
		allObjects.add(obj);

		if (obj instanceof Beacon) {
			beacons.add((Beacon) obj);
		}

		if (obj instanceof AiCore) {
			cores.add((AiCore) obj);
		}

		if (obj instanceof Asteroid) {
			asteroids.add((Asteroid) obj);
		}

		if (obj instanceof Base) {
			bases.add((Base) obj);
		}

		if (obj instanceof Ship) {
			ships.add((Ship) obj);
		}

		if (obj instanceof AbstractWeapon) {
			weapons.add((AbstractWeapon) obj);
		}

		if (obj instanceof Drone) { // herr0861 edit
			drones.add((Drone) obj);
		}

		if (obj instanceof Flag) {
			flags.add((Flag) obj);
		}

		objectsById.put(obj.getId(), obj);
	}

	/**
	 * Delete an object from the physics simulation
	 * 
	 * @param obj
	 */
	public void removeObject(AbstractObject obj) {
		allObjects.remove(obj);

		if (obj.getClass() == Beacon.class) {
			beacons.remove((Beacon) obj);
		}

		if (obj.getClass() == Asteroid.class) {
			asteroids.remove((Asteroid) obj);
		}

		if (obj.getClass() == AiCore.class) {
			cores.remove((AiCore) obj);
		}

		if (obj.getClass() == Base.class) {
			bases.remove((Base) obj);
		}

		if (obj.getClass() == Ship.class) {
			ships.remove((Ship) obj);
		}

		if (obj instanceof AbstractWeapon) {
			weapons.remove((AbstractWeapon) obj);
		}

		if (obj instanceof Drone) {// herr0861 edit
			drones.remove((Drone) obj);
		}

		if (obj instanceof Flag) {
			flags.remove((Flag) obj);
		}

		objectsById.remove(obj.getId());
	}

	/**
	 * return object by its ID
	 * 
	 * @param id
	 * @return
	 */
	public AbstractObject getObjectById(UUID id) {
		return objectsById.get(id);
	}

	/**
	 * Return the list of asteroids
	 * 
	 * @return
	 */
	public Set<Asteroid> getAsteroids() {
		return asteroids;
	}

	/**
	 * Return the list of beacons
	 * 
	 * @return
	 */
	public Set<Beacon> getBeacons() {
		return beacons;
	}

	/**
	 * Return the list of bases
	 * 
	 * @return
	 */
	public Set<Base> getBases() {
		return bases;
	}

	/**
	 * Return the list of ships
	 * 
	 * @return
	 */
	public Set<Ship> getShips() {
		return ships;
	}

	/**
	 * Return the list of cores currently in play
	 * 
	 * @return Set of AiCores
	 */
	public Set<AiCore> getCores() {
		return cores;
	}

	/**
	 * Return a list of weapons currently in play
	 * 
	 * @return
	 */
	public Set<AbstractWeapon> getWeapons() {
		return weapons;
	}

	/**
	 * Return a list of drones currently in play
	 * 
	 * @return
	 */
	public Set<Drone> getDrones() { // herr0861 edit
		return drones;
	}

	/**
	 * Return the list of flags currently in play
	 * 
	 * @return
	 */
	public Set<Flag> getFlags() {
		return flags;
	}

	/**
	 * Return the Environment height
	 * 
	 * @return
	 */
	public int getHeight() {
		return height;
	}

	/**
	 * Return the Environment width
	 * 
	 * @return
	 */
	public int getWidth() {
		return width;
	}

	/**
	 * Return the timestep duration
	 * 
	 * @return
	 */
	public double getTimestepDuration() {
		return timeStep;
	}

	/**
	 * Return the timestep
	 * 
	 * @return
	 */
	public int getCurrentTimestep() {
		return currentTimeStep;
	}

	/**
	 * Returns a new random free location in space
	 * 
	 * @param rand   Random number generator
	 * @param radius the radius around the new location that must be free
	 * @return
	 */
	public Position getRandomFreeLocation(Random rand, int radius) {
		Position randLocation = new Position(rand.nextFloat() * width, rand.nextFloat() * height);

		while (!isLocationFree(randLocation, radius)) {
			randLocation = new Position(rand.nextFloat() * width, rand.nextFloat() * height);
		}

		return randLocation;
	}

	/**
	 * Returns a new random free location in space
	 * 
	 * @param rand       Random number generator
	 * @param freeRadius the radius around the object that must be free
	 * @return
	 */
	public Position getRandomFreeLocationInRegion(Random rand, int freeRadius, int centerX, int centerY,
			double maxDistance) {
		Position centerPosition = new Position(centerX, centerY);
		double newX = ((2 * rand.nextDouble()) - 1) * maxDistance + centerX;
		double newY = ((2 * rand.nextDouble()) - 1) * maxDistance + centerY;
		Position randLocation = new Position(newX, newY);
		toroidalWrap(randLocation);

		while (!isLocationFree(randLocation, freeRadius)
				|| findShortestDistance(centerPosition, randLocation) > maxDistance) {
			newX = ((2 * rand.nextDouble()) - 1) * maxDistance + centerX;
			newY = ((2 * rand.nextDouble()) - 1) * maxDistance + centerY;
			randLocation = new Position(newX, newY);
			toroidalWrap(randLocation);
		}

		return randLocation;
	}

	/**
	 * Returns a new random free location inside the specified box of space
	 * 
	 * @param rand Random number generator
	 * @return
	 */
	public Position getRandomFreeLocationInRegion(Random rand, int freeRadius, int ULX, int ULY, int LRX, int LRY) {
		int boxWidth = LRX - ULX;
		int boxHeight = LRY - ULY;

		// System.out.println("Making a random location inside UL (x,y) " + ULX + ", " +
		// ULY +
		// " to LR (x,y) " + LRY + ", " + LRY);

		Position centerPosition = new Position(boxWidth / 2 + ULX, boxHeight / 2 + ULY);
		// System.out.println("Center position is " + centerPosition);
		double newX = ((2 * rand.nextDouble()) - 1) * (boxWidth / 2.0) + centerPosition.getX();
		double newY = ((2 * rand.nextDouble()) - 1) * (boxHeight / 2.0) + centerPosition.getY();
		Position randLocation = new Position(newX, newY);
		toroidalWrap(randLocation);

		while (!isLocationFree(randLocation, freeRadius)) {
			newX = ((2 * rand.nextDouble()) - 1) * (boxWidth / 2.0) + centerPosition.getX();
			newY = ((2 * rand.nextDouble()) - 1) * (boxHeight / 2.0) + centerPosition.getY();
			randLocation = new Position(newX, newY);
			toroidalWrap(randLocation);
		}

		// System.out.println("random location chosen is " + randLocation);
		return randLocation;
	}

	/**
	 * Is the specified location free (within the specified radius)?
	 * 
	 * @param location
	 * @param radius
	 * @return true if the location is free and false otherwise
	 */
	public boolean isLocationFree(Position location, int radius) {
		for (AbstractObject object : allObjects) {
			// fixed bug where it only checked radius and not diameter
			if (findShortestDistanceVector(object.getPosition(), location)
					.getMagnitude() <= (radius + (2 * object.getRadius()))) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Public interface to find the shortest toroidal distance from one location to
	 * another. Returns a vector pointing from location1 to location2. Use a
	 * getMagnitude() call on the vector to get the distance.
	 * 
	 * @param location1
	 * @param location2
	 * @return shortest distance vector pointing from location1 to location2
	 */
	public Vector2D findShortestDistanceVector(Position location1, Position location2) {
		return findShortestDistanceVector(location1, location2, width, height, halfWidth, halfHeight);
	}

	/**
	 * Public interface to find the shortest toroidal distance from one location to
	 * another. Returns a double (the distance). Use findShortestDistanceVector to
	 * get the vector telling you which way to move along this path. Useful if you
	 * just care about distance.
	 * 
	 * @param location1
	 * @param location2
	 * @return shortest distance length (magnitude of the vector pointing from
	 *         location1 to location2)
	 */
	public double findShortestDistance(Position location1, Position location2) {
		Vector2D shortDist = findShortestDistanceVector(location1, location2, width, height, halfWidth, halfHeight);
		return shortDist.getMagnitude();
	}

	/**
	 * Finds the shortest distance in toroidal space. Returns a vector pointing from
	 * the start to the target location and getMagnitude can be used to find the
	 * distance and the angle.
	 * 
	 * @param location1
	 * @param location2
	 * @param width
	 * @param height
	 * @param halfWidth
	 * @param halfHeight
	 * @return
	 */
	private Vector2D findShortestDistanceVector(Position location1, Position location2, float width, float height,
			float halfWidth, float halfHeight) {
		double x = location2.getX() - location1.getX();
		double y = location2.getY() - location1.getY();
		if (x > halfWidth) {
			if (y > halfHeight) {
				return new Vector2D(x - width, y - height);
			} else if (y < -halfHeight) {
				return new Vector2D(x - width, y + height);
			} else {
				return new Vector2D(x - width, y);
			}
		} else if (x < -halfWidth) {
			if (y > halfHeight) {
				return new Vector2D(x + width, y - height);
			} else if (y < -halfHeight) {
				return new Vector2D(x + width, y + height);
			} else {
				return new Vector2D(x + width, y);
			}
		} else if (y > halfHeight) {
			return new Vector2D(x, y - height);
		} else if (y < -halfHeight) {
			return new Vector2D(x, y + height);
		} else {
			return new Vector2D(x, y);
		}
	}

	/**
	 * Move all moveable objects and handle power ups.
	 */
	public void advanceTime(Random rand, int currentTimeStep, Map<UUID, SpaceSettlersPowerupEnum> powerups) {

		this.currentTimeStep = currentTimeStep;

		// heal any base injuries
		for (Base base : bases) {
			base.updateEnergy(base.getHealingIncrement());
		}

		// detect collisions across all objects
		detectCollisions();

		// get the power ups and create any objects (weapons) as necessary
		for (UUID key : powerups.keySet()) {
			AbstractObject swobject = getObjectById(key);
			// if the object is not alive or it is not actionable or is a drone, then ignore
			// this
			if (!swobject.isAlive() || (!(swobject instanceof AbstractActionableObject))
					|| (swobject instanceof Drone)) {// herr0861 edit
				continue;
			}

			// otherwise, handle the power up
			handlePowerup((AbstractActionableObject) swobject, powerups.get(key));
		}

		// now move all objects that are moveable (which may include weapons)
		for (AbstractObject object : allObjects) {
			// skip non-moveable objects or dead object
			if (!object.isMoveable() || !object.isAlive()) {
				continue;
			}

			Position currentPosition = object.getPosition();

			if (object instanceof Drone) { // herr0861 edit
				Drone drone = (Drone) object;
				if (drone.getCurrentAction() == null) {
					drone.setCurrentAction(this.deepClone());
				}

				AbstractAction action = drone.getCurrentAction();

				// AbstractAction action = drone.getDroneAction(this.deepClone());

				if (action == null) {
					action = new DoNothingAction(); // This should never happen, but I'll keep it just in case.
					System.out.println("Drone doing nothing!");

				}

				/*
				 * Don't actually need to clone this since the user can't modify it, but might
				 * as well to be consistent because maybe in the future people will be allowed
				 * to specify behavior for the drone.
				 */

				Movement actionMovement = action.getMovement(this.deepClone(), drone.deepClone());

				Position newPosition = applyMovement(currentPosition, actionMovement, timeStep);

				if (newPosition.isValid()) {
					drone.setPosition(newPosition);
				} else {
					newPosition = currentPosition;
				}

				// spend ship energy proportional to its acceleration (old formula used
				// velocity) and mass (new for space settlers
				// since resources cost mass) based on update to position (used
				// to be based on movement command, no result)
				// double penalty = ENERGY_PENALTY *
				// -Math.abs(ship.getPosition().getTotalTranslationalVelocity());
				double angularAccel = Math.abs(currentPosition.getAngularVelocity() - newPosition.getAngularVelocity())
						/ timeStep;
				double angularInertia = (3.0 * drone.getMass() * drone.getRadius() * angularAccel) / 2.0;
				//double linearAccel = Math.abs(currentPosition.getTranslationalVelocity().getMagnitude()
				//		- newPosition.getTranslationalVelocity().getMagnitude()) / timeStep;
				double linearAccel = currentPosition.getTranslationalVelocity().subtract(newPosition.getTranslationalVelocity()).getMagnitude() / timeStep;
				double linearInertia = drone.getMass() * linearAccel;
				int penalty = (int) Math.floor(0.7 * ENERGY_PENALTY * (angularInertia + linearInertia));
				drone.updateEnergy(-penalty);

			} else if (object.isControllable() && !(object instanceof Drone)) {

				Ship ship = (Ship) object;
				AbstractAction action = ship.getCurrentAction();

				// handle a null action
				if (action == null) {
					action = new DoNothingAction();
				}

				// need to clone the ship and space because otherwise the ship can affect
				// itself inside AbstractAction
				Movement actionMovement = action.getMovement(this.deepClone(), ship.deepClone());

				Vector2D currentVelocity = currentPosition.getTranslationalVelocity();
				double currentAngularVelocity = currentPosition.getAngularVelocity();
				Position newPosition = applyMovement(currentPosition, actionMovement, timeStep);

				// System.out.println("The old position is: [" + currentPosition + "] and the
				// new position is: [" + newPosition);
				if (newPosition.isValid()) {
					ship.setPosition(newPosition);
				} else {
					newPosition = currentPosition;
				}

				// spend ship energy proportional to its acceleration (old formula used
				// velocity) and mass (new for space settlers
				// since resources cost mass) based on update to position (used
				// to be based on movement command, no result)
				// double penalty = ENERGY_PENALTY *
				// -Math.abs(ship.getPosition().getTotalTranslationalVelocity());
				double angularAccel = Math.abs(currentPosition.getAngularVelocity() - newPosition.getAngularVelocity())
						/ timeStep;
				double angularInertia = (3.0 * ship.getMass() * ship.getRadius() * angularAccel) / 2.0;
				double linearAccel = currentPosition.getTranslationalVelocity().subtract(newPosition.getTranslationalVelocity()).getMagnitude() / timeStep;
				double linearInertia = ship.getMass() * linearAccel;
				int penalty = (int) Math.floor(ENERGY_PENALTY * (angularInertia + linearInertia));
				ship.updateEnergy(-penalty);

				// this isn't the most general fix but it will work for now (also has to be done
				// for bases)
				if (ship.isShielded()) {
					ship.updateEnergy(-PowerupToggleShield.SHIELD_STEP_COST);
				}

				// if (!ship.isAlive()) {
				// System.out.println("Ship " + ship.getTeamName() + ship.getId() + " is dead");
				// }

			} else {
				// move all other types of objects
				Position newPosition = moveOneTimestep(currentPosition);
				object.setPosition(newPosition);
				if (object instanceof Flag && Double.isNaN(newPosition.getX())) {
					System.out.println("alive: " + object.isAlive() + " draw: " + object.isDrawable() + " old pos is "
							+ currentPosition + " new position is " + newPosition);
					System.exit(-1);
				}
			}

			// if any ships or bases are frozen, decrement their frozen count
			if (object instanceof AbstractActionableObject && !object.isControllable()) {
				AbstractActionableObject actionable = (AbstractActionableObject) object;
				actionable.decrementFreezeCount();
			}

		}

		// go through and see if any bases have died
		Set<Base> basesClone = new LinkedHashSet<Base>(bases);
		for (Base base : basesClone) {
			// this isn't the most general fix but it will work for now (also has to be done
			// for bases)
			if (base.isShielded()) {
				base.updateEnergy(-PowerupToggleShield.SHIELD_STEP_COST);
			}

			if (!base.isAlive()) {
				// if the base died, increment kill and assist count
				Ship killShip = base.getKillTagTeam();
				Ship assistShip = base.getAssistTagTeam();

				if (killShip != null) killShip.incrementKillsInflicted();
				if (assistShip != null) assistShip.incrementAssistsInflicted();
				base.incrementKillsReceived();
				
				base.setAlive(false);
				removeObject(base);
				base.getTeam().removeBase(base);
			}
		}

		Set<Drone> dronesClone = new LinkedHashSet<Drone>(drones);
		for (Drone drone : dronesClone) {
			if (drone.getEnergy() <= 0 && drone.isAlive() == true) {// drone has died
				// if the drone died, increment kill and assist count
				Ship killShip = drone.getKillTagTeam();
				Ship assistShip = drone.getAssistTagTeam();

				if (killShip != null) killShip.incrementKillsInflicted();
				if (assistShip != null) assistShip.incrementAssistsInflicted();

				
				drone.setDeadAndDropObjects(rand, this); // kill the drone dropping the flag and all resources, but no
															// core. Should we make it have a chance to drop an AiCore?
															// Probably not.
				removeObject(drone);
				drone.getTeam().removeDrone(drone);
			}
		}

		// and see if any ships have died. Doing this here removes unintential side
		// effects
		// from when it was called inside updateEnergy
		for (Ship ship : ships) {
			if (ship.getEnergy() <= 0 && ship.isAlive() == true) {
				// if the drone died, increment kill and assist count
				Ship killShip = ship.getKillTagTeam();
				Ship assistShip = ship.getAssistTagTeam();

				if (killShip != null) killShip.incrementKillsInflicted();
				if (assistShip != null) assistShip.incrementAssistsInflicted();
				
				// mark the ship as having been killed
				ship.incrementKillsReceived();

				
				// Spawn a new AiCore with the same velocity magnitude and direction as its
				// parent ship.
				// handle dropping the core if the ship died
				Position corePosition = this.getRandomFreeLocationInRegion(rand, AiCore.CORE_RADIUS,
						(int) ship.getPosition().getX(), (int) ship.getPosition().getY(), 200);
				corePosition.setTranslationalVelocity(ship.getPosition().getTranslationalVelocity());
				corePosition.setAngularVelocity(ship.getPosition().getAngularVelocity());
				AiCore shipCore = new AiCore(corePosition, ship.getTeamName(), ship.getTeamColor());
				this.addObject(shipCore);

				// drop any resources that the ship was carrying - this was commented out due to
				// creating giant piles of resources
				ResourcePile resources = ship.getResources();

				if (resources.getTotal() > 0) {
					// Position newPosition = ship.getPosition();
					// newPosition.setTranslationalVelocity(new Vector2D(0,0));
					// newPosition.setAngularVelocity(0.0);
					// Asteroid newAsteroid = new Asteroid(newPosition, true, ship.getRadius(),
					// true, resources);
					// this.addObject(newAsteroid);

					// distributeResourcesToNearbyAsteroids(ship.getPosition(), resources);
					// System.out.println("Adding a new asteroid with resources " +
					// newAsteroid.getResources().getTotal() +
					// " due to death, total is " + asteroids.size());
					// System.out.println("Ship died and " + resources.getTotal() + " has been added
					// to an asteroid");
				}

				// set the ship to dead last (so we can grab its resources first)
				// this drops the flag
				ship.setDeadAndDropObjects(rand, this);
			}
		}
		
		// verify all tags are still accurate with the new energies
		for (Base base : bases) {
			base.updateTags();
		}

		for (Ship ship : ships) {
			ship.updateTags();
		}
	}

	/**
	 * Distribute the specified resources to nearby mineable asteroids (this happens
	 * when a ship dies) Right now it drops it on the single nearest asteroid but
	 * that may change if this ends up making massive asteroids
	 * 
	 * @param position
	 * @param resources
	 */
	private void distributeResourcesToNearbyAsteroids(Position position, ResourcePile resources) {
		double nearestDistance = Double.MAX_VALUE;
		Asteroid nearestAsteroid = null;

		// first find the nearest asteroid
		for (Asteroid asteroid : asteroids) {
			double dist = findShortestDistance(position, asteroid.getPosition());
			if (dist < nearestDistance) {
				nearestDistance = dist;
				nearestAsteroid = asteroid;
			}
		}

		// if it is mineable, just add the resources
		nearestAsteroid.addResources(resources);
		if (!nearestAsteroid.isMineable()) {
			// transform it to mineable
			nearestAsteroid.setMineable(true);
		}

	}

	/**
	 * Handle power ups for the specified object
	 * 
	 * @param swobject
	 * @param spacewarPowerup
	 */
	private void handlePowerup(AbstractActionableObject swobject, SpaceSettlersPowerupEnum spacewarPowerup) {
		switch (spacewarPowerup) {
		case FIRE_MISSILE:
			Ship ship = (Ship) swobject;
			AbstractWeapon weapon = ship.getNewWeapon(SpaceSettlersPowerupEnum.FIRE_MISSILE);
			if (weapon != null && weapon.isValidWeapon(ship)) {
				addObject(weapon);
				weapon.setFiringShip(ship);
				weapon.applyPowerup(ship);
			}
			break;

		case FIRE_EMP:
			ship = (Ship) swobject;
			weapon = ship.getNewWeapon(SpaceSettlersPowerupEnum.FIRE_EMP);
			if (weapon != null && weapon.isValidWeapon(ship)) {
				addObject(weapon);
				weapon.setFiringShip(ship);
				weapon.applyPowerup(ship);
			}
			break;

		case TOGGLE_SHIELD:
			PowerupToggleShield toggle = new PowerupToggleShield();
			toggle.applyPowerup(swobject);
			break;

		case DOUBLE_WEAPON_CAPACITY:
			PowerupDoubleWeapon weaponDoubler = new PowerupDoubleWeapon();
			weaponDoubler.applyPowerup(swobject);
			break;

		case DOUBLE_BASE_HEALING_SPEED:
			PowerupDoubleHealingBaseEnergy baseDoubler = new PowerupDoubleHealingBaseEnergy();
			baseDoubler.applyPowerup(swobject);
			break;

		case DOUBLE_MAX_ENERGY:
			PowerupDoubleMaxEnergy maxEnergyDoubler = new PowerupDoubleMaxEnergy();
			maxEnergyDoubler.applyPowerup(swobject);
			break;

		case FIRE_HEAT_SEEKING_MISSILE:
			break;

		case FIRE_TURRET:
			break;

		case LAY_MINE:
			break;

		default:
			break;

		}

	}

	/**
	 * Advances one time step using the set velocities
	 * 
	 * @param currentPosition
	 * @return
	 */
	private Position moveOneTimestep(Position position) {
		double angularVelocity = position.getAngularVelocity();
		double orientation = position.getOrientation() + (angularVelocity * timeStep);
		if (Double.isNaN(angularVelocity) || Double.isNaN(position.getTotalTranslationalVelocity())) {
			System.out.println("Help: velocity got set to Nan " + position);
		}

		// make sure orientation wraps correctly (-pi to pi)
		if (orientation > Math.PI) {
			orientation -= (2 * Math.PI);
		} else if (orientation < -Math.PI) {
			orientation += (2 * Math.PI);
		}

		// new x,y coordinates
		double newX = position.getX() + (position.getTranslationalVelocityX() * timeStep);
		double newY = position.getY() + (position.getTranslationalVelocityY() * timeStep);

		Position newPosition = new Position(newX, newY, orientation);
		newPosition.setAngularVelocity(angularVelocity);
		newPosition.setTranslationalVelocity(position.getTranslationalVelocity());
		toroidalWrap(newPosition);
		return newPosition;
	}

	/**
	 * Step through all the objects and ensure they are not colliding. If they are,
	 * call the collision handler for those objects. Sometimes you bounce
	 * (asteroids) and sometimes you pick the object up (beacons), etc.
	 */
	private void detectCollisions() {
		// would prefer to iterate over the set (as this is inefficient) but
		// the set iterator collides a with b and then b with a, allowing them to
		// pass through one another!
		AbstractObject[] allObjectsArray = (AbstractObject[]) allObjects.toArray(new AbstractObject[allObjects.size()]);

		// loop through all pairs of objects and see if they are colliding
		for (int i = 0; i < allObjectsArray.length; i++) {
			AbstractObject object1 = allObjectsArray[i];
			if (!object1.isAlive()) {
				continue;
			}

			for (int j = i + 1; j < allObjectsArray.length; j++) {
				AbstractObject object2 = allObjectsArray[j];

				if (!object2.isAlive()) {
					continue;
				}

				// skip them if they are the same object
				if (object1.equals(object2)) {
					continue;
				}

				double distance = findShortestDistance(object1.getPosition(), object2.getPosition());

				if (distance < (object1.getRadius() + object2.getRadius())) {
					collisionHandler.collide(object1, object2, this);
				}
			}
		}
	}

	/**
	 * Takes an acceleration and a simulation time step and moves the object
	 * 
	 * @param actionMovement
	 * @param timeStep
	 * @return
	 */
	public Position applyMovement(Position position, Movement movement, double timeStep) {
		Vector2D translationalAcceleration = movement.getTranslationalAcceleration();
		double angularAccel = movement.getAngularAccleration();

		// velocity is acceleration times time
		Vector2D translationalVelocity = position.getTranslationalVelocity().add(new Vector2D(
				translationalAcceleration.getXValue() * timeStep, translationalAcceleration.getYValue() * timeStep));
		double angularVelocity = position.getAngularVelocity() + (angularAccel * timeStep);

		// ensure the max/mins are respected
		if (translationalVelocity.getMagnitude() > MAX_TRANSLATIONAL_VELOCITY) {
			double ratio = translationalVelocity.getMagnitude() / MAX_TRANSLATIONAL_VELOCITY;
			translationalVelocity = translationalVelocity.multiply(1 / ratio);
		}

		angularVelocity = checkAngularVelocity(angularVelocity);

		Position newPosition = new Position(position.getX(), position.getY(), position.getOrientation());
		newPosition.setTranslationalVelocity(translationalVelocity);
		newPosition.setAngularVelocity(angularVelocity);

		return moveOneTimestep(newPosition);
	}

	/**
	 * Ensure the angular velocity doesn't exceed the max
	 * 
	 * @param angularVelocity
	 * @return
	 */
	private double checkAngularVelocity(double angularVelocity) {
		if (angularVelocity > MAX_ANGULAR_VELOCITY) {
			return MAX_ANGULAR_VELOCITY;
		} else if (angularVelocity < -MAX_ANGULAR_VELOCITY) {
			return -MAX_ANGULAR_VELOCITY;
		} else {
			return angularVelocity;
		}
	}

	/**
	 * Torridial wrap based on the height/width of the enviroment
	 * 
	 * @param position
	 */
	public void toroidalWrap(Position position) {
		while (position.getX() < 0) {
			position.setX(position.getX() + width);
		}

		while (position.getY() < 0) {
			position.setY(position.getY() + height);
		}

		position.setX(position.getX() % width);
		position.setY(position.getY() % height);
	}

	/**
	 * Respawns any dead objects in new random locations. Ships have a delay before
	 * they can respawn.  Asteroids do not respawn (they are re-created later randomly)
	 */
	public void respawnDeadObjects(Random random) {
		for (AbstractObject object : allObjects) {
			if (!object.isAlive() && object.canRespawn()) {
				Position newPosition = null;

				// flags should re-spawn at a randomly chosen starting location
				if (object instanceof Flag) {
					Flag flag = (Flag) object;
					newPosition = flag.getNewStartingPosition(random);

					// ensure their starting location is free (to handle the thought bug the class
					// introduced of putting a ship or a base where the flag should spawn)
					newPosition = getRandomFreeLocationInRegion(random, flag.getRadius() * 2, (int) newPosition.getX(),
							(int) newPosition.getY(), flag.getRadius() * 15);

				} else {
					// note this is times 4 in order to ensure objects don't spawn touching (and just
					// to get them a bit farther apart
					newPosition = getRandomFreeLocation(random, object.getRadius() * 4);
				}

				object.setPosition(newPosition);
				object.setAlive(true);
				object.setDrawable(true);

				// reset the UUID if it is a beacon
				if (object instanceof Beacon) {
					object.resetId();
				}
			}
		}

	}

	/**
	 * Clones all the objects in space (used for security so the teams can't
	 * manipulate other ships) +
	 * 
	 * @return
	 */
	public Toroidal2DPhysics deepClone() {
		Toroidal2DPhysics newSpace = new Toroidal2DPhysics(this);

		for (AbstractObject swObject : allObjects) {
			AbstractObject newObject = swObject.deepClone();

			newSpace.addObject(newObject);
		}

		return newSpace;
	}

	/**
	 * Loop through all weapons and remove any dead ones
	 */
	public void cleanupDeadWeapons() {
		ArrayList<AbstractObject> deadObjects = new ArrayList<AbstractObject>();
		for (AbstractObject object : allObjects) {
			if (object instanceof AbstractWeapon && !object.isAlive()) {
				deadObjects.add(object);
			}
		}

		for (AbstractObject deadObject : deadObjects) {
			removeObject(deadObject);
		}

	}

	/**
	 * Loop through all cores and remove any dead ones
	 */
	public void cleanupDeadCores() {
		ArrayList<AbstractObject> deadObjects = new ArrayList<AbstractObject>();

		for (AiCore core : cores) {
			if (!core.isAlive()) {
				deadObjects.add(core);
			}
		}

		for (AbstractObject deadObject : deadObjects) {
			removeObject(deadObject);
		}

	}

	/**
	 * Loop through all drones and remove any dead ones. herr0861 edit
	 */
	public void cleanupDeadDrones() {
		ArrayList<AbstractObject> deadObjects = new ArrayList<AbstractObject>();

		for (Drone drone : drones) {
			if (!drone.isAlive()) {
				deadObjects.add(drone);
			}
		}

		for (AbstractObject deadObject : deadObjects) {
			removeObject(deadObject);
		}
	}

	/**
	 * Loop through all asteroids and remove any dead ones
	 */
	public int cleanupAllAndCountMineableDeadAsteroids() {
		ArrayList<AbstractObject> deadObjects = new ArrayList<AbstractObject>();
		int numMineable = 0;

		for (Asteroid asteroid : asteroids) {
			if (!asteroid.isAlive()) {
				deadObjects.add(asteroid);
				if (asteroid.isMineable()) {
					numMineable++;
				}
			}
		}

		for (AbstractObject deadObject : deadObjects) {
			removeObject(deadObject);
		}
		
		// return the number of mineable ones that were removed
		return numMineable;
	}

	/**
	 * Return the maximum number of time steps for the simulation
	 * 
	 * @return
	 */

	public int getMaxTime() {
		return maxTime;
	}

	/**
	 * Return all objects
	 * 
	 * @return
	 */
	public Set<AbstractObject> getAllObjects() {
		return allObjects;
	}

	/**
	 * Check to see if following a straight line path between two given locations
	 * would result in a collision with a provided set of obstructions
	 * 
	 * @author Andrew and Thibault
	 * 
	 * @param startPosition the starting location of the straight line path
	 * @param goalPosition  the ending location of the straight line path
	 * @param obstructions  an Set of AbstractObject obstructions (i.e., if you
	 *                      don't wish to consider mineable asteroids or beacons
	 *                      obstructions)
	 * @param freeRadius    used to determine free space buffer size
	 * @return Whether or not a straight line path between two positions contains
	 *         obstructions from a given set
	 */
	public boolean isPathClearOfObstructions(Position startPosition, Position goalPosition,
			Set<AbstractObject> obstructions, int freeRadius) {
		Vector2D pathToGoal = findShortestDistanceVector(startPosition, goalPosition); // Shortest straight line path
																						// from startPosition to
																						// goalPosition
		double distanceToGoal = pathToGoal.getMagnitude(); // Distance of straight line path

		boolean pathIsClear = true; // Boolean showing whether or not the path is clear

		// Calculate distance between obstruction center and path (including buffer for
		// ship movement)
		// Uses hypotenuse * sin(theta) = opposite (on a right hand triangle)
		Vector2D pathToObstruction; // Vector from start position to obstruction
		double angleBetween; // Angle between vector from start position to obstruction

		// Loop through obstructions
		for (AbstractObject obstruction : obstructions) {
			// If the distance to the obstruction is greater than the distance to the end
			// goal, ignore the obstruction
			// bug fix for radius of obstruction provided by Yo 
			pathToObstruction = findShortestDistanceVector(startPosition, obstruction.getPosition());
			if (pathToObstruction.getMagnitude() > distanceToGoal + obstruction.getRadius()) {
				continue;
			}

			// Ignore angles > 90 degrees
			angleBetween = Math.abs(pathToObstruction.angleBetween(pathToGoal));
			if (angleBetween > Math.PI / 2) {
				continue;
			}

			// Compare distance between obstruction and path with buffer distance
			if (pathToObstruction.getMagnitude() * Math.sin(angleBetween) < obstruction.getRadius()
					+ freeRadius * 1.5) {
				pathIsClear = false;
				break;
			}
		}

		return pathIsClear;

	}

	/**
	 * Set the team information for this time step
	 * 
	 * @param teamInfo
	 */
	public void setTeamInfo(Set<ImmutableTeamInfo> teamInfo) {
		this.teamInfo = teamInfo;
	}

	/**
	 * Get the team's information
	 * 
	 * @return
	 */
	public Set<ImmutableTeamInfo> getTeamInfo() {
		return teamInfo;
	}

} 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;
		}
	}
	
}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import spacesettlers.actions.AbstractAction;
import spacesettlers.actions.DoNothingAction;
import spacesettlers.actions.MoveToObjectAction;
import spacesettlers.actions.PurchaseCosts;
import spacesettlers.actions.PurchaseTypes;
import spacesettlers.game.AbstractGameAgent;
import spacesettlers.graphics.SpacewarGraphics;
import spacesettlers.objects.AbstractActionableObject;
import spacesettlers.objects.AbstractObject;
import spacesettlers.objects.AiCore;
import spacesettlers.objects.Asteroid;
import spacesettlers.objects.Base;
import spacesettlers.objects.Beacon;
import spacesettlers.objects.Ship;
import spacesettlers.objects.powerups.SpaceSettlersPowerupEnum;
import spacesettlers.objects.resources.ResourcePile;
import spacesettlers.simulator.Toroidal2DPhysics;
import spacesettlers.utilities.Position;
				}
			}
		}
		return nearestBase;
	}

	/**
	 * Returns the asteroid of highest value that isn't already being chased by this team
	 * 
	 * @return
	 */
	private Asteroid pickHighestValueNearestFreeAsteroid(Toroidal2DPhysics space, Ship ship) {
		Set<Asteroid> asteroids = space.getAsteroids();
		int bestMoney = Integer.MIN_VALUE;
		Asteroid bestAsteroid = null;
		double minDistance = Double.MAX_VALUE;

		for (Asteroid asteroid : asteroids) {
			if (!asteroidToShipMap.containsKey(asteroid.getId())) {
				if (asteroid.isMineable() && asteroid.getResources().getTotal() > bestMoney) {
					double dist = space.findShortestDistance(asteroid.getPosition(), ship.getPosition());
					if (dist < minDistance) {
						bestMoney = asteroid.getResources().getTotal();
						//System.out.println("Considering asteroid " + asteroid.getId() + " as a best one");
						bestAsteroid = asteroid;
						minDistance = dist;
					}
				}
			}
		}
		//System.out.println("Best asteroid has " + bestMoney);
		return bestAsteroid;
	}

	/**
	 * Find the nearest beacon to this ship
	 * @param space
	 * @param ship
	 * @return
	 */
	private Beacon pickNearestBeacon(Toroidal2DPhysics space, Ship ship) {
		// get the current beacons
		Set<Beacon> beacons = space.getBeacons();

		Beacon closestBeacon = null;
		double bestDistance = Double.POSITIVE_INFINITY;

		for (Beacon beacon : beacons) {
			double dist = space.findShortestDistance(ship.getPosition(), beacon.getPosition());
			if (dist < bestDistance) {
				bestDistance = dist;
				closestBeacon = beacon;
			}
		}

		return closestBeacon;
	}



	@Override
	public void getMovementEnd(Toroidal2DPhysics space, Set<AbstractActionableObject> actionableObjects) {
		ArrayList<Asteroid> finishedAsteroids = new ArrayList<Asteroid>();

		for (UUID asteroidId : asteroidToShipMap.keySet()) {
			Asteroid asteroid = (Asteroid) space.getObjectById(asteroidId);
			if (asteroid != null && (!asteroid.isAlive() || asteroid.isMoveable())) {
				finishedAsteroids.add(asteroid);
				//System.out.println("Removing asteroid from map");
			}
		}

		for (Asteroid asteroid : finishedAsteroids) {
			asteroidToShipMap.remove(asteroid.getId());
		}
		
		// check to see who bounced off bases
		for (UUID shipId : aimingForBase.keySet()) {
			if (aimingForBase.get(shipId)) {
				Ship ship = (Ship) space.getObjectById(shipId);
				if (ship.getResources().getTotal() == 0 ) {
					// we hit the base (or died, either way, we are not aiming for base now)
					//System.out.println("Hit the base and dropped off resources");
					aimingForBase.put(shipId, false);
					justHitBase.put(shipId, true);
					goingForCore.put(ship.getId(), false);
				}
			}
		}
		


	}

	@Override
	public void initialize(Toroidal2DPhysics space) {
		asteroidToShipMap = new HashMap<UUID, Ship>();
		asteroidCollectorID = null;
		aimingForBase = new HashMap<UUID, Boolean>();
		goingForCore = new HashMap<UUID, Boolean>();
		justHitBase = new HashMap<UUID, Boolean>();
	}

	@Override
	public void shutDown(Toroidal2DPhysics space) {
		// TODO Auto-generated method stub

	}

	@Override
	public Set<SpacewarGraphics> getGraphics() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	/**
	 * If there is enough resourcesAvailable, buy a base.  Place it by finding a ship that is sufficiently
	 * far away from the existing bases
	 */
	public Map<UUID, PurchaseTypes> getTeamPurchases(Toroidal2DPhysics space,