Projects >> battlecode-server >>1e7e86b7234915a1f6293202042712402feb7a93

Chunk
Conflicting content
    private double[] teamResources = new double[2];

    private Map baseHQs = new EnumMap(Team.class);
<<<<<<< HEAD
    private Map> baseTowers = new EnumMap>(Team.class);
    private Map mineLocations = new HashMap();
    private Map droppedSupplies = new HashMap();
=======
    private final Map gameObjectsByLoc = new HashMap();

>>>>>>> 73a30dc3ab16ca4c6c08fd15ddef07e22d722d2e
    private Map oreMined = new HashMap();
    private Map droppedSupplies = new HashMap();
    private Map mapMemory = new EnumMap(Team.class);
Solution content
    private double[] teamResources = new double[2];
    private int[] teamCapturingNumber = new int[2];

    private List encampments = new ArrayList();
    private Map encampmentMap = new HashMap();
    private Map baseHQs = new EnumMap(Team.class);
    private Map> baseTowers = new EnumMap>(Team.class);
    private Map mineLocations = new HashMap();
    private Map droppedSupplies = new HashMap();
    private Map oreMined = new HashMap();
    private Map mapMemory = new EnumMap(Team.class);
File
GameWorld.java
Developer's decision
Manual
Kind of conflict
Attribute
Method invocation
Chunk
Conflicting content
<<<<<<< HEAD
package battlecode.world;

import static battlecode.common.GameActionExceptionType.NOT_ACTIVE;
import static battlecode.common.GameActionExceptionType.CANT_DO_THAT_BRO;
import static battlecode.common.GameActionExceptionType.CANT_SENSE_THAT;
import static battlecode.common.GameActionExceptionType.MISSING_UPGRADE;
import static battlecode.common.GameActionExceptionType.NOT_ENOUGH_RESOURCE;
import static battlecode.common.GameActionExceptionType.NO_ROBOT_THERE;
import static battlecode.common.GameActionExceptionType.OUT_OF_RANGE;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import battlecode.common.CommanderSkillType;
import battlecode.common.DependencyProgress;
import battlecode.common.Direction;
import battlecode.common.GameActionException;
import battlecode.common.GameActionExceptionType;
import battlecode.common.GameConstants;
import battlecode.common.MapLocation;
import battlecode.common.RobotController;
import battlecode.common.RobotInfo;
import battlecode.common.RobotType;
import battlecode.common.Team;
import battlecode.common.TerrainTile;
import battlecode.common.Upgrade;
import battlecode.engine.GenericController;
import battlecode.engine.instrumenter.RobotDeathException;
import battlecode.engine.instrumenter.RobotMonitor;
import battlecode.world.signal.AttackSignal;
import battlecode.world.signal.CaptureSignal;
import battlecode.world.signal.CastSignal;
import battlecode.world.signal.HatSignal;
import battlecode.world.signal.IndicatorStringSignal;
import battlecode.world.signal.LocationSupplyChangeSignal;
import battlecode.world.signal.MatchObservationSignal;
import battlecode.world.signal.MineSignal;
import battlecode.world.signal.MovementSignal;
import battlecode.world.signal.ResearchSignal;
import battlecode.world.signal.SpawnSignal;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;


/*
TODO:
- tweak player
-specs & software page?
- performance tips
- figure out why it's not deterministic

- optimize
- non-constant java.util costs

        super(gw, r);
    }
- player's navigation class, and specs about it
- fix execute action hack w/ robotmonitor ??
- fix playerfactory/spawnsignal hack??
- better suicide() ??
- pare down GW, GWviewer methods; add engine.getallsignals?
*/

public class RobotControllerImpl extends ControllerShared implements RobotController, GenericController {

    public RobotControllerImpl(GameWorld gw, InternalRobot r) {

    public int hashCode() {
        return robot.getID();
    }

    // *********************************
    // ****** GLOBAL QUERY METHODS *****
    // *********************************

    public int getMapWidth() {
        return robot.myGameWorld.getGameMap().getWidth();
    }

    public int getMapHeight() {
        return robot.myGameWorld.getGameMap().getHeight();
    }

    public boolean hasUpgrade(Upgrade upgrade) {
        assertNotNull(upgrade);
        return gameWorld.hasUpgrade(getTeam(), upgrade);
    }
    
    public double getTeamOre() {
        return gameWorld.resources(getTeam());
    }

    public DependencyProgress checkDependencyProgress(RobotType type) {
        if (gameWorld.getRobotTypeCount(robot.getTeam(), type) > 0) {
            return DependencyProgress.DONE;
        } else if (gameWorld.getTotalRobotTypeCount(robot.getTeam(), type) > 0) {
            return DependencyProgress.INPROGRESS;
        } else {
            return DependencyProgress.NONE;
        }
    }

    public boolean hasCommander() {
        return gameWorld.hasCommander(robot.getTeam());
    }
    
    public void assertHaveResource(double amount) throws GameActionException {
        if (amount > gameWorld.resources(getTeam()))
            throw new GameActionException(NOT_ENOUGH_RESOURCE, "You do not have enough ORE to do that.");
    }
    
    public void assertHaveUpgrade(Upgrade upgrade) throws GameActionException {
        if (!gameWorld.hasUpgrade(getTeam(), upgrade))
            throw new GameActionException(MISSING_UPGRADE, "You need the following upgrade: "+upgrade);
    }

    // *********************************
    // ****** UNIT QUERY METHODS *******
    // *********************************

    public int getID() {
        return robot.getID();
    }

    public Team getTeam() {
        return robot.getTeam();
    }

    public RobotType getType() {
        return robot.type;
    }

    public MapLocation getLocation() {
        return robot.getLocation();
    }

    public double getTurnsUntilMovement() {
        return robot.getTimeUntilMovement();
    }

    public double getTurnsUntilAttack() {
        return robot.getTimeUntilAttack();
    }

    public double getHealth() {
        return robot.getEnergonLevel();
    }

    public double getSupplyLevel() {
        return robot.getSupplyLevel();
    }
    
    public int getXP() {
        return robot.getXP();
    }

    public boolean isBuildingSomething() {
        return getBuildingTypeBeingBuilt() != null;
    }

    public RobotType getBuildingTypeBeingBuilt() {
        return robot.getCapturingType();
    }

    public int getBuildingRoundsRemaining() {
        return robot.getCapturingRounds();
    }

    public int getMissileCount() {
        return robot.getMissileCount();
    }

    // ***********************************
    // ****** GENERAL SENSOR METHODS *****
		int factor=1;
    // ***********************************

    public boolean checkCanSense(MapLocation loc) {
        
        int sensorRadius = robot.type.sensorRadiusSquared;

        if (robot.myLocation.distanceSquaredTo(loc) <= sensorRadius) {
            return true;
        }
       
        for (InternalObject o : gameWorld.allObjects()) {
            if ((Robot.class.isInstance(o)) && (o.getTeam() == robot.getTeam() || loc.distanceSquaredTo(o.getLocation()) <= sensorRadius)) {
                return true;
            }
        }
        return false;
    }
    
    public boolean checkCanSense(InternalObject obj) {
        return obj.exists() && (obj.getTeam() == getTeam() || checkCanSense(obj.getLocation()));
    }

    public void assertCanSense(MapLocation loc) throws GameActionException {
        if (!checkCanSense(loc))
            throw new GameActionException(CANT_SENSE_THAT, "That location is not within the robot's sensor range.");
    }

    public void assertCanSense(InternalObject obj) throws GameActionException {
        if (!checkCanSense(obj))
            throw new GameActionException(CANT_SENSE_THAT, "That object is not within the robot's sensor range.");
    }

    public MapLocation senseHQLocation() {
        return gameWorld.getBaseHQ(getTeam()).getLocation();
    }
    
    public MapLocation senseEnemyHQLocation() {
        return gameWorld.senseEnemyHQLocation(getTeam());
    }

    public MapLocation[] senseTowerLocations() {
	return gameWorld.senseTowerLocations(getTeam());
    }

    public MapLocation[] senseEnemyTowerLocations() {
	return gameWorld.senseTowerLocations(getTeam().opponent());
    }

    public TerrainTile senseTerrainTile(MapLocation loc) {
        assertNotNull(loc);
        return gameWorld.senseMapTerrain(getTeam(), loc);
    }
    
    public boolean canSenseObject(GameObject o) {
        return checkCanSense(castInternalObject(o));
    }

    public boolean canSenseSquare(MapLocation loc) {
        return checkCanSense(loc);
    }

    public RobotInfo senseRobotAtLocation(MapLocation loc) throws GameActionException {
        assertNotNull(loc);
        assertCanSense(loc);
        InternalRobot obj = (InternalRobot) gameWorld.getObject(loc);
        if (obj != null && checkCanSense(obj)) {
            return obj.getRobotInfo();
        } else {
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    public  T[] senseNearbyGameObjects(final Class type) {
        Predicate p = new Predicate() {
            public boolean apply(InternalObject o) {
                return checkCanSense(o) && (type.isInstance(o)) && (!o.equals(robot));
            }
        };
        return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
    }

    // USE THIS METHOD CAREFULLY
    public  RobotInfo[] getRobotsFromGameObjects(T[] array) {
        RobotInfo[] robots = new RobotInfo[array.length];
        for (int i = 0; i < robots.length; ++i) {
            InternalRobot ir = (InternalRobot) array[i];
            robots[i] = ir.getRobotInfo();
        }
        return robots;
    }

    public RobotInfo[] senseNearbyRobots() {
        return getRobotsFromGameObjects(senseNearbyGameObjects(Robot.class));
    }
    
    @SuppressWarnings("unchecked")
        if (d == Direction.NONE || d == Direction.OMNI)
    public  T[] senseNearbyGameObjects(final Class type, final int radiusSquared) {
        Predicate p = new Predicate() {
            public boolean apply(InternalObject o) {
                return o.myLocation.distanceSquaredTo(robot.myLocation) <= radiusSquared 
                        && checkCanSense(o) && (type.isInstance(o)) && (!o.equals(robot));
            }
        };
        return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
    }

    public RobotInfo[] senseNearbyRobots(int radiusSquared) {
        return getRobotsFromGameObjects(senseNearbyGameObjects(Robot.class, radiusSquared));
    }

    @SuppressWarnings("unchecked")
    public  T[] senseNearbyGameObjects(final Class type, final int radiusSquared, final Team team) {
        if (team == null) {
            return senseNearbyGameObjects(type, radiusSquared);
        }
        Predicate p = new Predicate() {
            public boolean apply(InternalObject o) {
                return o.myLocation.distanceSquaredTo(robot.myLocation) <= radiusSquared
                        && o.getTeam() == team
                        && checkCanSense(o) && (type.isInstance(o)) && (!o.equals(robot));
            }
        };
        return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
    }

    public RobotInfo[] senseNearbyRobots(int radiusSquared, Team team) {
        return getRobotsFromGameObjects(senseNearbyGameObjects(Robot.class, radiusSquared, team));
    }

    @SuppressWarnings("unchecked")
    public  T[] senseNearbyGameObjects(final Class type, final MapLocation center, final int radiusSquared, final Team team) {
        if (team == null) {
            Predicate p = new Predicate() {
                public boolean apply(InternalObject o) {
                    return o.myLocation.distanceSquaredTo(center) <= radiusSquared
                            && checkCanSense(o) && (type.isInstance(o)) && (!o.equals(robot));
                }
            };
            return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
        }
        Predicate p = new Predicate() {
            public boolean apply(InternalObject o) {
                return o.myLocation.distanceSquaredTo(center) <= radiusSquared
                        && o.getTeam() == team
                        && checkCanSense(o) && (type.isInstance(o)) && (!o.equals(robot));
            }
        };
        return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
    }

    public RobotInfo[] senseNearbyRobots(MapLocation center, int radiusSquared, Team team) {
        return getRobotsFromGameObjects(senseNearbyGameObjects(Robot.class, center, radiusSquared, team));
    }

    // ***********************************
    // ****** MOVEMENT METHODS ***********
    // ***********************************

    public void assertNotMoving() throws GameActionException {
        if (!isMovementActive()) {
            throw new GameActionException(NOT_ACTIVE, "This robot has movement delay and cannot move.");
        }
    }

    public void assertCanMove(Direction d) throws GameActionException {
        if (!canMove(d)) {
            throw new GameActionException(GameActionExceptionType.CANT_MOVE_THERE, "Cannot move in the given direction: " + d);
        }
    }

    protected void assertValidDirection(Direction d) {
        assertNotNull(d);
        if (d == Direction.NONE || d == Direction.OMNI) {
            throw new IllegalArgumentException("You cannot move in the direction NONE or OMNI.");
        }
    }

    public boolean isMovementActive() {
        return getTurnsUntilMovement() < 1;
    }

    public boolean canMove(Direction d) {
            return false;
        assertValidDirection(d);
        return gameWorld.canMove(getLocation().add(d), robot.type);
    }

    public void move(Direction d) throws GameActionException {
        if (robot.type.isBuilding)
            throw new GameActionException(CANT_DO_THAT_BRO, "Buildings can't move");
        assertNotMoving();
        assertCanMove(d);
        double delay = robot.calculateMovementActionDelay(getLocation(), getLocation().add(d), senseTerrainTile(getLocation()));

        int factor = 1;
        if (robot.getSupplyLevel() >= robot.type.supplyUpkeep) {
            robot.decreaseSupplyLevel(robot.type.supplyUpkeep);
        } else {
            factor = 2;
        }

        robot.activateMovement(new MovementSignal(robot, getLocation().add(d),
                true, ((int) delay) * factor), robot.getLoadingDelayForType(), delay * factor);
    }

    // ***********************************
    // ****** ATTACK METHODS *************
    // ***********************************

    public boolean isAttackActive() {
        return getTurnsUntilAttack() < 1;
    }

    protected void assertNotAttacking() throws GameActionException {
        if (!isAttackActive())
            throw new GameActionException(NOT_ACTIVE, "This robot has action delay and cannot attack.");
    }

    protected void assertCanAttack(MapLocation loc) throws GameActionException {
        if (!canAttackSquare(loc))
            throw new GameActionException(OUT_OF_RANGE, "That location is out of this robot's attack range");
    }

    public boolean canAttackSquare(MapLocation loc) {
        assertNotNull(loc);
        return GameWorld.canAttackSquare(robot, loc);
    }

    public void attackSquare(MapLocation loc) throws GameActionException {
        assertNotAttacking();
        assertNotNull(loc);
        assertCanAttack(loc);
        if (robot.type == RobotType.BASHER) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Bashers can only attack using the bash() method.");
        }

        int factor = 1;
        if (robot.getSupplyLevel() >= robot.type.supplyUpkeep) {
            robot.decreaseSupplyLevel(robot.type.supplyUpkeep);
        } else {
            factor = 2;
        }

        robot.activateAttack(new AttackSignal(robot, loc), robot.calculateAttackActionDelay(robot.type) * factor, robot.getCooldownDelayForType());
    }

    

    public void bash() throws GameActionException {
        assertNotAttacking();
        if (robot.type != RobotType.BASHER) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only Bashers can attack using the attack() method.");
        }

        int factor = 1;
        if (robot.getSupplyLevel() >= robot.type.supplyUpkeep) {
            robot.decreaseSupplyLevel(robot.type.supplyUpkeep);
        } else {
            factor = 2;
        }

        robot.activateAttack(new AttackSignal(robot, getLocation()), robot.calculateAttackActionDelay(robot.type) * factor, robot.getCooldownDelayForType());
    }

    public void explode() throws GameActionException {
        if (robot.type != RobotType.MISSILE) {
    // ***********************************
            throw new GameActionException(GameActionExceptionType.CANT_DO_THAT_BRO, "only missiles can self destruct");
        }
        if (robot.type == RobotType.MISSILE) {
            robot.setSelfDestruct();
        }
        throw new RobotDeathException();
    }

    // ***********************************
    // ****** BROADCAST METHODS **********
        
    // ***********************************

    public boolean hasBroadcasted() {
        return robot.hasBroadcasted();
    }
    
    public void broadcast(int channel, int data) throws GameActionException {
        if (channel<0 || channel>GameConstants.BROADCAST_MAX_CHANNELS)
            throw new GameActionException(CANT_DO_THAT_BRO, "Can only use radio channels from 0 to "+GameConstants.BROADCAST_MAX_CHANNELS+", inclusive");
        
        robot.addBroadcast(channel, data);
    }
    
    @Override
    public int readBroadcast(int channel) throws GameActionException {
        if (channel<0 || channel>GameConstants.BROADCAST_MAX_CHANNELS)
            throw new GameActionException(CANT_DO_THAT_BRO, "Can only use radio channels from 0 to "+GameConstants.BROADCAST_MAX_CHANNELS+", inclusive");
	Integer queued = robot.getQueuedBroadcastFor(channel);
	if (queued != null) {
	    return queued.intValue();
	}
        int m = gameWorld.getMessage(robot.getTeam(), channel);
        return m;
    }

    // ***********************************
    // ****** SUPPLY METHODS *************
    // ***********************************

    public double senseSupplyLevelAtLocation(MapLocation loc) throws GameActionException {
        return gameWorld.senseSupplyLevel(getTeam(), loc);
    }

    public void dropSupplies(int amount) throws GameActionException {
        double amt_to_drop = (double) amount;
        if (robot.getSupplyLevel() < amt_to_drop) {
            amt_to_drop = robot.getSupplyLevel();
        }

        // some signal here
        robot.decreaseSupplyLevel(amt_to_drop);
        gameWorld.changeSupplyLevel(robot.getLocation(), amt_to_drop);
    }

    public void transferSupplies(int amount, MapLocation loc) throws GameActionException {
        double amt_to_transfer = (double) amount;
        if (robot.getSupplyLevel() < amt_to_transfer) {
            amt_to_transfer = robot.getSupplyLevel();
        }
        if (loc.distanceSquaredTo(getLocation()) > GameConstants.SUPPLY_TRANSFER_RADIUS_SQUARED) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Can't transfer supply that much distance.");
        }
        InternalObject obj = gameWorld.getObject(loc);
        if (obj == null) {
            throw new GameActionException(CANT_DO_THAT_BRO, "No one to receive supply from transfer in that direction.");
        }
        robot.decreaseSupplyLevel(amt_to_transfer);
        InternalRobot other = (InternalRobot) obj;
        other.increaseSupplyLevel(amt_to_transfer);
    }

