/**
   * Assign a carrier for this treasure.
   *
   * @param lb A <code>LogBuilder</code> to log to.
   * @return A suitable carrier <code>AIUnit</code>, to which this unit has been queued for
   *     transport.
   */
  private AIUnit assignCarrier(LogBuilder lb) {
    final AIUnit aiUnit = getAIUnit();
    final Unit unit = getUnit();
    final Player player = unit.getOwner();
    final Europe europe = player.getEurope();

    List<Unit> carriers = player.getCarriersForUnit(unit);
    if (carriers.isEmpty()) return null;

    // Pick the closest carrier and queue this unit.
    final Location here = unit.getLocation();
    int turns = INFINITY;
    Unit closest = null;
    for (Unit c : carriers) {
      int t = c.getTurnsToReach(here);
      if (turns > t) {
        turns = t;
        closest = c;
      }
    }
    final AIMain aiMain = getAIMain();
    TransportMission tm;
    AIUnit aiCarrier;
    if (closest != null
        && (aiCarrier = aiMain.getAIUnit(closest)) != null
        && (tm = aiCarrier.getMission(TransportMission.class)) != null) {
      setTarget(europe);
      aiUnit.changeTransport(aiCarrier);
      if (tm.forceCollection(aiUnit, lb)) {
        lb.add(" forced collection on ", aiCarrier.getUnit());
        return aiCarrier;
      }
    }
    return null;
  }
 /**
  * Why is this mission invalid with a given colony target, given that intermediate colonies are
  * excluded.
  *
  * @param aiUnit The <code>AIUnit</code> to check.
  * @param colony The potential target <code>Colony</code>.
  * @return A reason for mission invalidity, or null if none found.
  */
 private static String invalidFullColonyReason(AIUnit aiUnit, Colony colony) {
   String reason = invalidTargetReason(colony, aiUnit.getUnit().getOwner());
   return (reason != null)
       ? reason
       : (!aiUnit.getUnit().canCashInTreasureTrain(colony.getTile()))
           ? "cashin-impossible-at-location"
           : null;
 }
  /** {@inheritDoc} */
  @Override
  public Mission doMission(LogBuilder lb) {
    lb.add(tag);
    String reason = invalidReason();
    if (isTargetReason(reason)) {
      return retargetMission(reason, lb);
    } else if (reason != null) {
      return lbFail(lb, false, reason);
    }

    for (; ; ) {
      // Go to the target.
      final Unit unit = getUnit();
      Unit.MoveType mt =
          travelToTarget(getTarget(), CostDeciders.avoidSettlementsAndBlockingUnits(), lb);
      switch (mt) {
        case MOVE: // Arrived
          break;

        case MOVE_HIGH_SEAS:
        case MOVE_NO_MOVES:
        case MOVE_NO_REPAIR:
        case MOVE_ILLEGAL:
          return lbWait(lb);

        case MOVE_NO_ACCESS_EMBARK:
        case MOVE_NO_TILE:
          return this;

        default:
          return lbMove(lb, mt);
      }

      // Cash in now if:
      // - already in Europe
      // - or can never get there
      // - it is free to transport the treasure
      // - or there is no potential carrier to get the treasure to there
      // Otherwise, it is better to send to Europe.
      lbAt(lb);
      final AIUnit aiUnit = getAIUnit();
      final Europe europe = getUnit().getOwner().getEurope();
      if (unit.canCashInTreasureTrain()) {
        AIUnit aiCarrier = null;
        boolean cashin = unit.isInEurope() || europe == null || unit.getTransportFee() == 0;
        if (!cashin && aiUnit.getTransport() == null) {
          cashin = assignCarrier(lb) == null;
        }
        if (cashin)
          return (AIMessage.askCashInTreasureTrain(aiUnit))
              ? lbDone(lb, false, "cashed in")
              : lbFail(lb, false, "cashin");
      }
      return retargetMission("transport expected", lb);
    }
  }
 /**
  * Why would this mission be invalid with the given unit?
  *
  * @param aiUnit The <code>AIUnit</code> to test.
  * @return A reason why the mission would be invalid with the unit, or null if none found.
  */
 private static String invalidMissionReason(AIUnit aiUnit) {
   String reason = invalidAIUnitReason(aiUnit);
   if (reason != null) return reason;
   final Unit unit = aiUnit.getUnit();
   return (!unit.canCarryTreasure())
       ? "unit-cannot-carry-treasure"
       : (unit.getTreasureAmount() <= 0) ? "unit-treasure-nonpositive" : null;
 }
 /**
  * Evaluate a potential cashin mission for a given unit and path.
  *
  * @param aiUnit The <code>AIUnit</code> to do the mission.
  * @param path A <code>PathNode</code> to take to the target.
  * @return A score for the proposed mission.
  */
 public static int scorePath(AIUnit aiUnit, PathNode path) {
   Location loc;
   if (path == null
       || (loc = extractTarget(aiUnit, path)) == null
       || (loc instanceof Colony && invalidFullColonyReason(aiUnit, loc.getColony()) != null))
     return Integer.MIN_VALUE;
   return aiUnit.getUnit().getTreasureAmount() / (path.getTotalTurns() + 1);
 }
 /**
  * Why would this mission be invalid with the given AI unit and location?
  *
  * @param aiUnit The <code>AIUnit</code> to check.
  * @param loc The <code>Location</code> to check.
  * @return A reason for invalidity, or null if none found.
  */
 public static String invalidReason(AIUnit aiUnit, Location loc) {
   String reason = invalidMissionReason(aiUnit);
   return (reason != null)
       ? reason
       : (loc instanceof Colony)
           ? invalidColonyReason(aiUnit, (Colony) loc)
           : (loc instanceof IndianSettlement)
               ? invalidTargetReason(loc, aiUnit.getUnit().getOwner())
               : Mission.TARGETINVALID;
 }
 /**
  * Why would an IndianDemandMission be invalid with the given unit and colony.
  *
  * @param aiUnit The <code>AIUnit</code> to test.
  * @param colony The <code>Colony</code> to test.
  * @return A reason why the mission would be invalid with the unit and colony or null if none
  *     found.
  */
 private static String invalidColonyReason(AIUnit aiUnit, Colony colony) {
   String reason = invalidTargetReason(colony);
   if (reason != null) return reason;
   final Unit unit = aiUnit.getUnit();
   final Player owner = unit.getOwner();
   Player targetPlayer = colony.getOwner();
   switch (owner.getStance(targetPlayer)) {
     case UNCONTACTED:
     case PEACE:
     case ALLIANCE:
       return "bad-stance";
     case WAR:
     case CEASE_FIRE:
       Tension tension = unit.getHomeIndianSettlement().getAlarm(targetPlayer);
       if (tension != null && tension.getLevel().compareTo(Tension.Level.CONTENT) <= 0)
         return "happy";
       break;
   }
   return null;
 }
  /**
   * Find a suitable cash in location for this unit.
   *
   * @param aiUnit The <code>AIUnit</code> to execute this mission.
   * @param range The maximum number of moves to search.
   * @param deferOK Enables deferring to a fallback colony.
   * @return A <code>PathNode</code> to the target, or null if not found which includes the case
   *     when Europe should be preferred (because the unit can not get there by itself).
   */
  private static PathNode findTargetPath(AIUnit aiUnit, int range, boolean deferOK) {
    if (invalidAIUnitReason(aiUnit) != null) return null;
    final Unit unit = aiUnit.getUnit();
    final Location start = unit.getPathStartLocation();
    final Player player = unit.getOwner();
    final Europe europe = player.getEurope();
    final Unit carrier = unit.getCarrier();
    final CostDecider standardCd = CostDeciders.avoidSettlementsAndBlockingUnits();
    PathNode path;

    if (player.getNumberOfSettlements() <= 0 || start == null) {
      // No settlements or not on the map, so go straight to
      // Europe.  If Europe does not exist then this mission is
      // doomed.
      return (europe == null)
          ? null
          : unit.findPath(unit.getLocation(), europe, carrier, standardCd);
    }

    // Can the unit get to a cash in site?
    return unit.search(start, getGoalDecider(aiUnit, deferOK), standardCd, range, carrier);
  }
 private static IndianSettlement getHome(AIUnit aiUnit) {
   return aiUnit.getUnit().getHomeIndianSettlement();
 }
 /**
  * Checks if a unit is carrying a tribute.
  *
  * @param aiUnit The <code>AIUnit</code> to check.
  * @return True if the unit is carrying goods.
  */
 private static boolean hasTribute(AIUnit aiUnit) {
   return aiUnit.getUnit().hasGoodsCargo();
 }
 /**
  * Why is this mission invalid with a given Europe target?
  *
  * @param aiUnit The <code>AIUnit</code> to check.
  * @param europe The potential target <code>Europe</code>.
  * @return A reason for mission invalidity, or null if none found.
  */
 private static String invalidEuropeReason(AIUnit aiUnit, Europe europe) {
   return invalidTargetReason(europe, aiUnit.getUnit().getOwner());
 }
 /**
  * Why is this mission invalid with a given colony target, given that intermediate colonies are
  * included.
  *
  * @param aiUnit The <code>AIUnit</code> to check.
  * @param colony The potential target <code>Colony</code>.
  * @return A reason for mission invalidity, or null if none found.
  */
 private static String invalidColonyReason(AIUnit aiUnit, Colony colony) {
   return invalidTargetReason(colony, aiUnit.getUnit().getOwner());
 }