// moves away from a location using the moveTo method (and its implications)
  public Direction moveAway(MapLocation location) throws GameActionException {

    if (location == null) return null;

    MapLocation currentLocation = this.robot.locationController.currentLocation();
    Direction direction = currentLocation.directionTo(location);

    return this.moveToward(
        currentLocation.add(MovementController.directionWithOffset(direction, -4)));
  }
  // figures out the better of two directions to go in
  private Direction moveTowardDirections(
      MapLocation currentLocation,
      MapLocation desiredLocation,
      Direction directionOne,
      Direction directionTwo,
      Boolean allowGreaterDistance,
      double currentDistance)
      throws GameActionException {

    Direction direction = null;

    MapLocation moveLocationOne = currentLocation.add(directionOne);
    MapLocation moveLocationTwo = currentLocation.add(directionTwo);
    MapLocation moveLocation = null;

    int moveLocationInteger =
        (moveLocationOne.distanceSquaredTo(desiredLocation)
                <= moveLocationTwo.distanceSquaredTo(desiredLocation))
            ? 1
            : 2;
    if (moveLocationInteger == 1) {

      moveLocation = moveLocationOne;
      direction = directionOne;

    } else {

      moveLocation = moveLocationTwo;
      direction = directionTwo;
    }

    if (!allowGreaterDistance && moveLocation.distanceSquaredTo(desiredLocation) > currentDistance)
      return null;
    if (this.moveTo(direction)) {

      return direction;

    } else {

      if (moveLocationInteger == 1) {

        moveLocation = moveLocationTwo;
        direction = directionTwo;

      } else {

        moveLocation = moveLocationOne;
        direction = directionOne;
      }
      if (!allowGreaterDistance
          && moveLocation.distanceSquaredTo(desiredLocation) > currentDistance) return null;
      if (this.moveTo(direction)) {

        return direction;
      }
    }
    return null;
  }
  public Direction moveToward(MapLocation location, Boolean allowGreaterDistance, int[] offsets)
      throws GameActionException {

    if (location == null) return null;

    MapLocation currentLocation = this.robot.locationController.currentLocation();
    double currentDistance = currentLocation.distanceSquaredTo(location);

    Direction direction = currentLocation.directionTo(location);
    int directionInteger = directionToInt(direction);

    if (this.actualLastPosition != null
        && currentLocation.distanceSquaredTo(this.actualLastPosition) <= 1) this.turnsStuck++;

    if (this.turnsStuck > 1) return this.moveAroundObstacleToward(location);

    if (moveTo(direction)) {

      this.actualLastPosition = currentLocation;
      return direction;

    } else {

      for (int offset : offsets) {

        Direction directionOne = MovementController.directionFromInt(directionInteger + offset);
        Direction directionTwo = MovementController.directionFromInt(directionInteger - offset);
        direction =
            this.moveTowardDirections(
                currentLocation,
                location,
                directionOne,
                directionTwo,
                allowGreaterDistance,
                currentDistance);
        if (direction != null) {

          this.actualLastPosition = currentLocation;
          return direction;
        }
      }
    }

    this.turnsStuck++;
    return null;
  }
  public Direction moveAway(RobotInfo[] enemies) throws GameActionException {

    MapLocation currentLocation = this.robot.locationController.currentLocation();

    RobotInfo closestEnemy = null;
    double closestDistance = Double.MAX_VALUE;

    for (RobotInfo enemy : enemies) {

      double distance = currentLocation.distanceSquaredTo(enemy.location);
      if (distance < closestDistance) {

        closestEnemy = enemy;
        closestDistance = distance;
      }
    }

    Direction opposite =
        MovementController.directionWithOffset(
            currentLocation.directionTo(closestEnemy.location), -4);
    return this.moveToward(currentLocation.add(opposite, 40));
  }
  public Boolean canMoveSafely(
      Direction direction, Boolean moveAroundHQ, Boolean moveAroundTowers, Boolean moveAroundUnits)
      throws GameActionException {

    if (direction == null) return false;

    RobotController rc = this.robot.robotController;
    Boolean canMove = rc.canMove(direction);
    if (!canMove) return false;

    // run some initial checks
    MapLocation towerLocation = this.robot.locationController.enemyTowerInRange();
    if (towerLocation != null) moveAroundTowers = false;

    // start checking if we can move

    MapLocation currentLocation = this.robot.locationController.currentLocation();
    MapLocation moveLocation = currentLocation.add(direction);

    if (moveAroundHQ) {

      MapLocation[] towers = this.robot.unitController.enemyTowers();
      if (moveLocation.distanceSquaredTo(this.robot.locationController.enemyHQLocation())
          <= HQ.enemyAttackRadiusSquared(towers.length)) return false;
    }

    if (moveAroundTowers) {

      MapLocation[] towers = this.robot.unitController.enemyTowers();
      for (MapLocation tower : towers) {

        if (moveLocation.distanceSquaredTo(tower) <= Tower.type().attackRadiusSquared) return false;
      }
    }

    if (moveAroundUnits) {

      double buffer = 0;
      if (this.robot.type == Launcher.type()) buffer = 10;

      RobotInfo[] enemies = this.robot.unitController.nearbyEnemies();
      for (RobotInfo enemy : enemies) {

        this.robot.broadcaster.evaluateSeenLaunchersWithType(enemy.type);
        if (!UnitController.isUnitTypeDangerous(enemy.type)) continue;
        if (moveLocation.distanceSquaredTo(enemy.location)
            <= enemy.type.attackRadiusSquared + buffer) return false;
      }
    }

    /*RobotInfo[] enemies = this.robot.unitController.nearbyEnemies();
      for (RobotInfo enemy : enemies) {

      	this.robot.broadcaster.evaluateSeenLaunchersWithType(enemy.type);
    if (!UnitController.isUnitTypeDangerous(enemy.type)) continue;

    if (enemy.type == Launcher.type()) {

          	if (moveLocation.distanceSquaredTo(enemy.location) <= 60) return false;

    } else {

    	if (moveAroundUnits) {

              	if (moveLocation.distanceSquaredTo(enemy.location) <= enemy.type.attackRadiusSquared) return false;

      		}

    }

      }*/

    return true;
  }
  // follows a wall until it can move towards it's initial target, switching directions if it gets
  // stuck
  public Direction moveAroundObstacleToward(MapLocation location) throws GameActionException {

    if (this.traversalDirection == 0) { // start with a random traversal direction

      if (this.robot.random.nextBoolean()) this.traversalDirection = -1;
      else this.traversalDirection = 1;
    }

    MapLocation robotLocation = this.robot.locationController.currentLocation();
    this.actualLastPosition = robotLocation;
    Direction directionToTarget = robotLocation.directionTo(location);
    Direction directionToLastPosition =
        this.lastPosition != null ? robotLocation.directionTo(this.lastPosition) : null;
    int switchedDirection = 0;

    this.robot.robotController.setIndicatorString(
        2,
        "Traversal Direction "
            + this.traversalDirection
            + " Dir to target "
            + directionToTarget
            + " Dir to LP "
            + directionToLastPosition);

    // see if it can move toward it's target
    if (directionToTarget == directionToLastPosition || !this.moveTo(directionToTarget)) {

      if (this.lastLastPosition != null
          && robotLocation.distanceSquaredTo(this.lastLastPosition) <= 1
          && this.turnsStuckWhilePathfinding == 0) { // if just moved in a triangle

        this.traversalDirection = -this.traversalDirection;
      }

      this.robot.robotController.setIndicatorString(2, "I CAN'T move towards target");

      // traverse along obstacle, switching directions if it hits a loop around or outer wall
      for (int i = 1; i < 8 && switchedDirection <= 1; i++) {

        Direction direction =
            MovementController.directionWithOffset(directionToTarget, i * this.traversalDirection);
        MapLocation nextLocation = robotLocation.add(direction);
        this.robot.robotController.setIndicatorDot(nextLocation, 255, 255, 255);

        if (!direction.equals(directionToLastPosition) || this.turnsStuckWhilePathfinding > 1) {

          if (this.robot.movementController.moveTo(direction)) {

            this.lastLastPosition = this.lastPosition;
            this.lastPosition = robotLocation;
            if (this.turnsStuckWhilePathfinding > 0) this.turnsStuckWhilePathfinding -= 1;

            return direction;

          } else {

            if (this.robot
                .robotController
                .senseTerrainTile(nextLocation)
                .equals(TerrainTile.OFF_MAP)) {

              this.traversalDirection =
                  -this
                      .traversalDirection; // Might need to do additional checks if this switches
                                           // too often
              i = 0;
              switchedDirection++;
            }
          }
        }
      }

      // check if didn't move (dead end)
      if (robotLocation.equals(this.actualLastPosition)) {

        this.turnsStuckWhilePathfinding++;
      }

    } else {

      this.robot.robotController.setIndicatorString(2, "I can move towards target");
      this.robot.robotController.setIndicatorLine(robotLocation, location, 255, 255, 255);
      this.turnsStuck = 0;
      return directionToTarget;
    }

    // System.out.println("Didn't find move location.");
    return null;
  }