    public void pickUpSupplies(int amount) throws GameActionException {
        double amount_to_pickup = (double) amount;
        if (gameWorld.getSupplyLevel(robot.getLocation()) < amount_to_pickup) {
            amount_to_pickup = gameWorld.getSupplyLevel(robot.getLocation());
        }

        // some signal here

        robot.increaseSupplyLevel(amount_to_pickup);
        gameWorld.changeSupplyLevel(robot.getLocation(), -amount_to_pickup);
    }

    public void transferSuppliesToHQ() throws GameActionException {
        if (robot.type != RobotType.SUPPLYDEPOT) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only supply depot can transfer supplies to hq");
        }

        double amount = robot.getSupplyLevel();
        robot.decreaseSupplyLevel(amount);
        gameWorld.getBaseHQ(getTeam()).increaseSupplyLevel(amount);
    }
    // ****** MINING METHODS *************
    // ***********************************

    public void mine() throws GameActionException {
        if (robot.type != RobotType.BEAVER && robot.type != RobotType.MINER) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only BEAVER and MINER can mine");
        }
        assertNotMoving();
        MapLocation loc = getLocation();
		if (robot.getSupplyLevel() >= robot.type.supplyUpkeep) {
            robot.decreaseSupplyLevel(robot.type.supplyUpkeep);
        } else {
            factor = 2;
        }
		
		robot.activateMovement(new MineSignal(loc, getTeam(), getType()), 1*factor, 2*factor);
    }

    public double senseOre(MapLocation loc) throws GameActionException {
        return gameWorld.senseOre(getTeam(), loc);
    }   

    // ***********************************
    // ****** BUILDING/SPAWNING **********
    // ***********************************

    public void launchMissile(Direction dir) throws GameActionException {
        if (robot.type != RobotType.LAUNCHER)
            throw new GameActionException(CANT_DO_THAT_BRO, "Only LAUNCHER can launch missiles");

        if (robot.getMissileCount() == 0) {
            throw new GameActionException(CANT_DO_THAT_BRO, "No missiles to launch");
        }

        assertNotMoving();

        MapLocation loc = getLocation().add(dir);
        if (!gameWorld.canMove(loc, RobotType.MISSILE))
            throw new GameActionException(GameActionExceptionType.CANT_MOVE_THERE, "That square is occupied.");

        robot.decrementMissileCount();
        robot.activateMovement(
                new SpawnSignal(loc, RobotType.MISSILE, robot.getTeam(), robot, 0), 0, 0);
    }

    public boolean canSpawn(Direction dir, RobotType type) {
        if (!robot.type.isBuilding || type.spawnSource != robot.type || type == RobotType.COMMANDER && hasCommander()) {
            return false;
        }

        MapLocation loc = getLocation().add(dir);
        if (!gameWorld.canMove(loc, type))
            return false;

        double cost = type.oreCost;
        if (cost > gameWorld.resources(getTeam())) {
            return false;
        }

        return true;
    }

    public void spawn(Direction dir, RobotType type) throws GameActionException {
        if (!robot.type.isBuilding) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only buildings can spawn");
        }
        if (type.spawnSource != robot.type) {
            throw new GameActionException(CANT_DO_THAT_BRO, "This spawn can only be by a certain type");
        }
        if (type == RobotType.COMMANDER && hasCommander()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only one commander per team!");
        }

        assertNotMoving();
        double cost = type.oreCost;

	if (type == RobotType.COMMANDER) {
	    cost *= (1 << Math.min(gameWorld.getCommandersSpawned(robot.getTeam()), 8));
	}
        
        assertHaveResource(cost);
        gameWorld.adjustResources(getTeam(), -cost);

        MapLocation loc = getLocation().add(dir);
        if (!gameWorld.canMove(loc, type))
            throw new GameActionException(GameActionExceptionType.CANT_MOVE_THERE, "That square is occupied.");

        robot.activateMovement(
                new SpawnSignal(loc, type, robot.getTeam(), robot, 0), robot.type == RobotType.HQ ? 0 : type.buildTurns, type.buildTurns 
                );
        robot.resetSpawnCounter();

	if (type == RobotType.COMMANDER) {
	    gameWorld.incrementCommandersSpawned(robot.getTeam());
	}
    }

    public boolean canBuild(Direction dir, RobotType type) {
        if (robot.type != RobotType.BEAVER)
            return false;
        if (!type.isBuilding)
            return false;

        // check dependencies
        if (gameWorld.getRobotTypeCount(getTeam(), type.dependency) == 0) {
            return false;
        }
        MapLocation loc = getLocation().add(dir);
        if (!gameWorld.canMove(loc, type))
            return false;

        double cost = type.oreCost;
        if (cost > gameWorld.resources(getTeam())) {
            return false;
        }

        return true;
    }
    
    public void build(Direction dir, RobotType type) throws GameActionException {
        if (robot.type != RobotType.BEAVER)
            throw new GameActionException(CANT_DO_THAT_BRO, "Only BEAVER can build");
        if (!type.isBuilding)
            throw new GameActionException(CANT_DO_THAT_BRO, "Can only build buildings");

        // check dependencies
        if (gameWorld.getRobotTypeCount(getTeam(), type.dependency) == 0) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Missing depency for build of " + type);
        }

        assertNotMoving();
        double cost = type.oreCost;
        
        assertHaveResource(cost);
        gameWorld.adjustResources(getTeam(), -cost);

        MapLocation loc = getLocation().add(dir);
        if (!gameWorld.canMove(loc, type))
            throw new GameActionException(GameActionExceptionType.CANT_MOVE_THERE, "That square is occupied.");

        int delay = type.buildTurns;

        robot.activateMovement(
                new SpawnSignal(loc, type, robot.getTeam(), robot, delay), delay, delay
                );
        robot.resetSpawnCounter();
    }
    

    //***********************************
    //****** UPGRADE METHODS ************
    //***********************************

    public void researchUpgrade(Upgrade upgrade) throws GameActionException {
        if (gameWorld.hasUpgrade(getTeam(), upgrade))
            throw new GameActionException(CANT_DO_THAT_BRO, "You already have that upgrade. ("+upgrade+")");
        if (checkResearchProgress(upgrade) > 0) {
            throw new GameActionException(CANT_DO_THAT_BRO, "You already started researching this upgrade. ("+upgrade+")");
        }
        assertNotMoving();
        assertHaveResource(upgrade.oreCost);
        gameWorld.adjustResources(getTeam(), -upgrade.oreCost);
        robot.activateResearch(new ResearchSignal(robot, upgrade), upgrade.numRounds, upgrade.numRounds);
    }
    
    public int checkResearchProgress(Upgrade upgrade) throws GameActionException {
        return gameWorld.getUpgradeProgress(getTeam(), upgrade);
    }
   
    //***********************************
    //****** COMMANDER METHODS **********
    //***********************************
    public boolean hasLearnedSkill(CommanderSkillType skill) throws GameActionException {
	if (!hasCommander()) {
	    throw new GameActionException(CANT_DO_THAT_BRO, "Cannot call hasLearnedSkill without a Commander.");
	}
	return gameWorld.hasSkill(robot.getTeam(), skill);
    }

    public void castFlash(MapLocation loc) throws GameActionException {
	assertNotNull(loc);

	if (robot.type != RobotType.COMMANDER) {
	    throw new GameActionException(CANT_DO_THAT_BRO, "Only Commanders can cast Flash.");
	}
        int factor = 1;
        if (robot.getSupplyLevel() >= robot.type.supplyUpkeep) {
            robot.decreaseSupplyLevel(robot.type.supplyUpkeep);
        } else {
            factor = 2;
        }

	//is this kosher? i hope so
	
	assertNotMoving();
	if (!gameWorld.canMove(loc, robot.type)) {
	    throw new GameActionException(GameActionExceptionType.CANT_MOVE_THERE, "Cannot teleport to " + loc.toString());
	}
	else {
	    robot.activateMovement(new CastSignal(robot, loc), robot.getLoadingDelayForType(), GameConstants.FLASH_MOVEMENT_DELAY * factor);
	}
    }
    public int getFlashCooldown() throws GameActionException {
	if (!hasCommander()) {
	    throw new GameActionException(CANT_DO_THAT_BRO, "Cannot call getFlashCooldown without a Commander.");
	} 
	if (!hasLearnedSkill(CommanderSkillType.FLASH)) {
	    throw new GameActionException(CANT_DO_THAT_BRO, "Cannot call getFlashCooldown without having learned Flash.");
	}
	return gameWorld.getSkillCooldown(robot.getTeam(), CommanderSkillType.FLASH);
    }
    
    // ***********************************
    // ****** OTHER ACTION METHODS *******
    // ***********************************

    public void yield() {
        RobotMonitor.endRunner();
    }

    public void disintegrate() {
        throw new RobotDeathException();
    }
    
    public void resign() {
        for (InternalObject obj : gameWorld.getAllGameObjects())
            if ((obj instanceof InternalRobot) && obj.getTeam() == robot.getTeam())
                gameWorld.notifyDied((InternalRobot) obj);
        gameWorld.removeDead();
    }

    public void win() {
        for (InternalObject obj : gameWorld.getAllGameObjects())
            if ((obj instanceof InternalRobot) && obj.getTeam() == robot.getTeam())
                gameWorld.notifyDied((InternalRobot) obj);
        gameWorld.removeDead();
    }

    // ***********************************
    // ******** MISC. METHODS ************
    // ***********************************

    public void wearHat() throws GameActionException {
        assertNotMoving();
        if (!(robot.getHatCount() == 0 && robot.type == RobotType.HQ)) {
            assertHaveResource(GameConstants.HAT_ORE_COST);
            gameWorld.adjustResources(getTeam(), -GameConstants.HAT_ORE_COST);
        }
        robot.incrementHatCount();
        robot.activateMovement(new HatSignal(robot, gameWorld.randGen.nextInt()), 0, 1);
    }
   
    public void setTeamMemory(int index, long value) {
        gameWorld.setTeamMemory(robot.getTeam(), index, value);
    }

    public void setTeamMemory(int index, long value, long mask) {
        gameWorld.setTeamMemory(robot.getTeam(), index, value, mask);
    }

    public long[] getTeamMemory() {
        long[] arr = gameWorld.getOldTeamMemory()[robot.getTeam().ordinal()];
        return Arrays.copyOf(arr, arr.length);
    }

    // ***********************************
    // ******** DEBUG METHODS ************
    // ***********************************

    public void setIndicatorString(int stringIndex, String newString) {
        if (stringIndex >= 0 && stringIndex < GameConstants.NUMBER_OF_INDICATOR_STRINGS)
            (new IndicatorStringSignal(robot, stringIndex, newString)).accept(gameWorld);
    }

    public void addMatchObservation(String observation) {
        (new MatchObservationSignal(robot, observation)).accept(gameWorld);
    }

    public void breakpoint() {
        gameWorld.notifyBreakpoint();
    }
}
=======
package battlecode.world;


import static battlecode.common.GameActionExceptionType.NOT_ACTIVE;
    }

import static battlecode.common.GameActionExceptionType.CANT_DO_THAT_BRO;
import static battlecode.common.GameActionExceptionType.CANT_SENSE_THAT;
import static battlecode.common.GameActionExceptionType.MISSING_UPGRADE;
import static battlecode.common.GameActionExceptionType.NOT_ENOUGH_RESOURCE;
import static battlecode.common.GameActionExceptionType.NO_ROBOT_THERE;
import static battlecode.common.GameActionExceptionType.OUT_OF_RANGE;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import battlecode.common.CommanderSkillType;
import battlecode.common.DependencyProgress;
import battlecode.common.Direction;
import battlecode.common.GameActionException;
import battlecode.common.GameActionExceptionType;
import battlecode.common.GameConstants;
import battlecode.common.MapLocation;
import battlecode.common.RobotController;
import battlecode.common.RobotInfo;
import battlecode.common.RobotType;
import battlecode.common.Team;
import battlecode.common.TerrainTile;
import battlecode.common.Upgrade;
import battlecode.engine.GenericController;
import battlecode.engine.instrumenter.RobotDeathException;
import battlecode.engine.instrumenter.RobotMonitor;
import battlecode.world.signal.AttackSignal;
import battlecode.world.signal.CastSignal;
import battlecode.world.signal.IndicatorDotSignal;
import battlecode.world.signal.IndicatorLineSignal;
import battlecode.world.signal.IndicatorStringSignal;
import battlecode.world.signal.LocationSupplyChangeSignal;
import battlecode.world.signal.MatchObservationSignal;
import battlecode.world.signal.MineSignal;
import battlecode.world.signal.MovementSignal;
import battlecode.world.signal.ResearchSignal;
import battlecode.world.signal.SpawnSignal;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;


/*
TODO:
- tweak player
-specs & software page?
- performance tips
- figure out why it's not deterministic

- optimize
- non-constant java.util costs
- player's navigation class, and specs about it
- fix execute action hack w/ robotmonitor ??
- fix playerfactory/spawnsignal hack??
- better suicide() ??
- pare down GW, GWviewer methods; add engine.getallsignals?
*/

public class RobotControllerImpl extends ControllerShared implements RobotController, GenericController {

    public RobotControllerImpl(GameWorld gw, InternalRobot r) {
        super(gw, r);
    }

    public int hashCode() {
        return robot.getID();
    }

    // *********************************
    // ****** GLOBAL QUERY METHODS *****
    // *********************************

    public int getMapWidth() {
        return robot.myGameWorld.getGameMap().getWidth();
    }

    public int getMapHeight() {
        return robot.myGameWorld.getGameMap().getHeight();
    }

    public double getTeamOre() {
        return gameWorld.resources(getTeam());
    }
    
    public void assertHaveResource(double amount) throws GameActionException {
        if (amount > gameWorld.resources(getTeam())) {
            throw new GameActionException(NOT_ENOUGH_RESOURCE, "You do not have enough ORE to do that.");
        }
    }
    
    // *********************************
    // ****** UNIT QUERY METHODS *******
    // *********************************

    public int getID() {
        return robot.getID();
    public Team getTeam() {
        return robot.getTeam();
    }

    public RobotType getType() {
        return robot.type;
    }

    public MapLocation getLocation() {
        return robot.getLocation();
    }

    public double getTurnsUntilMovement() {
        return robot.getTimeUntilMovement();
    }

    public double getTurnsUntilAttack() {
        return robot.getTimeUntilAttack();
    }

    public double getHealth() {
        return robot.getHealthLevel();
    }

    public double getSupplyLevel() {
        return robot.getSupplyLevel();
    }
    
    public int getXP() {
        return robot.getXP();
    }

    public int getMissileCount() {
        return robot.getMissileCount();
    }

    // ***********************************
    // ****** GENERAL SENSOR METHODS *****
    // ***********************************

    public MapLocation senseHQLocation() {
        return gameWorld.getBaseHQ(getTeam()).getLocation();
    }
    
    public MapLocation senseEnemyHQLocation() {
        return gameWorld.getBaseHQ(getTeam().opponent()).getLocation();
    }

    public TerrainTile senseTerrainTile(MapLocation loc) {
        assertNotNull(loc);
        return gameWorld.senseMapTerrain(getTeam(), loc);
    }

    public boolean canSense(MapLocation loc) {
        int sensorRadius = robot.type.sensorRadiusSquared;

        if (robot.myLocation.distanceSquaredTo(loc) <= sensorRadius) {
            return true;
        }
       
        for (InternalObject o : gameWorld.allObjects()) {
            if ((Robot.class.isInstance(o)) && (o.getTeam() == robot.getTeam() || loc.distanceSquaredTo(o.getLocation()) <= sensorRadius)) {
                return true;
            }
        }
        return false;
    }
    
    public boolean canSense(InternalObject obj) {
        return obj.exists() && (obj.getTeam() == getTeam() || canSense(obj.getLocation()));
    }

    public void assertCanSense(MapLocation loc) throws GameActionException {
        if (!canSense(loc)) {
            throw new GameActionException(CANT_SENSE_THAT, "That location is not within the robot's sensor range.");
        }
    }

    public void assertCanSense(InternalObject obj) throws GameActionException {
        if (!canSense(obj)) {
            throw new GameActionException(CANT_SENSE_THAT, "That object is not within the robot's sensor range.");
        }
    }

    public boolean canSenseLocation(MapLocation loc) {
        return canSense(loc);
    }

    public boolean isLocationOccupied(MapLocation loc) throws GameActionException {
        assertNotNull(loc);
        assertCanSense(loc);
        InternalRobot obj = (InternalRobot) gameWorld.getObject(loc);
        return obj != null;
    }

    public RobotInfo senseRobotAtLocation(MapLocation loc) throws GameActionException {
        assertNotNull(loc);
        assertCanSense(loc);
        InternalRobot obj = (InternalRobot) gameWorld.getObject(loc);
        if (obj != null && canSense(obj)) {
            return obj.getRobotInfo();
        } else {
            return null;
        }
    }

    public boolean isMovingUnit() {
    @SuppressWarnings("unchecked")
    public  T[] senseNearbyGameObjects(final Class type) {
        Predicate p = new Predicate() {
            public boolean apply(InternalObject o) {
                return canSense(o) && (type.isInstance(o)) && (!o.equals(robot));
            }
        };
        return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
    }

    // USE THIS METHOD CAREFULLY
    public  RobotInfo[] getRobotsFromGameObjects(T[] array) {
        RobotInfo[] robots = new RobotInfo[array.length];
        for (int i = 0; i < robots.length; ++i) {
            InternalRobot ir = (InternalRobot) array[i];
            robots[i] = ir.getRobotInfo();
        }
        return robots;
    }

    public RobotInfo[] senseNearbyRobots() {
        return getRobotsFromGameObjects(senseNearbyGameObjects(Robot.class));
    }
    
    @SuppressWarnings("unchecked")
    public  T[] senseNearbyGameObjects(final Class type, final int radiusSquared) {
        Predicate p = new Predicate() {
            public boolean apply(InternalObject o) {
                return o.myLocation.distanceSquaredTo(robot.myLocation) <= radiusSquared 
                        && canSense(o) && (type.isInstance(o)) && (!o.equals(robot));
            }
        };
        return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
    }

    public RobotInfo[] senseNearbyRobots(int radiusSquared) {
        return getRobotsFromGameObjects(senseNearbyGameObjects(Robot.class, radiusSquared));
    }

    @SuppressWarnings("unchecked")
    public  T[] senseNearbyGameObjects(final Class type, final int radiusSquared, final Team team) {
        if (team == null) {
            return senseNearbyGameObjects(type, radiusSquared);
        }
        Predicate p = new Predicate() {
            public boolean apply(InternalObject o) {
                return o.myLocation.distanceSquaredTo(robot.myLocation) <= radiusSquared
                        && o.getTeam() == team
                        && canSense(o) && (type.isInstance(o)) && (!o.equals(robot));
            }
        };
        return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
    }

    public RobotInfo[] senseNearbyRobots(int radiusSquared, Team team) {
        return getRobotsFromGameObjects(senseNearbyGameObjects(Robot.class, radiusSquared, team));
    }

    @SuppressWarnings("unchecked")
    public  T[] senseNearbyGameObjects(final Class type, final MapLocation center, final int radiusSquared, final Team team) {
        if (team == null) {
            Predicate p = new Predicate() {
                public boolean apply(InternalObject o) {
                    return o.myLocation.distanceSquaredTo(center) <= radiusSquared
                            && canSense(o) && (type.isInstance(o)) && (!o.equals(robot));
                }
            };
            return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
        }
        Predicate p = new Predicate() {
            public boolean apply(InternalObject o) {
                return o.myLocation.distanceSquaredTo(center) <= radiusSquared
                        && o.getTeam() == team
                        && canSense(o) && (type.isInstance(o)) && (!o.equals(robot));
            }
        };
        return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
    }

    public RobotInfo[] senseNearbyRobots(MapLocation center, int radiusSquared, Team team) {
        return getRobotsFromGameObjects(senseNearbyGameObjects(Robot.class, center, radiusSquared, team));
    }

    // ***********************************
    // ****** MOVEMENT METHODS ***********

    // ***********************************

    public boolean isMovementActive() {
        return getTurnsUntilMovement() < 1;
    }

    public boolean isPathable(RobotType type, MapLocation loc) {
        return gameWorld.canMove(loc, type);
    }
        return robot.type.canMove();
    }

    public boolean isValidDirection(Direction dir) {
        return dir != null && dir != Direction.NONE && dir != Direction.OMNI;
    }

    public void assertIsMovementActive() throws GameActionException {
        if (!isMovementActive()) {
            throw new GameActionException(NOT_ACTIVE, "This robot has movement delay.");
        }
    }

    public void assertIsPathable(RobotType type, MapLocation loc) throws GameActionException {
        if (!isPathable(type, loc)) {
            throw new GameActionException(GameActionExceptionType.CANT_MOVE_THERE, "Cannot move robot of given type to that location.");
        }
    }

    public void assertIsMovingUnit() throws GameActionException {
        if (!isMovingUnit()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "This unit cannot move.");
        }
    }

    protected void assertIsValidDirection(Direction d) {
        if (!isValidDirection(d)) {
            throw new IllegalArgumentException("You cannot move in the direction NONE or OMNI or null.");
        }
    }

    public boolean canMove(Direction dir) {
        return isMovingUnit() && isValidDirection(dir) && isPathable(robot.type, getLocation().add(dir));
    }

    public void move(Direction d) throws GameActionException {
        assertIsMovementActive();
        assertIsMovingUnit();
        assertIsValidDirection(d);
        assertIsPathable(robot.type, getLocation().add(d));

        double delay = robot.calculateMovementActionDelay(getLocation(), getLocation().add(d), senseTerrainTile(getLocation()));

        robot.activateMovement(new MovementSignal(robot, getLocation().add(d),
                true, (int) delay), robot.getLoadingDelayForType(), delay);
    }

    // ***********************************
    // ****** ATTACK METHODS *************
    // ***********************************

    public boolean isAttackActive() {
        return getTurnsUntilAttack() < 1;
    }

    public boolean isAttackingUnit() {
        return robot.type.canAttack();
    }

    public boolean isValidAttackLocation(MapLocation loc) {
        assertNotNull(loc);
        return isAttackingUnit() && gameWorld.canAttackSquare(robot, loc);
    }

    protected void assertIsAttackActive() throws GameActionException {
        if (!isAttackActive())
            throw new GameActionException(NOT_ACTIVE, "This robot has attack delay and cannot attack.");
    }

    protected void assertIsAttackingUnit() throws GameActionException {
        if (!isAttackingUnit()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Not attacking unit.");
        }
    }

    protected void assertValidAttackLocation(MapLocation loc) throws GameActionException {
        if (!isValidAttackLocation(loc)) {
            throw new GameActionException(OUT_OF_RANGE, "That location is out of this robot's attack range");
        }
    }

    public boolean canAttackLocation(MapLocation loc) {
        return isAttackingUnit() && isValidAttackLocation(loc) && robot.type != RobotType.BASHER;
    }

    public void attackLocation(MapLocation loc) throws GameActionException {
    // ***********************************
        assertNotNull(loc);
        assertIsAttackActive();
        assertIsAttackingUnit();
        assertValidAttackLocation(loc);

        if (robot.type == RobotType.BASHER) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Bashers can only attack using the bash() method.");
        }

        robot.activateAttack(new AttackSignal(robot, loc), robot.getAttackDelayForType(), robot.getCooldownDelayForType());
    }

    public void bash() throws GameActionException {
        assertIsAttackActive();
        if (robot.type != RobotType.BASHER) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only Bashers can attack using the attack() method.");
        }

        robot.activateAttack(new AttackSignal(robot, getLocation()), robot.getAttackDelayForType(), robot.getCooldownDelayForType());
    }

    public void explode() throws GameActionException {
        if (robot.type == RobotType.MISSILE) {
            robot.setSelfDestruct();
        }
        throw new RobotDeathException();
    }

    //***********************************
    //****** COMMANDER METHODS **********
    //***********************************

    public boolean hasCommander() {
        return gameWorld.hasCommander(robot.getTeam());
    }

    public void assertHasCommander() throws GameActionException {
        if (!hasCommander()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Cannot call without a Commander.");
        }
    }

    public void assertNoCommander() throws GameActionException {
        if (hasCommander()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Already have a Commander.");
        }
    }

    public boolean hasLearnedSkill(CommanderSkillType skill) throws GameActionException {
        assertHasCommander();
        return gameWorld.hasSkill(robot.getTeam(), skill);
    }

    public void castFlash(MapLocation loc) throws GameActionException {
        assertNotNull(loc);

        if (robot.type != RobotType.COMMANDER) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only Commanders can cast Flash.");
        }

        //is this kosher? i hope so
        assertIsMovementActive();
        assertIsPathable(robot.type, loc);

        robot.activateMovement(new CastSignal(robot, loc), robot.getLoadingDelayForType(), GameConstants.FLASH_MOVEMENT_DELAY);
    }

    public int getFlashCooldown() throws GameActionException {
        assertHasCommander();

        if (!hasLearnedSkill(CommanderSkillType.FLASH)) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Cannot call getFlashCooldown without having learned Flash.");
        }

        return gameWorld.getSkillCooldown(robot.getTeam(), CommanderSkillType.FLASH);
    }

    // ***********************************
    // ****** BROADCAST METHODS **********
    // ***********************************

    public boolean hasBroadcasted() {
        return robot.hasBroadcasted();
    }
    
    public void broadcast(int channel, int data) throws GameActionException {
        if (channel<0 || channel>GameConstants.BROADCAST_MAX_CHANNELS)
            throw new GameActionException(CANT_DO_THAT_BRO, "Can only use radio channels from 0 to "+GameConstants.BROADCAST_MAX_CHANNELS+", inclusive");
        
        robot.addBroadcast(channel, data);
    }
    
    @Override
    public int readBroadcast(int channel) throws GameActionException {
        if (channel<0 || channel>GameConstants.BROADCAST_MAX_CHANNELS)
            throw new GameActionException(CANT_DO_THAT_BRO, "Can only use radio channels from 0 to "+GameConstants.BROADCAST_MAX_CHANNELS+", inclusive");
	Integer queued = robot.getQueuedBroadcastFor(channel);
	if (queued != null) {
	    return queued.intValue();
	}
        int m = gameWorld.getMessage(robot.getTeam(), channel);
        return m;
    }
    // ****** SUPPLY METHODS *************
    // ***********************************

    public double senseSupplyLevelAtLocation(MapLocation loc) throws GameActionException {
        return gameWorld.senseSupplyLevel(getTeam(), loc);
    }

    public void dropSupplies(int amount) throws GameActionException {
        robot.dropSupply(amount);
    }

    public void pickUpSupplies(int amount) throws GameActionException {
        robot.pickUpSupply(amount);
    }

    public void transferSupplies(int amount, MapLocation loc) throws GameActionException {
        if (loc.distanceSquaredTo(getLocation()) > GameConstants.SUPPLY_TRANSFER_RADIUS_SQUARED) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Can't transfer supply that much distance.");
        }
        InternalRobot obj = (InternalRobot) gameWorld.getObject(loc);
        if (obj == null) {
            throw new GameActionException(CANT_DO_THAT_BRO, "No one to receive supply from transfer in that direction.");
        }
        robot.transferSupply(amount, obj);
    }

    public void transferSuppliesToHQ() throws GameActionException {
        if (robot.type != RobotType.SUPPLYDEPOT) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only supply depot can transfer supplies to hq");
        }

        robot.transferSupply(Integer.MAX_VALUE, gameWorld.getBaseHQ(robot.getTeam()));
    }

    // ***********************************
    // ****** MINING METHODS *************
    // ***********************************

    public boolean isMiningUnit() {
        return robot.type.canMine();
    }

    public boolean canMine() {
        return isMiningUnit();
    }

    public void assertIsMiningUnit() throws GameActionException {
        if (!isMiningUnit()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Cannot mine.");
        }
    }

    public void mine() throws GameActionException {
        assertIsMovementActive();
        assertIsMiningUnit();
        MapLocation loc = getLocation();
		
		robot.activateMovement(new MineSignal(loc, getTeam(), getType()), GameConstants.MINING_LOADING_DELAY, GameConstants.MINING_MOVEMENT_DELAY);
    }

    public double senseOre(MapLocation loc) throws GameActionException {
        return gameWorld.senseOre(getTeam(), loc);
    }   

    // ***********************************
    // ****** LAUNCHER *******************
    // ***********************************

    public boolean isLaunchingUnit() {
        return robot.type.canLaunch();
    }

    public void assertIsLaunchingUnit() throws GameActionException {
        if (!isLaunchingUnit()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Must be launcher.");
        }
    }

    public void assertHaveMissiles() throws GameActionException {
        if (robot.getMissileCount() == 0) {
            throw new GameActionException(CANT_DO_THAT_BRO, "No missiles.");
        }
    }

    public void assertDidNotMoveYet() throws GameActionException {
        if (robot.movedThisTurn()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Launchers can't move and launch in the same turn.");
        }
    }

    public void assertCanLaunchAtLocation(MapLocation loc) throws GameActionException {
        if (!robot.canLaunchMissileAtLocation(loc)) {
            throw new GameActionException(GameActionExceptionType.CANT_MOVE_THERE, "Missile already launched in that direction.");
        }
    }

    public boolean canLaunch(Direction dir) {
        MapLocation loc = getLocation().add(dir);
        return isLaunchingUnit() && isPathable(RobotType.MISSILE, loc) && !robot.movedThisTurn() && robot.getMissileCount() > 0 && robot.canLaunchMissileAtLocation(loc);
    }

    public void launchMissile(Direction dir) throws GameActionException {
        assertIsLaunchingUnit();
        assertHaveMissiles();
        assertDidNotMoveYet();

        MapLocation loc = getLocation().add(dir);
        assertIsPathable(RobotType.MISSILE, loc);
        assertCanLaunchAtLocation(loc);

        robot.decrementMissileCount();
        robot.launchMissile(loc);
    }

    // ***********************************
    // ****** BUILDING/SPAWNING **********
    // ***********************************

    public DependencyProgress checkDependencyProgress(RobotType type) {
        if (gameWorld.getActiveRobotTypeCount(robot.getTeam(), type) > 0) {
            return DependencyProgress.DONE;
        } else if (gameWorld.getTotalRobotTypeCount(robot.getTeam(), type) > 0) {
            return DependencyProgress.INPROGRESS;
        } else {
            return DependencyProgress.NONE;
        }
    }

    public boolean isSpawningUnit() {
        return robot.type.canSpawn();
    }

    public void assertIsSpawningUnit() throws GameActionException {
        if (!isSpawningUnit()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Must be spawning unit.");
        }
    }

    public boolean hasSpawnRequirements(RobotType type) {
        if (!isSpawningUnit() || type == RobotType.COMMANDER && hasCommander()) {
            return false;
        }

        double cost = type.oreCost;
        if (type == RobotType.COMMANDER) {
            cost *= (1 << Math.min(gameWorld.getCommandersSpawned(robot.getTeam()), 8));
        }
        if (cost > gameWorld.resources(getTeam())) {
            return false;
        }

        return type.spawnSource == robot.type;
    }

    public boolean canSpawn(Direction dir, RobotType type) {
        MapLocation loc = getLocation().add(dir);
        return isPathable(robot.type, loc) && hasSpawnRequirements(type);
    }

    public void spawn(Direction dir, RobotType type) throws GameActionException {
        assertIsSpawningUnit();
        if (type == RobotType.COMMANDER) {
            assertNoCommander();
        }
        assertIsMovementActive();

        if (type.spawnSource != robot.type) {
            throw new GameActionException(CANT_DO_THAT_BRO, "This spawn can only be by a certain type");
        }

        MapLocation loc = getLocation().add(dir);
        assertIsPathable(type, loc);

        double cost = type.oreCost;
        if (type == RobotType.COMMANDER) {
            cost *= (1 << Math.min(gameWorld.getCommandersSpawned(robot.getTeam()), 8));
        }
        assertHaveResource(cost);

        robot.activateMovement(
                new SpawnSignal(loc, type, robot.getTeam(), robot, 0), 0, type.buildTurns);
    }

    public boolean isBuildingUnit() {
        return robot.type.canBuild();
    }

    public void assertIsBuildingUnit() throws GameActionException {
        if (!isBuildingUnit()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only BEAVER can build");
        }
    }

    public void assertIsBuildable(RobotType type) throws GameActionException {
        if (!type.isBuildable()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Can only build buildings");
        }
    }

    public void assertHasDependencyFor(RobotType type) throws GameActionException {
        if (gameWorld.getActiveRobotTypeCount(getTeam(), type.dependency) == 0) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Missing depency for build of " + type);
        }
    }

    public boolean hasBuildRequirements(RobotType type) {
        return isBuildingUnit() && type.isBuildable() && gameWorld.getActiveRobotTypeCount(getTeam(), type.dependency) > 0 && type.oreCost <= gameWorld.resources(getTeam());
    }

    public boolean canBuild(Direction dir, RobotType type) {
        MapLocation loc = getLocation().add(dir);
        return isPathable(type, loc) && hasBuildRequirements(type);
    }
    
    public void build(Direction dir, RobotType type) throws GameActionException {
        assertIsBuildingUnit();
        assertIsBuildable(type);
        assertHasDependencyFor(type);
        assertIsMovementActive();

        double cost = type.oreCost;
        assertHaveResource(cost);

        MapLocation loc = getLocation().add(dir);
        assertIsPathable(type, loc);

        int delay = type.buildTurns;
        robot.activateMovement(
                new SpawnSignal(loc, type, robot.getTeam(), robot, delay), delay, delay);
    }

    //***********************************
    //****** UPGRADE METHODS ************
    //***********************************

    public boolean isResearchingUnit() {
        return robot.type.canResearch();
    }

    public void assertIsResearchingUnit() throws GameActionException {
        if (!isResearchingUnit()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only HQ can research.");
        }
    }

    public boolean hasUpgrade(Upgrade upgrade) {
        assertNotNull(upgrade);
        return gameWorld.hasUpgrade(getTeam(), upgrade);
    }

    public void assertHaveUpgrade(Upgrade upgrade) throws GameActionException {
        if (!gameWorld.hasUpgrade(getTeam(), upgrade))
            throw new GameActionException(MISSING_UPGRADE, "You need the following upgrade: "+upgrade);
    }

    public void assertNoUpgrade(Upgrade upgrade) throws GameActionException {
        if (gameWorld.hasUpgrade(getTeam(), upgrade))
            throw new GameActionException(MISSING_UPGRADE, "You already have the following upgrade: "+upgrade);
    }

    public boolean canResearch(Upgrade upgrade) {
        return isResearchingUnit() && !hasUpgrade(upgrade) && upgrade.oreCost / upgrade.numRounds <= getTeamOre();
    }

    public void researchUpgrade(Upgrade upgrade) throws GameActionException {
        assertIsResearchingUnit();
        assertNoUpgrade(upgrade);
        assertIsMovementActive();
        assertHaveResource(upgrade.oreCost / upgrade.numRounds);
        robot.activateResearch(new ResearchSignal(robot, upgrade), 1, 1);
    }
    
    public int checkResearchProgress(Upgrade upgrade) {
        return gameWorld.getUpgradeProgress(getTeam(), upgrade);
    }
   
    // ***********************************
    // ****** OTHER ACTION METHODS *******
    // ***********************************

    public void yield() {
        RobotMonitor.endRunner();
    }

    public void disintegrate() {
        throw new RobotDeathException();
    }
    
    public void resign() {
        for (InternalObject obj : gameWorld.getAllGameObjects())
            if ((obj instanceof InternalRobot) && obj.getTeam() == robot.getTeam())
                gameWorld.notifyDied((InternalRobot) obj);
        gameWorld.removeDead();
    }

    // ***********************************
    // ******** MISC. METHODS ************
    // ***********************************

    public void setTeamMemory(int index, long value) {
        gameWorld.setTeamMemory(robot.getTeam(), index, value);
    }

    public void setTeamMemory(int index, long value, long mask) {
        gameWorld.setTeamMemory(robot.getTeam(), index, value, mask);
    }

    public long[] getTeamMemory() {
        long[] arr = gameWorld.getOldTeamMemory()[robot.getTeam().ordinal()];
        return Arrays.copyOf(arr, arr.length);
    }

    // ***********************************
    // ******** DEBUG METHODS ************
    // ***********************************

    public void setIndicatorString(int stringIndex, String newString) {
        if (stringIndex >= 0 && stringIndex < GameConstants.NUMBER_OF_INDICATOR_STRINGS)
            (new IndicatorStringSignal(robot, stringIndex, newString)).accept(gameWorld);
    }

    public void setIndicatorDot(MapLocation loc, int red, int green, int blue) {
        assertNotNull(loc);
        new IndicatorDotSignal(robot,loc,red,green,blue).accept(gameWorld);
    }

    public void setIndicatorLine(MapLocation from, MapLocation to, int red, int green, int blue) {
        assertNotNull(from);
        assertNotNull(to);
        new IndicatorLineSignal(robot,from,to,red,green,blue).accept(gameWorld);
    }

    public long getControlBits() {
        return robot.getControlBits();
    }

    public void addMatchObservation(String observation) {
        (new MatchObservationSignal(robot, observation)).accept(gameWorld);
    }

    public void breakpoint() {
        gameWorld.notifyBreakpoint();
    }
}
>>>>>>> 73a30dc3ab16ca4c6c08fd15ddef07e22d722d2e
Solution content
        }
        return robots;
    }




<<<<<<< HEAD
package battlecode.world;

import static battlecode.common.GameActionExceptionType.NOT_ACTIVE;
import static battlecode.common.GameActionExceptionType.CANT_DO_THAT_BRO;
import static battlecode.common.GameActionExceptionType.CANT_SENSE_THAT;
import static battlecode.common.GameActionExceptionType.MISSING_UPGRADE;
import static battlecode.common.GameActionExceptionType.NOT_ENOUGH_RESOURCE;
import static battlecode.common.GameActionExceptionType.NO_ROBOT_THERE;
import static battlecode.common.GameActionExceptionType.OUT_OF_RANGE;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import battlecode.common.CommanderSkillType;
import battlecode.common.DependencyProgress;
import battlecode.common.Direction;
import battlecode.common.GameActionException;
import battlecode.common.GameActionExceptionType;
import battlecode.common.GameConstants;
import battlecode.common.MapLocation;
import battlecode.common.RobotController;
import battlecode.common.RobotInfo;
import battlecode.common.RobotType;
import battlecode.common.Team;
import battlecode.common.TerrainTile;
import battlecode.common.Upgrade;
import battlecode.engine.GenericController;
import battlecode.engine.instrumenter.RobotDeathException;
import battlecode.engine.instrumenter.RobotMonitor;
import battlecode.world.signal.AttackSignal;
import battlecode.world.signal.CaptureSignal;
import battlecode.world.signal.CastSignal;
import battlecode.world.signal.HatSignal;
import battlecode.world.signal.IndicatorStringSignal;
import battlecode.world.signal.LocationSupplyChangeSignal;
import battlecode.world.signal.MatchObservationSignal;
import battlecode.world.signal.MineSignal;
import battlecode.world.signal.MovementSignal;
import battlecode.world.signal.ResearchSignal;
import battlecode.world.signal.SpawnSignal;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;


/*
TODO:
- tweak player
-specs & software page?
- performance tips
- figure out why it's not deterministic

- optimize
- non-constant java.util costs
- player's navigation class, and specs about it
    }

- fix execute action hack w/ robotmonitor ??
- fix playerfactory/spawnsignal hack??
- better suicide() ??
- pare down GW, GWviewer methods; add engine.getallsignals?
*/

public class RobotControllerImpl extends ControllerShared implements RobotController, GenericController {

    public RobotControllerImpl(GameWorld gw, InternalRobot r) {
        super(gw, r);
    }

    public int hashCode() {
        return robot.getID();
    }

    // *********************************
    // ****** GLOBAL QUERY METHODS *****
    // *********************************

    public int getMapWidth() {
        return robot.myGameWorld.getGameMap().getWidth();
    }

    public int getMapHeight() {
        return robot.myGameWorld.getGameMap().getHeight();
    }

    public boolean hasUpgrade(Upgrade upgrade) {
        assertNotNull(upgrade);
        return gameWorld.hasUpgrade(getTeam(), upgrade);
    }
    
    public double getTeamOre() {
        return gameWorld.resources(getTeam());
    }

    public DependencyProgress checkDependencyProgress(RobotType type) {
        if (gameWorld.getRobotTypeCount(robot.getTeam(), type) > 0) {
            return DependencyProgress.DONE;
        } else if (gameWorld.getTotalRobotTypeCount(robot.getTeam(), type) > 0) {
            return DependencyProgress.INPROGRESS;
        } else {
            return DependencyProgress.NONE;
        }
    }

    public boolean hasCommander() {
        return gameWorld.hasCommander(robot.getTeam());
    }
    
    public void assertHaveResource(double amount) throws GameActionException {
        if (amount > gameWorld.resources(getTeam()))
            throw new GameActionException(NOT_ENOUGH_RESOURCE, "You do not have enough ORE to do that.");
    }
    
    public void assertHaveUpgrade(Upgrade upgrade) throws GameActionException {
        if (!gameWorld.hasUpgrade(getTeam(), upgrade))
            throw new GameActionException(MISSING_UPGRADE, "You need the following upgrade: "+upgrade);
    }

    // *********************************
    // ****** UNIT QUERY METHODS *******
    // *********************************

    public int getID() {
        return robot.getID();
    }

    public Team getTeam() {
        return robot.getTeam();
    }

    public RobotType getType() {
        return robot.type;
    }

    public MapLocation getLocation() {
        return robot.getLocation();
    }

    public double getTurnsUntilMovement() {
        return robot.getTimeUntilMovement();
    }

    public double getTurnsUntilAttack() {
        return robot.getTimeUntilAttack();
    }

    public double getHealth() {
        return robot.getEnergonLevel();
    }

    public double getSupplyLevel() {
        return robot.getSupplyLevel();
    }
    
    public int getXP() {
        return robot.getXP();
    }

    public boolean isBuildingSomething() {
        return getBuildingTypeBeingBuilt() != null;
    }

    public RobotType getBuildingTypeBeingBuilt() {
        return robot.getCapturingType();
    public int getBuildingRoundsRemaining() {
        return robot.getCapturingRounds();
    }

    public int getMissileCount() {
        return robot.getMissileCount();
    }

    // ***********************************
    // ****** GENERAL SENSOR METHODS *****
    // ***********************************

    public boolean checkCanSense(MapLocation loc) {
        
        int sensorRadius = robot.type.sensorRadiusSquared;

        if (robot.myLocation.distanceSquaredTo(loc) <= sensorRadius) {
            return true;
        }
       
        for (InternalObject o : gameWorld.allObjects()) {
            if ((Robot.class.isInstance(o)) && (o.getTeam() == robot.getTeam() || loc.distanceSquaredTo(o.getLocation()) <= sensorRadius)) {
                return true;
            }
        }
        return false;
    }
    
    public boolean checkCanSense(InternalObject obj) {
        return obj.exists() && (obj.getTeam() == getTeam() || checkCanSense(obj.getLocation()));
    }

    public void assertCanSense(MapLocation loc) throws GameActionException {
        if (!checkCanSense(loc))
            throw new GameActionException(CANT_SENSE_THAT, "That location is not within the robot's sensor range.");
    }

    public void assertCanSense(InternalObject obj) throws GameActionException {
        if (!checkCanSense(obj))
            throw new GameActionException(CANT_SENSE_THAT, "That object is not within the robot's sensor range.");
    }

    public MapLocation senseHQLocation() {
        return gameWorld.getBaseHQ(getTeam()).getLocation();
    }
    
    public MapLocation senseEnemyHQLocation() {
        return gameWorld.senseEnemyHQLocation(getTeam());
    }

    public MapLocation[] senseTowerLocations() {
	return gameWorld.senseTowerLocations(getTeam());
    }

    public MapLocation[] senseEnemyTowerLocations() {
	return gameWorld.senseTowerLocations(getTeam().opponent());
    }

    public TerrainTile senseTerrainTile(MapLocation loc) {
        assertNotNull(loc);
        return gameWorld.senseMapTerrain(getTeam(), loc);
    }
    
    public boolean canSenseObject(GameObject o) {
        return checkCanSense(castInternalObject(o));
    }

    public boolean canSenseSquare(MapLocation loc) {
        return checkCanSense(loc);
    }

    public RobotInfo senseRobotAtLocation(MapLocation loc) throws GameActionException {
        assertNotNull(loc);
        assertCanSense(loc);
        InternalRobot obj = (InternalRobot) gameWorld.getObject(loc);
        if (obj != null && checkCanSense(obj)) {
            return obj.getRobotInfo();
        } else {
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    public  T[] senseNearbyGameObjects(final Class type) {
        Predicate p = new Predicate() {
            public boolean apply(InternalObject o) {
                return checkCanSense(o) && (type.isInstance(o)) && (!o.equals(robot));
            }
        };
        return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
    }

    // USE THIS METHOD CAREFULLY
    public  RobotInfo[] getRobotsFromGameObjects(T[] array) {
        RobotInfo[] robots = new RobotInfo[array.length];
        for (int i = 0; i < robots.length; ++i) {
            InternalRobot ir = (InternalRobot) array[i];
            robots[i] = ir.getRobotInfo();
    public RobotInfo[] senseNearbyRobots() {
        return getRobotsFromGameObjects(senseNearbyGameObjects(Robot.class));
    }
    
    @SuppressWarnings("unchecked")
    public  T[] senseNearbyGameObjects(final Class type, final int radiusSquared) {
        Predicate p = new Predicate() {
            public boolean apply(InternalObject o) {
                return o.myLocation.distanceSquaredTo(robot.myLocation) <= radiusSquared 
                        && checkCanSense(o) && (type.isInstance(o)) && (!o.equals(robot));
            }
        };
        return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
    }

    public RobotInfo[] senseNearbyRobots(int radiusSquared) {
        return getRobotsFromGameObjects(senseNearbyGameObjects(Robot.class, radiusSquared));
    }

    @SuppressWarnings("unchecked")
    public  T[] senseNearbyGameObjects(final Class type, final int radiusSquared, final Team team) {
        if (team == null) {
            return senseNearbyGameObjects(type, radiusSquared);
        }
        Predicate p = new Predicate() {
            public boolean apply(InternalObject o) {
                return o.myLocation.distanceSquaredTo(robot.myLocation) <= radiusSquared
                        && o.getTeam() == team
                        && checkCanSense(o) && (type.isInstance(o)) && (!o.equals(robot));
            }
        };
        return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
    }

    public RobotInfo[] senseNearbyRobots(int radiusSquared, Team team) {
        return getRobotsFromGameObjects(senseNearbyGameObjects(Robot.class, radiusSquared, team));
    }

    @SuppressWarnings("unchecked")
    public  T[] senseNearbyGameObjects(final Class type, final MapLocation center, final int radiusSquared, final Team team) {
        if (team == null) {
            Predicate p = new Predicate() {
                public boolean apply(InternalObject o) {
                    return o.myLocation.distanceSquaredTo(center) <= radiusSquared
                            && checkCanSense(o) && (type.isInstance(o)) && (!o.equals(robot));
                }
            };
            return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
        }
        Predicate p = new Predicate() {
            public boolean apply(InternalObject o) {
                return o.myLocation.distanceSquaredTo(center) <= radiusSquared
                        && o.getTeam() == team
                        && checkCanSense(o) && (type.isInstance(o)) && (!o.equals(robot));
            }
        };
        return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
    }

    public RobotInfo[] senseNearbyRobots(MapLocation center, int radiusSquared, Team team) {
        return getRobotsFromGameObjects(senseNearbyGameObjects(Robot.class, center, radiusSquared, team));
    }

    // ***********************************
    // ****** MOVEMENT METHODS ***********
    // ***********************************

    public void assertNotMoving() throws GameActionException {
        if (!isMovementActive()) {
            throw new GameActionException(NOT_ACTIVE, "This robot has movement delay and cannot move.");
        }
    }

    public void assertCanMove(Direction d) throws GameActionException {
        if (!canMove(d)) {
            throw new GameActionException(GameActionExceptionType.CANT_MOVE_THERE, "Cannot move in the given direction: " + d);
        }
    }

    protected void assertValidDirection(Direction d) {
        assertNotNull(d);
        if (d == Direction.NONE || d == Direction.OMNI) {
            throw new IllegalArgumentException("You cannot move in the direction NONE or OMNI.");
        }
    }

    public boolean isMovementActive() {
        return getTurnsUntilMovement() < 1;
    }

    public boolean canMove(Direction d) {
        if (d == Direction.NONE || d == Direction.OMNI)
            return false;
        assertValidDirection(d);
        return gameWorld.canMove(getLocation().add(d), robot.type);
    }

    public void move(Direction d) throws GameActionException {
        if (robot.type.isBuilding)
            throw new GameActionException(CANT_DO_THAT_BRO, "Buildings can't move");
        assertNotMoving();
        assertCanMove(d);
        double delay = robot.calculateMovementActionDelay(getLocation(), getLocation().add(d), senseTerrainTile(getLocation()));

        int factor = 1;
        if (robot.getSupplyLevel() >= robot.type.supplyUpkeep) {
            robot.decreaseSupplyLevel(robot.type.supplyUpkeep);
        } else {
            factor = 2;
        }

        robot.activateMovement(new MovementSignal(robot, getLocation().add(d),
                true, ((int) delay) * factor), robot.getLoadingDelayForType(), delay * factor);
    }

    // ***********************************
    // ****** ATTACK METHODS *************
    // ***********************************

    public boolean isAttackActive() {
        return getTurnsUntilAttack() < 1;
    }

    protected void assertNotAttacking() throws GameActionException {
        if (!isAttackActive())
            throw new GameActionException(NOT_ACTIVE, "This robot has action delay and cannot attack.");
    }

    protected void assertCanAttack(MapLocation loc) throws GameActionException {
        if (!canAttackSquare(loc))
            throw new GameActionException(OUT_OF_RANGE, "That location is out of this robot's attack range");
    }

    public boolean canAttackSquare(MapLocation loc) {
        assertNotNull(loc);
        return GameWorld.canAttackSquare(robot, loc);
    }

    public void attackSquare(MapLocation loc) throws GameActionException {
        assertNotAttacking();
        assertNotNull(loc);
        assertCanAttack(loc);
        if (robot.type == RobotType.BASHER) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Bashers can only attack using the bash() method.");
        }

        int factor = 1;
        if (robot.getSupplyLevel() >= robot.type.supplyUpkeep) {
            robot.decreaseSupplyLevel(robot.type.supplyUpkeep);
        } else {
            factor = 2;
        }

        robot.activateAttack(new AttackSignal(robot, loc), robot.calculateAttackActionDelay(robot.type) * factor, robot.getCooldownDelayForType());
    }

    

    public void bash() throws GameActionException {
        assertNotAttacking();
        if (robot.type != RobotType.BASHER) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only Bashers can attack using the attack() method.");
        }

        int factor = 1;
        if (robot.getSupplyLevel() >= robot.type.supplyUpkeep) {
            robot.decreaseSupplyLevel(robot.type.supplyUpkeep);
        } else {
            factor = 2;
        }

        robot.activateAttack(new AttackSignal(robot, getLocation()), robot.calculateAttackActionDelay(robot.type) * factor, robot.getCooldownDelayForType());
    }

    public void explode() throws GameActionException {
        if (robot.type != RobotType.MISSILE) {
            throw new GameActionException(GameActionExceptionType.CANT_DO_THAT_BRO, "only missiles can self destruct");
        }
        if (robot.type == RobotType.MISSILE) {
            robot.setSelfDestruct();
        }
        throw new RobotDeathException();
    }

    // ***********************************
    // ****** BROADCAST METHODS **********
    // ***********************************

    public boolean hasBroadcasted() {
        return robot.hasBroadcasted();
    }
    
    public void broadcast(int channel, int data) throws GameActionException {
        if (channel<0 || channel>GameConstants.BROADCAST_MAX_CHANNELS)
            throw new GameActionException(CANT_DO_THAT_BRO, "Can only use radio channels from 0 to "+GameConstants.BROADCAST_MAX_CHANNELS+", inclusive");
        
        robot.addBroadcast(channel, data);
    }
    
    @Override
    public int readBroadcast(int channel) throws GameActionException {
        if (channel<0 || channel>GameConstants.BROADCAST_MAX_CHANNELS)
            throw new GameActionException(CANT_DO_THAT_BRO, "Can only use radio channels from 0 to "+GameConstants.BROADCAST_MAX_CHANNELS+", inclusive");
	Integer queued = robot.getQueuedBroadcastFor(channel);
	if (queued != null) {
	    return queued.intValue();
	}
        int m = gameWorld.getMessage(robot.getTeam(), channel);
        return m;
    }

    // ***********************************
    // ****** SUPPLY METHODS *************
    // ***********************************

    public double senseSupplyLevelAtLocation(MapLocation loc) throws GameActionException {
        return gameWorld.senseSupplyLevel(getTeam(), loc);
    }

    public void dropSupplies(int amount) throws GameActionException {
        double amt_to_drop = (double) amount;
        if (robot.getSupplyLevel() < amt_to_drop) {
            amt_to_drop = robot.getSupplyLevel();
        }

        // some signal here
        robot.decreaseSupplyLevel(amt_to_drop);
        gameWorld.changeSupplyLevel(robot.getLocation(), amt_to_drop);
    }

    public void transferSupplies(int amount, MapLocation loc) throws GameActionException {
        double amt_to_transfer = (double) amount;
        if (robot.getSupplyLevel() < amt_to_transfer) {
            amt_to_transfer = robot.getSupplyLevel();
        }
        if (loc.distanceSquaredTo(getLocation()) > GameConstants.SUPPLY_TRANSFER_RADIUS_SQUARED) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Can't transfer supply that much distance.");
        }
        InternalObject obj = gameWorld.getObject(loc);
        if (obj == null) {
            throw new GameActionException(CANT_DO_THAT_BRO, "No one to receive supply from transfer in that direction.");
        }
        robot.decreaseSupplyLevel(amt_to_transfer);
        InternalRobot other = (InternalRobot) obj;
        other.increaseSupplyLevel(amt_to_transfer);
    }

    public void pickUpSupplies(int amount) throws GameActionException {
        double amount_to_pickup = (double) amount;
        if (gameWorld.getSupplyLevel(robot.getLocation()) < amount_to_pickup) {
            amount_to_pickup = gameWorld.getSupplyLevel(robot.getLocation());
        }

        // some signal here

        robot.increaseSupplyLevel(amount_to_pickup);
        gameWorld.changeSupplyLevel(robot.getLocation(), -amount_to_pickup);
    }

    public void transferSuppliesToHQ() throws GameActionException {
        if (robot.type != RobotType.SUPPLYDEPOT) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only supply depot can transfer supplies to hq");
        }

        double amount = robot.getSupplyLevel();
        robot.decreaseSupplyLevel(amount);
        gameWorld.getBaseHQ(getTeam()).increaseSupplyLevel(amount);
    }

    // ***********************************
    // ****** MINING METHODS *************
    // ***********************************

    public void mine() throws GameActionException {
        if (robot.type != RobotType.BEAVER && robot.type != RobotType.MINER) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only BEAVER and MINER can mine");
        }
        assertNotMoving();
        MapLocation loc = getLocation();
        
		int factor=1;
		if (robot.getSupplyLevel() >= robot.type.supplyUpkeep) {
            robot.decreaseSupplyLevel(robot.type.supplyUpkeep);
        } else {
            factor = 2;
        }
		
		robot.activateMovement(new MineSignal(loc, getTeam(), getType()), 1*factor, 2*factor);
    }

    public double senseOre(MapLocation loc) throws GameActionException {
        return gameWorld.senseOre(getTeam(), loc);
    }   

    // ***********************************
    // ****** BUILDING/SPAWNING **********
    // ***********************************

    public void launchMissile(Direction dir) throws GameActionException {
        if (robot.type != RobotType.LAUNCHER)
            throw new GameActionException(CANT_DO_THAT_BRO, "Only LAUNCHER can launch missiles");

        if (robot.getMissileCount() == 0) {
            throw new GameActionException(CANT_DO_THAT_BRO, "No missiles to launch");
        }

        assertNotMoving();

        MapLocation loc = getLocation().add(dir);
        if (!gameWorld.canMove(loc, RobotType.MISSILE))
            throw new GameActionException(GameActionExceptionType.CANT_MOVE_THERE, "That square is occupied.");

        robot.decrementMissileCount();
        robot.activateMovement(
                new SpawnSignal(loc, RobotType.MISSILE, robot.getTeam(), robot, 0), 0, 0);
    }

    public boolean canSpawn(Direction dir, RobotType type) {
        if (!robot.type.isBuilding || type.spawnSource != robot.type || type == RobotType.COMMANDER && hasCommander()) {
            return false;
        }

        MapLocation loc = getLocation().add(dir);
        if (!gameWorld.canMove(loc, type))
            return false;

        double cost = type.oreCost;
        if (cost > gameWorld.resources(getTeam())) {
            return false;
        }

        return true;
    }

    public void spawn(Direction dir, RobotType type) throws GameActionException {
        if (!robot.type.isBuilding) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only buildings can spawn");
        }
        if (type.spawnSource != robot.type) {
            throw new GameActionException(CANT_DO_THAT_BRO, "This spawn can only be by a certain type");
        }
        if (type == RobotType.COMMANDER && hasCommander()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only one commander per team!");
        }

        assertNotMoving();
        double cost = type.oreCost;

	if (type == RobotType.COMMANDER) {
	    cost *= (1 << Math.min(gameWorld.getCommandersSpawned(robot.getTeam()), 8));
	}
        
        assertHaveResource(cost);
        gameWorld.adjustResources(getTeam(), -cost);

        MapLocation loc = getLocation().add(dir);
        if (!gameWorld.canMove(loc, type))
            throw new GameActionException(GameActionExceptionType.CANT_MOVE_THERE, "That square is occupied.");

        robot.activateMovement(
                new SpawnSignal(loc, type, robot.getTeam(), robot, 0), robot.type == RobotType.HQ ? 0 : type.buildTurns, type.buildTurns 
                );
        robot.resetSpawnCounter();

	if (type == RobotType.COMMANDER) {
	    gameWorld.incrementCommandersSpawned(robot.getTeam());
	}
    }
    public boolean canBuild(Direction dir, RobotType type) {
        if (robot.type != RobotType.BEAVER)
            return false;
        if (!type.isBuilding)
            return false;

        // check dependencies
        if (gameWorld.getRobotTypeCount(getTeam(), type.dependency) == 0) {
            return false;
        }
        MapLocation loc = getLocation().add(dir);
        if (!gameWorld.canMove(loc, type))
            return false;

        double cost = type.oreCost;
        if (cost > gameWorld.resources(getTeam())) {
            return false;
        }

        return true;
    }
    
    public void build(Direction dir, RobotType type) throws GameActionException {
        if (robot.type != RobotType.BEAVER)
            throw new GameActionException(CANT_DO_THAT_BRO, "Only BEAVER can build");
        if (!type.isBuilding)
            throw new GameActionException(CANT_DO_THAT_BRO, "Can only build buildings");

        // check dependencies
        if (gameWorld.getRobotTypeCount(getTeam(), type.dependency) == 0) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Missing depency for build of " + type);
        }

        assertNotMoving();
        double cost = type.oreCost;
        
        assertHaveResource(cost);
        gameWorld.adjustResources(getTeam(), -cost);

        MapLocation loc = getLocation().add(dir);
        if (!gameWorld.canMove(loc, type))
            throw new GameActionException(GameActionExceptionType.CANT_MOVE_THERE, "That square is occupied.");

        int delay = type.buildTurns;

        robot.activateMovement(
                new SpawnSignal(loc, type, robot.getTeam(), robot, delay), delay, delay
                );
        robot.resetSpawnCounter();
    }
    

    //***********************************
    //****** UPGRADE METHODS ************
    //***********************************

    public void researchUpgrade(Upgrade upgrade) throws GameActionException {
        if (gameWorld.hasUpgrade(getTeam(), upgrade))
            throw new GameActionException(CANT_DO_THAT_BRO, "You already have that upgrade. ("+upgrade+")");
        if (checkResearchProgress(upgrade) > 0) {
            throw new GameActionException(CANT_DO_THAT_BRO, "You already started researching this upgrade. ("+upgrade+")");
        }
        assertNotMoving();
        assertHaveResource(upgrade.oreCost);
        gameWorld.adjustResources(getTeam(), -upgrade.oreCost);
        robot.activateResearch(new ResearchSignal(robot, upgrade), upgrade.numRounds, upgrade.numRounds);
    }
    
    public int checkResearchProgress(Upgrade upgrade) throws GameActionException {
        return gameWorld.getUpgradeProgress(getTeam(), upgrade);
    }
   
    //***********************************
    //****** COMMANDER METHODS **********
    //***********************************
    public boolean hasLearnedSkill(CommanderSkillType skill) throws GameActionException {
	if (!hasCommander()) {
	    throw new GameActionException(CANT_DO_THAT_BRO, "Cannot call hasLearnedSkill without a Commander.");
	}
	return gameWorld.hasSkill(robot.getTeam(), skill);
    }

    public void castFlash(MapLocation loc) throws GameActionException {
	assertNotNull(loc);

	if (robot.type != RobotType.COMMANDER) {
	    throw new GameActionException(CANT_DO_THAT_BRO, "Only Commanders can cast Flash.");
	}
        int factor = 1;
        if (robot.getSupplyLevel() >= robot.type.supplyUpkeep) {
            robot.decreaseSupplyLevel(robot.type.supplyUpkeep);
        } else {
            factor = 2;
        }

	//is this kosher? i hope so
	
	assertNotMoving();
	if (!gameWorld.canMove(loc, robot.type)) {
	    throw new GameActionException(GameActionExceptionType.CANT_MOVE_THERE, "Cannot teleport to " + loc.toString());
	}
	else {
	    robot.activateMovement(new CastSignal(robot, loc), robot.getLoadingDelayForType(), GameConstants.FLASH_MOVEMENT_DELAY * factor);
	}
    }
    public int getFlashCooldown() throws GameActionException {
	if (!hasCommander()) {
	    throw new GameActionException(CANT_DO_THAT_BRO, "Cannot call getFlashCooldown without a Commander.");
	} 
	if (!hasLearnedSkill(CommanderSkillType.FLASH)) {
	    throw new GameActionException(CANT_DO_THAT_BRO, "Cannot call getFlashCooldown without having learned Flash.");
	}
	return gameWorld.getSkillCooldown(robot.getTeam(), CommanderSkillType.FLASH);
    }
    
    // ***********************************
    // ****** OTHER ACTION METHODS *******
    // ***********************************

    public void yield() {
        RobotMonitor.endRunner();
    }

    public void disintegrate() {
        throw new RobotDeathException();
    }
    
    public void resign() {
        for (InternalObject obj : gameWorld.getAllGameObjects())
            if ((obj instanceof InternalRobot) && obj.getTeam() == robot.getTeam())
                gameWorld.notifyDied((InternalRobot) obj);
        gameWorld.removeDead();
    }

    public void win() {
        for (InternalObject obj : gameWorld.getAllGameObjects())
            if ((obj instanceof InternalRobot) && obj.getTeam() == robot.getTeam())
                gameWorld.notifyDied((InternalRobot) obj);
        gameWorld.removeDead();
    }

    // ***********************************
    // ******** MISC. METHODS ************
    // ***********************************

    public void wearHat() throws GameActionException {
        assertNotMoving();
        if (!(robot.getHatCount() == 0 && robot.type == RobotType.HQ)) {
            assertHaveResource(GameConstants.HAT_ORE_COST);
            gameWorld.adjustResources(getTeam(), -GameConstants.HAT_ORE_COST);
        }
        robot.incrementHatCount();
        robot.activateMovement(new HatSignal(robot, gameWorld.randGen.nextInt()), 0, 1);
    }
   
    public void setTeamMemory(int index, long value) {
        gameWorld.setTeamMemory(robot.getTeam(), index, value);
    }

    public void setTeamMemory(int index, long value, long mask) {
        gameWorld.setTeamMemory(robot.getTeam(), index, value, mask);
    }

    public long[] getTeamMemory() {
        long[] arr = gameWorld.getOldTeamMemory()[robot.getTeam().ordinal()];
        return Arrays.copyOf(arr, arr.length);
    }

    // ***********************************
    // ******** DEBUG METHODS ************
    // ***********************************

    public void setIndicatorString(int stringIndex, String newString) {
        if (stringIndex >= 0 && stringIndex < GameConstants.NUMBER_OF_INDICATOR_STRINGS)
            (new IndicatorStringSignal(robot, stringIndex, newString)).accept(gameWorld);
    }

    public void addMatchObservation(String observation) {
        (new MatchObservationSignal(robot, observation)).accept(gameWorld);
    }

    public void breakpoint() {
        gameWorld.notifyBreakpoint();
    }
}
=======
package battlecode.world;

import static battlecode.common.GameActionExceptionType.NOT_ACTIVE;
import static battlecode.common.GameActionExceptionType.CANT_DO_THAT_BRO;
import static battlecode.common.GameActionExceptionType.CANT_SENSE_THAT;
import static battlecode.common.GameActionExceptionType.MISSING_UPGRADE;
import static battlecode.common.GameActionExceptionType.NOT_ENOUGH_RESOURCE;
import static battlecode.common.GameActionExceptionType.NO_ROBOT_THERE;
import static battlecode.common.GameActionExceptionType.OUT_OF_RANGE;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import battlecode.common.CommanderSkillType;
import battlecode.common.DependencyProgress;
import battlecode.common.Direction;
import battlecode.common.GameActionException;
import battlecode.common.GameActionExceptionType;
import battlecode.common.GameConstants;
import battlecode.common.MapLocation;
import battlecode.common.RobotController;
import battlecode.common.RobotInfo;
import battlecode.common.RobotType;
import battlecode.common.Team;
import battlecode.common.TerrainTile;
import battlecode.common.Upgrade;
import battlecode.engine.GenericController;
import battlecode.engine.instrumenter.RobotDeathException;
import battlecode.engine.instrumenter.RobotMonitor;
import battlecode.world.signal.AttackSignal;
import battlecode.world.signal.CastSignal;
import battlecode.world.signal.IndicatorDotSignal;
import battlecode.world.signal.IndicatorLineSignal;
import battlecode.world.signal.IndicatorStringSignal;
import battlecode.world.signal.LocationSupplyChangeSignal;
import battlecode.world.signal.MatchObservationSignal;
import battlecode.world.signal.MineSignal;
import battlecode.world.signal.MovementSignal;
import battlecode.world.signal.ResearchSignal;
import battlecode.world.signal.SpawnSignal;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;


/*
TODO:
- tweak player
-specs & software page?
- performance tips
- figure out why it's not deterministic

- optimize
- non-constant java.util costs
- player's navigation class, and specs about it
- fix execute action hack w/ robotmonitor ??
- fix playerfactory/spawnsignal hack??
- better suicide() ??
- pare down GW, GWviewer methods; add engine.getallsignals?
*/

public class RobotControllerImpl extends ControllerShared implements RobotController, GenericController {

    public RobotControllerImpl(GameWorld gw, InternalRobot r) {
        super(gw, r);
    }

    public int hashCode() {
        return robot.getID();
    }

    // *********************************
    // ****** GLOBAL QUERY METHODS *****
    // *********************************

    public int getMapWidth() {
        return robot.myGameWorld.getGameMap().getWidth();
    }

    public int getMapHeight() {
        return robot.myGameWorld.getGameMap().getHeight();
    }

    public double getTeamOre() {
        return gameWorld.resources(getTeam());
    }
    
    public void assertHaveResource(double amount) throws GameActionException {
        if (amount > gameWorld.resources(getTeam())) {
            throw new GameActionException(NOT_ENOUGH_RESOURCE, "You do not have enough ORE to do that.");
        }
    }
    
    // *********************************
    // ****** UNIT QUERY METHODS *******
    // *********************************

    public int getID() {
        return robot.getID();
    }

    public Team getTeam() {
        return robot.getTeam();
    }

    public RobotType getType() {
        return robot.type;
    }

    public MapLocation getLocation() {
        return robot.getLocation();
    }

    public double getTurnsUntilMovement() {
        return robot.getTimeUntilMovement();
    }

    public double getTurnsUntilAttack() {
        return robot.getTimeUntilAttack();
    }

    public double getHealth() {
        return robot.getHealthLevel();
    }

    public double getSupplyLevel() {
        return robot.getSupplyLevel();
    }
    
    public int getXP() {
        return robot.getXP();
    }

    public int getMissileCount() {
        return robot.getMissileCount();
    }

    // ***********************************
    // ****** GENERAL SENSOR METHODS *****
    // ***********************************

    public MapLocation senseHQLocation() {
        return gameWorld.getBaseHQ(getTeam()).getLocation();
    }
    
    public MapLocation senseEnemyHQLocation() {
        return gameWorld.getBaseHQ(getTeam().opponent()).getLocation();
    }

    public TerrainTile senseTerrainTile(MapLocation loc) {
        assertNotNull(loc);
        return gameWorld.senseMapTerrain(getTeam(), loc);
    }

    public boolean canSense(MapLocation loc) {
        int sensorRadius = robot.type.sensorRadiusSquared;

        if (robot.myLocation.distanceSquaredTo(loc) <= sensorRadius) {
            return true;
        }
       
        for (InternalObject o : gameWorld.allObjects()) {
            if ((Robot.class.isInstance(o)) && (o.getTeam() == robot.getTeam() || loc.distanceSquaredTo(o.getLocation()) <= sensorRadius)) {
                return true;
            }
        }
        return false;
    }
    
    public boolean canSense(InternalObject obj) {
        return obj.exists() && (obj.getTeam() == getTeam() || canSense(obj.getLocation()));
    }

    public void assertCanSense(MapLocation loc) throws GameActionException {
        if (!canSense(loc)) {
            throw new GameActionException(CANT_SENSE_THAT, "That location is not within the robot's sensor range.");
        }
    }

    public void assertCanSense(InternalObject obj) throws GameActionException {
        if (!canSense(obj)) {
            throw new GameActionException(CANT_SENSE_THAT, "That object is not within the robot's sensor range.");
        }
    }

    public boolean canSenseLocation(MapLocation loc) {
        return canSense(loc);
    }

    public boolean isLocationOccupied(MapLocation loc) throws GameActionException {
        assertNotNull(loc);
        assertCanSense(loc);
        InternalRobot obj = (InternalRobot) gameWorld.getObject(loc);
        return obj != null;
    }

    public RobotInfo senseRobotAtLocation(MapLocation loc) throws GameActionException {
        assertNotNull(loc);
        assertCanSense(loc);
        InternalRobot obj = (InternalRobot) gameWorld.getObject(loc);
        if (obj != null && canSense(obj)) {
            return obj.getRobotInfo();
        } else {
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    public  T[] senseNearbyGameObjects(final Class type) {
        Predicate p = new Predicate() {
            public boolean apply(InternalObject o) {
                return canSense(o) && (type.isInstance(o)) && (!o.equals(robot));
            }
        };
        return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
    }

    // USE THIS METHOD CAREFULLY
    public  RobotInfo[] getRobotsFromGameObjects(T[] array) {
        RobotInfo[] robots = new RobotInfo[array.length];
        for (int i = 0; i < robots.length; ++i) {
            InternalRobot ir = (InternalRobot) array[i];
            robots[i] = ir.getRobotInfo();
        }
        return robots;
    }

    public RobotInfo[] senseNearbyRobots() {
        return getRobotsFromGameObjects(senseNearbyGameObjects(Robot.class));
    }
    
    @SuppressWarnings("unchecked")
    public  T[] senseNearbyGameObjects(final Class type, final int radiusSquared) {
        Predicate p = new Predicate() {
            public boolean apply(InternalObject o) {
                return o.myLocation.distanceSquaredTo(robot.myLocation) <= radiusSquared 
                        && canSense(o) && (type.isInstance(o)) && (!o.equals(robot));
            }
        };
        return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
    }

    public RobotInfo[] senseNearbyRobots(int radiusSquared) {
        return getRobotsFromGameObjects(senseNearbyGameObjects(Robot.class, radiusSquared));
    }

    @SuppressWarnings("unchecked")
    public  T[] senseNearbyGameObjects(final Class type, final int radiusSquared, final Team team) {
        if (team == null) {
            return senseNearbyGameObjects(type, radiusSquared);
        }
        Predicate p = new Predicate() {
            public boolean apply(InternalObject o) {
                return o.myLocation.distanceSquaredTo(robot.myLocation) <= radiusSquared
                        && o.getTeam() == team
                        && canSense(o) && (type.isInstance(o)) && (!o.equals(robot));
            }
        };
        return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
    }

    public RobotInfo[] senseNearbyRobots(int radiusSquared, Team team) {
        return getRobotsFromGameObjects(senseNearbyGameObjects(Robot.class, radiusSquared, team));
    }

    @SuppressWarnings("unchecked")
    public  T[] senseNearbyGameObjects(final Class type, final MapLocation center, final int radiusSquared, final Team team) {
        if (team == null) {
            Predicate p = new Predicate() {
                public boolean apply(InternalObject o) {
                    return o.myLocation.distanceSquaredTo(center) <= radiusSquared
                            && canSense(o) && (type.isInstance(o)) && (!o.equals(robot));
                }
            };
            return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
        }
        Predicate p = new Predicate() {
            public boolean apply(InternalObject o) {
                return o.myLocation.distanceSquaredTo(center) <= radiusSquared
                        && o.getTeam() == team
                        && canSense(o) && (type.isInstance(o)) && (!o.equals(robot));
            }
        };
        return Iterables.toArray((Iterable) Iterables.filter(gameWorld.allObjects(), p), type);
    }

    public RobotInfo[] senseNearbyRobots(MapLocation center, int radiusSquared, Team team) {
        return getRobotsFromGameObjects(senseNearbyGameObjects(Robot.class, center, radiusSquared, team));
    }

    // ***********************************
    // ****** MOVEMENT METHODS ***********
    // ***********************************
    public boolean isMovementActive() {
        return getTurnsUntilMovement() < 1;
    }

    public boolean isPathable(RobotType type, MapLocation loc) {
        return gameWorld.canMove(loc, type);
    }

    public boolean isMovingUnit() {
        return robot.type.canMove();
    }

    public boolean isValidDirection(Direction dir) {
        return dir != null && dir != Direction.NONE && dir != Direction.OMNI;
    }

    public void assertIsMovementActive() throws GameActionException {
        if (!isMovementActive()) {
            throw new GameActionException(NOT_ACTIVE, "This robot has movement delay.");
        }
    }

    public void assertIsPathable(RobotType type, MapLocation loc) throws GameActionException {
        if (!isPathable(type, loc)) {
            throw new GameActionException(GameActionExceptionType.CANT_MOVE_THERE, "Cannot move robot of given type to that location.");
        }
    }

    public void assertIsMovingUnit() throws GameActionException {
        if (!isMovingUnit()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "This unit cannot move.");
        }
    }

    protected void assertIsValidDirection(Direction d) {
        if (!isValidDirection(d)) {
            throw new IllegalArgumentException("You cannot move in the direction NONE or OMNI or null.");
        }
    }

    public boolean canMove(Direction dir) {
        return isMovingUnit() && isValidDirection(dir) && isPathable(robot.type, getLocation().add(dir));
    }

    public void move(Direction d) throws GameActionException {
        assertIsMovementActive();
        assertIsMovingUnit();
        assertIsValidDirection(d);
        assertIsPathable(robot.type, getLocation().add(d));

        double delay = robot.calculateMovementActionDelay(getLocation(), getLocation().add(d), senseTerrainTile(getLocation()));

        robot.activateMovement(new MovementSignal(robot, getLocation().add(d),
                true, (int) delay), robot.getLoadingDelayForType(), delay);
    }

    // ***********************************
    // ****** ATTACK METHODS *************
    // ***********************************

    public boolean isAttackActive() {
        return getTurnsUntilAttack() < 1;
    }

    public boolean isAttackingUnit() {
        return robot.type.canAttack();
    }

    public boolean isValidAttackLocation(MapLocation loc) {
        assertNotNull(loc);
        return isAttackingUnit() && gameWorld.canAttackSquare(robot, loc);
    }

    protected void assertIsAttackActive() throws GameActionException {
        if (!isAttackActive())
            throw new GameActionException(NOT_ACTIVE, "This robot has attack delay and cannot attack.");
    }

    protected void assertIsAttackingUnit() throws GameActionException {
        if (!isAttackingUnit()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Not attacking unit.");
        }
    }

    protected void assertValidAttackLocation(MapLocation loc) throws GameActionException {
        if (!isValidAttackLocation(loc)) {
            throw new GameActionException(OUT_OF_RANGE, "That location is out of this robot's attack range");
        }
    }

    public boolean canAttackLocation(MapLocation loc) {
        return isAttackingUnit() && isValidAttackLocation(loc) && robot.type != RobotType.BASHER;
    }

    public void attackLocation(MapLocation loc) throws GameActionException {
        assertNotNull(loc);
        assertIsAttackActive();
        assertIsAttackingUnit();
        assertValidAttackLocation(loc);

        if (robot.type == RobotType.BASHER) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Bashers can only attack using the bash() method.");
        }

        robot.activateAttack(new AttackSignal(robot, loc), robot.getAttackDelayForType(), robot.getCooldownDelayForType());
    }

    public void bash() throws GameActionException {
        assertIsAttackActive();
        if (robot.type != RobotType.BASHER) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only Bashers can attack using the attack() method.");
        }

        robot.activateAttack(new AttackSignal(robot, getLocation()), robot.getAttackDelayForType(), robot.getCooldownDelayForType());
    }

    public void explode() throws GameActionException {
        if (robot.type == RobotType.MISSILE) {
            robot.setSelfDestruct();
        }
        throw new RobotDeathException();
    }

    //***********************************
    //****** COMMANDER METHODS **********
    //***********************************

    public boolean hasCommander() {
        return gameWorld.hasCommander(robot.getTeam());
    }

    public void assertHasCommander() throws GameActionException {
        if (!hasCommander()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Cannot call without a Commander.");
        }
    }

    public void assertNoCommander() throws GameActionException {
        if (hasCommander()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Already have a Commander.");
        }
    }

    public boolean hasLearnedSkill(CommanderSkillType skill) throws GameActionException {
        assertHasCommander();
        return gameWorld.hasSkill(robot.getTeam(), skill);
    }

    public void castFlash(MapLocation loc) throws GameActionException {
        assertNotNull(loc);

        if (robot.type != RobotType.COMMANDER) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only Commanders can cast Flash.");
        }

        //is this kosher? i hope so
        assertIsMovementActive();
        assertIsPathable(robot.type, loc);

        robot.activateMovement(new CastSignal(robot, loc), robot.getLoadingDelayForType(), GameConstants.FLASH_MOVEMENT_DELAY);
    }

    public int getFlashCooldown() throws GameActionException {
        assertHasCommander();

        if (!hasLearnedSkill(CommanderSkillType.FLASH)) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Cannot call getFlashCooldown without having learned Flash.");
        }

        return gameWorld.getSkillCooldown(robot.getTeam(), CommanderSkillType.FLASH);
    }

    // ***********************************
    // ****** BROADCAST METHODS **********
    // ***********************************

    public boolean hasBroadcasted() {
        return robot.hasBroadcasted();
    }
    
    public void broadcast(int channel, int data) throws GameActionException {
        if (channel<0 || channel>GameConstants.BROADCAST_MAX_CHANNELS)
            throw new GameActionException(CANT_DO_THAT_BRO, "Can only use radio channels from 0 to "+GameConstants.BROADCAST_MAX_CHANNELS+", inclusive");
        
        robot.addBroadcast(channel, data);
    }
    
    @Override
    public int readBroadcast(int channel) throws GameActionException {
        if (channel<0 || channel>GameConstants.BROADCAST_MAX_CHANNELS)
            throw new GameActionException(CANT_DO_THAT_BRO, "Can only use radio channels from 0 to "+GameConstants.BROADCAST_MAX_CHANNELS+", inclusive");
	Integer queued = robot.getQueuedBroadcastFor(channel);
	if (queued != null) {
	    return queued.intValue();
	}
        int m = gameWorld.getMessage(robot.getTeam(), channel);
        return m;
    }

    // ***********************************
    // ****** SUPPLY METHODS *************
    // ***********************************

    public double senseSupplyLevelAtLocation(MapLocation loc) throws GameActionException {
        return gameWorld.senseSupplyLevel(getTeam(), loc);
    }

    public void dropSupplies(int amount) throws GameActionException {
        robot.dropSupply(amount);
    }

    public void pickUpSupplies(int amount) throws GameActionException {
        robot.pickUpSupply(amount);
    }

    public void transferSupplies(int amount, MapLocation loc) throws GameActionException {
        if (loc.distanceSquaredTo(getLocation()) > GameConstants.SUPPLY_TRANSFER_RADIUS_SQUARED) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Can't transfer supply that much distance.");
        }
        InternalRobot obj = (InternalRobot) gameWorld.getObject(loc);
        if (obj == null) {
            throw new GameActionException(CANT_DO_THAT_BRO, "No one to receive supply from transfer in that direction.");
        }
        robot.transferSupply(amount, obj);
    }

    public void transferSuppliesToHQ() throws GameActionException {
        if (robot.type != RobotType.SUPPLYDEPOT) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only supply depot can transfer supplies to hq");
        }

        robot.transferSupply(Integer.MAX_VALUE, gameWorld.getBaseHQ(robot.getTeam()));
    }

    // ***********************************
    // ****** MINING METHODS *************
    // ***********************************

    public boolean isMiningUnit() {
        return robot.type.canMine();
    }

    public boolean canMine() {
        return isMiningUnit();
    }

    public void assertIsMiningUnit() throws GameActionException {
        if (!isMiningUnit()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Cannot mine.");
        }
    }

    public void mine() throws GameActionException {
        assertIsMovementActive();
        assertIsMiningUnit();
        MapLocation loc = getLocation();
		
		robot.activateMovement(new MineSignal(loc, getTeam(), getType()), GameConstants.MINING_LOADING_DELAY, GameConstants.MINING_MOVEMENT_DELAY);
    }

    public double senseOre(MapLocation loc) throws GameActionException {
        return gameWorld.senseOre(getTeam(), loc);
    }   

    // ***********************************
    // ****** LAUNCHER *******************
    // ***********************************

    public boolean isLaunchingUnit() {
        return robot.type.canLaunch();
    }

    public void assertIsLaunchingUnit() throws GameActionException {
        if (!isLaunchingUnit()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Must be launcher.");
        }
    }

    public void assertHaveMissiles() throws GameActionException {
        if (robot.getMissileCount() == 0) {
            throw new GameActionException(CANT_DO_THAT_BRO, "No missiles.");
        }
    }

    public void assertDidNotMoveYet() throws GameActionException {
        if (robot.movedThisTurn()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Launchers can't move and launch in the same turn.");
        }
    }
    public void assertCanLaunchAtLocation(MapLocation loc) throws GameActionException {
        if (!robot.canLaunchMissileAtLocation(loc)) {
            throw new GameActionException(GameActionExceptionType.CANT_MOVE_THERE, "Missile already launched in that direction.");
        }
    }

    public boolean canLaunch(Direction dir) {
        MapLocation loc = getLocation().add(dir);
        return isLaunchingUnit() && isPathable(RobotType.MISSILE, loc) && !robot.movedThisTurn() && robot.getMissileCount() > 0 && robot.canLaunchMissileAtLocation(loc);
    }

    public void launchMissile(Direction dir) throws GameActionException {
        assertIsLaunchingUnit();
        assertHaveMissiles();
        assertDidNotMoveYet();

        MapLocation loc = getLocation().add(dir);
        assertIsPathable(RobotType.MISSILE, loc);
        assertCanLaunchAtLocation(loc);

        robot.decrementMissileCount();
        robot.launchMissile(loc);
    }

    // ***********************************
    // ****** BUILDING/SPAWNING **********
    // ***********************************

    public DependencyProgress checkDependencyProgress(RobotType type) {
        if (gameWorld.getActiveRobotTypeCount(robot.getTeam(), type) > 0) {
            return DependencyProgress.DONE;
        } else if (gameWorld.getTotalRobotTypeCount(robot.getTeam(), type) > 0) {
            return DependencyProgress.INPROGRESS;
        } else {
            return DependencyProgress.NONE;
        }
    }

    public boolean isSpawningUnit() {
        return robot.type.canSpawn();
    }

    public void assertIsSpawningUnit() throws GameActionException {
        if (!isSpawningUnit()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Must be spawning unit.");
        }
    }

    public boolean hasSpawnRequirements(RobotType type) {
        if (!isSpawningUnit() || type == RobotType.COMMANDER && hasCommander()) {
            return false;
        }

        double cost = type.oreCost;
        if (type == RobotType.COMMANDER) {
            cost *= (1 << Math.min(gameWorld.getCommandersSpawned(robot.getTeam()), 8));
        }
        if (cost > gameWorld.resources(getTeam())) {
            return false;
        }

        return type.spawnSource == robot.type;
    }

    public boolean canSpawn(Direction dir, RobotType type) {
        MapLocation loc = getLocation().add(dir);
        return isPathable(robot.type, loc) && hasSpawnRequirements(type);
    }

    public void spawn(Direction dir, RobotType type) throws GameActionException {
        assertIsSpawningUnit();
        if (type == RobotType.COMMANDER) {
            assertNoCommander();
        }
        assertIsMovementActive();

        if (type.spawnSource != robot.type) {
            throw new GameActionException(CANT_DO_THAT_BRO, "This spawn can only be by a certain type");
        }

        MapLocation loc = getLocation().add(dir);
        assertIsPathable(type, loc);

        double cost = type.oreCost;
        if (type == RobotType.COMMANDER) {
            cost *= (1 << Math.min(gameWorld.getCommandersSpawned(robot.getTeam()), 8));
        }
        assertHaveResource(cost);

        robot.activateMovement(
                new SpawnSignal(loc, type, robot.getTeam(), robot, 0), 0, type.buildTurns);
    }

    public boolean isBuildingUnit() {
        return robot.type.canBuild();
    }

    public void assertIsBuildingUnit() throws GameActionException {
        if (!isBuildingUnit()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only BEAVER can build");
        }
    }

    public void assertIsBuildable(RobotType type) throws GameActionException {
        if (!type.isBuildable()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Can only build buildings");
        }
    }

    public void assertHasDependencyFor(RobotType type) throws GameActionException {
        if (gameWorld.getActiveRobotTypeCount(getTeam(), type.dependency) == 0) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Missing depency for build of " + type);
        }
    }

    public boolean hasBuildRequirements(RobotType type) {
        return isBuildingUnit() && type.isBuildable() && gameWorld.getActiveRobotTypeCount(getTeam(), type.dependency) > 0 && type.oreCost <= gameWorld.resources(getTeam());
    }

    public boolean canBuild(Direction dir, RobotType type) {
        MapLocation loc = getLocation().add(dir);
        return isPathable(type, loc) && hasBuildRequirements(type);
    }
    
    public void build(Direction dir, RobotType type) throws GameActionException {
        assertIsBuildingUnit();
        assertIsBuildable(type);
        assertHasDependencyFor(type);
        assertIsMovementActive();

        double cost = type.oreCost;
        assertHaveResource(cost);

        MapLocation loc = getLocation().add(dir);
        assertIsPathable(type, loc);

        int delay = type.buildTurns;
        robot.activateMovement(
                new SpawnSignal(loc, type, robot.getTeam(), robot, delay), delay, delay);
    }

    //***********************************
    //****** UPGRADE METHODS ************
    //***********************************

    public boolean isResearchingUnit() {
        return robot.type.canResearch();
    }

    public void assertIsResearchingUnit() throws GameActionException {
        if (!isResearchingUnit()) {
            throw new GameActionException(CANT_DO_THAT_BRO, "Only HQ can research.");
        }
    }

    public boolean hasUpgrade(Upgrade upgrade) {
        assertNotNull(upgrade);
        return gameWorld.hasUpgrade(getTeam(), upgrade);
    }

    public void assertHaveUpgrade(Upgrade upgrade) throws GameActionException {
        if (!gameWorld.hasUpgrade(getTeam(), upgrade))
            throw new GameActionException(MISSING_UPGRADE, "You need the following upgrade: "+upgrade);
    }

    public void assertNoUpgrade(Upgrade upgrade) throws GameActionException {
        if (gameWorld.hasUpgrade(getTeam(), upgrade))
            throw new GameActionException(MISSING_UPGRADE, "You already have the following upgrade: "+upgrade);
    }

    public boolean canResearch(Upgrade upgrade) {
        return isResearchingUnit() && !hasUpgrade(upgrade) && upgrade.oreCost / upgrade.numRounds <= getTeamOre();
    }

    public void researchUpgrade(Upgrade upgrade) throws GameActionException {
        assertIsResearchingUnit();
        assertNoUpgrade(upgrade);
        assertIsMovementActive();
        assertHaveResource(upgrade.oreCost / upgrade.numRounds);
        robot.activateResearch(new ResearchSignal(robot, upgrade), 1, 1);
    }
    
    public int checkResearchProgress(Upgrade upgrade) {
        return gameWorld.getUpgradeProgress(getTeam(), upgrade);
    }
   
    // ***********************************
    // ****** OTHER ACTION METHODS *******
    // ***********************************

    public void yield() {
        RobotMonitor.endRunner();
    }

    public void disintegrate() {
        throw new RobotDeathException();
    }
    
    public void resign() {
        for (InternalObject obj : gameWorld.getAllGameObjects())
            if ((obj instanceof InternalRobot) && obj.getTeam() == robot.getTeam())
                gameWorld.notifyDied((InternalRobot) obj);
        gameWorld.removeDead();
    }

    // ***********************************
    // ******** MISC. METHODS ************
    // ***********************************

    public void setTeamMemory(int index, long value) {
        gameWorld.setTeamMemory(robot.getTeam(), index, value);
    }

    public void setTeamMemory(int index, long value, long mask) {
        gameWorld.setTeamMemory(robot.getTeam(), index, value, mask);
    }

    public long[] getTeamMemory() {
        long[] arr = gameWorld.getOldTeamMemory()[robot.getTeam().ordinal()];
        return Arrays.copyOf(arr, arr.length);
    }

    // ***********************************
    // ******** DEBUG METHODS ************
    // ***********************************

    public void setIndicatorString(int stringIndex, String newString) {
        if (stringIndex >= 0 && stringIndex < GameConstants.NUMBER_OF_INDICATOR_STRINGS)
            (new IndicatorStringSignal(robot, stringIndex, newString)).accept(gameWorld);
    }

    public void setIndicatorDot(MapLocation loc, int red, int green, int blue) {
        assertNotNull(loc);
        new IndicatorDotSignal(robot,loc,red,green,blue).accept(gameWorld);
    }

    public void setIndicatorLine(MapLocation from, MapLocation to, int red, int green, int blue) {
        assertNotNull(from);
        assertNotNull(to);
        new IndicatorLineSignal(robot,from,to,red,green,blue).accept(gameWorld);
    }

    public long getControlBits() {
        return robot.getControlBits();
    }

    public void addMatchObservation(String observation) {
        (new MatchObservationSignal(robot, observation)).accept(gameWorld);
    }

    public void breakpoint() {
        gameWorld.notifyBreakpoint();
    }
}
>>>>>>> 73a30dc3ab16ca4c6c08fd15ddef07e22d722d2e
File
RobotControllerImpl.java
Developer's decision
Manual
Kind of conflict
Class declaration
Comment
Import
Package declaration