diff --git a/megamek/src/megamek/client/bot/princess/BasicPathRanker.java b/megamek/src/megamek/client/bot/princess/BasicPathRanker.java index 9920f39ec82..153f56a20e6 100644 --- a/megamek/src/megamek/client/bot/princess/BasicPathRanker.java +++ b/megamek/src/megamek/client/bot/princess/BasicPathRanker.java @@ -33,11 +33,11 @@ public class BasicPathRanker extends PathRanker implements IPathRanker { // this is a value used to indicate how much we value the unit being at its destination private final int ARRIVED_AT_DESTINATION_FACTOR = 250; - + // this is a value used to indicate how much we dis-value the unit being destroyed as a result of // what it's doing private final int UNIT_DESTRUCTION_FACTOR = 1000; - + protected final DecimalFormat LOG_DECIMAL = new DecimalFormat("0.00", DecimalFormatSymbols.getInstance()); private final NumberFormat LOG_INT = NumberFormat.getIntegerInstance(); @@ -45,19 +45,19 @@ public class BasicPathRanker extends PathRanker implements IPathRanker { private PathEnumerator pathEnumerator; - // the best damage enemies could expect were I not here. Used to determine + // the best damage enemies could expect were I not here. Used to determine // whether they will target me. private Map bestDamageByEnemies; - + public BasicPathRanker(Princess owningPrincess) { super(owningPrincess); - + bestDamageByEnemies = new TreeMap<>(); - + LogManager.getLogger().debug("Using " + getOwner().getBehaviorSettings().getDescription() + " behavior"); } - + FireControl getFireControl(Entity entity) { return getOwner().getFireControl(entity); } @@ -65,7 +65,7 @@ FireControl getFireControl(Entity entity) { void setPathEnumerator(PathEnumerator pathEnumerator) { this.pathEnumerator = pathEnumerator; } - + PathEnumerator getPathEnumerator() { return pathEnumerator; } @@ -125,13 +125,13 @@ EntityEvaluationResponse evaluateUnmovedEnemy(Entity enemy, MovePath path, EntityEvaluationResponse returnResponse = new EntityEvaluationResponse(); - //Airborne aeros on ground maps always move after other units, and would require an + //Airborne aeros on ground maps always move after other units, and would require an // entirely different evaluation //TODO (low priority) implement a way to see if I can dodge aero units if (enemy.isAirborneAeroOnGroundMap()) { return returnResponse; } - + Coords finalCoords = path.getFinalCoords(); int myFacing = path.getFinalFacing(); Coords behind = finalCoords.translated((myFacing + 3) % 6); @@ -143,9 +143,9 @@ EntityEvaluationResponse evaluateUnmovedEnemy(Entity enemy, MovePath path, } int range = closest.distance(finalCoords); - // I would prefer if the enemy must end its move in my line of fire - // if so, I can guess that I may do some damage to it (cover - // notwithstanding). At the very least, I can force the enemy to + // I would prefer if the enemy must end its move in my line of fire + // if so, I can guess that I may do some damage to it (cover + // notwithstanding). At the very least, I can force the enemy to // take cover on its move. HexLine leftBounds; HexLine rightBounds; @@ -181,10 +181,11 @@ EntityEvaluationResponse evaluateUnmovedEnemy(Entity enemy, MovePath path, Math.ceil(enemy.getWeight() / 5.0) * damageDiscount); } - + return returnResponse; } + @Override protected List getPSRList(MovePath path) { return super.getPSRList(path); @@ -227,13 +228,13 @@ private double calculateFallMod(double successProbability, if (!losEffects.canSee()) { return 0; } - + Targetable actualTarget = path.getEntity(); - - // if the target is infantry protected by a building, we have to fire at the building instead. + + // if the target is infantry protected by a building, we have to fire at the building instead. if (losEffects.infantryProtected()) { actualTarget = new BuildingTarget(targetState.getPosition(), game.getBoard(), false); - targetState = new EntityState(actualTarget); + targetState = new EntityState(actualTarget); } int maxHeat = (enemy.getHeatCapacity() - enemy.heat) + (enemy.isAero() ? 0 : 5); @@ -284,7 +285,7 @@ private double calculateFallMod(double successProbability, return 0; } - // If I am an infantry unit that cannot both move and fire, and I am + // If I am an infantry unit that cannot both move and fire, and I am // moving, I can't do damage. boolean isZeroMpInfantry = me instanceof Infantry && (me.getWalkMP() == 0); if (isZeroMpInfantry && path.getMpUsed() > 0) { @@ -328,7 +329,7 @@ EntityEvaluationResponse evaluateMovedEnemy(Entity enemy, MovePath path, Game ga EntityEvaluationResponse returnResponse = new EntityEvaluationResponse(); int distance = enemy.getPosition().distance(path.getFinalCoords()); - + // How much damage can they do to me? double theirDamagePotential = calculateDamagePotential(enemy, new EntityState(enemy), path, new EntityState(path), distance, game); @@ -341,7 +342,7 @@ EntityEvaluationResponse evaluateMovedEnemy(Entity enemy, MovePath path, Game ga // How much damage can I do to them? returnResponse.setMyEstimatedDamage(calculateMyDamagePotential(path, enemy, distance, game)); - + // How much physical damage can I do to them? if (distance <= 1) { returnResponse.setMyEstimatedPhysicalDamage(calculateMyKickDamagePotential(path, enemy, game)); @@ -350,7 +351,8 @@ EntityEvaluationResponse evaluateMovedEnemy(Entity enemy, MovePath path, Game ga return returnResponse; } - // The further I am from a target, the lower this path ranks (weighted by + + // The further I am from a target, the lower this path ranks (weighted by // Hyper Aggression. protected double calculateAggressionMod(Entity movingUnit, MovePath path, Game game, StringBuilder formula) { @@ -367,7 +369,7 @@ protected double calculateAggressionMod(Entity movingUnit, MovePath path, Game g return aggressionMod; } - // The further I am from my teammates, the lower this path ranks (weighted + // The further I am from my teammates, the lower this path ranks (weighted // by Herd Mentality). protected double calculateHerdingMod(Coords friendsCoords, MovePath path, StringBuilder formula) { if (friendsCoords == null) { @@ -425,8 +427,8 @@ protected double calculateSelfPreservationMod(Entity movingUnit, MovePath path, Game game, StringBuilder formula) { - BehaviorType behaviorType = getOwner().getUnitBehaviorTracker().getBehaviorType(movingUnit, getOwner()); - + BehaviorType behaviorType = getOwner().getUnitBehaviorTracker().getBehaviorType(movingUnit, getOwner()); + if (behaviorType == BehaviorType.ForcedWithdrawal || behaviorType == BehaviorType.MoveToDestination) { int newDistanceToHome = distanceToHomeEdge(path.getFinalCoords(), @@ -434,17 +436,17 @@ protected double calculateSelfPreservationMod(Entity movingUnit, game); double selfPreservation = getOwner().getBehaviorSettings() .getSelfPreservationValue(); - + double selfPreservationMod = 0; - + // normally, we favor being closer to the edge we're trying to get to if (newDistanceToHome > 0) { selfPreservationMod = newDistanceToHome * selfPreservation; - // if this path gets us to the edge, we value it considerably more than we do paths that don't get us there + // if this path gets us to the edge, we value it considerably more than we do paths that don't get us there } else { selfPreservationMod = -ARRIVED_AT_DESTINATION_FACTOR; } - + formula.append(" - selfPreservationMod [") .append(LOG_DECIMAL.format(selfPreservationMod)) .append(" = ").append(LOG_DECIMAL.format(newDistanceToHome)) @@ -454,17 +456,18 @@ protected double calculateSelfPreservationMod(Entity movingUnit, } return 0.0; } - + /** * Tells me whether this path will result in me flying to a location * from which there is absolutely no way to remain on the board the following turn. - * + * * Not applicable for ground units, so the default behavior is to return 0. */ protected double calculateOffBoardMod(MovePath path) { return 0.0; } + /** * A path ranking */ @@ -481,13 +484,13 @@ protected RankedPath rankPath(MovePath path, Game game, int maxRange, double fal double successProbability = getMovePathSuccessProbability(pathCopy, formula); double utility = -calculateFallMod(successProbability, formula); - // look at all of my enemies + // look at all of my enemies FiringPhysicalDamage damageEstimate = new FiringPhysicalDamage(); - + double expectedDamageTaken = checkPathForHazards(pathCopy, movingUnit, game); - + expectedDamageTaken += MinefieldUtil.checkPathForMinefieldHazards(pathCopy); - + boolean extremeRange = game.getOptions().booleanOption(OptionsConstants.ADVCOMBAT_TACOPS_RANGE); boolean losRange = game.getOptions().booleanOption(OptionsConstants.ADVCOMBAT_TACOPS_LOS_RANGE); for (Entity enemy : enemies) { @@ -517,7 +520,7 @@ protected RankedPath rankPath(MovePath path, Game game, int maxRange, double fal // For units that have not moved this round eval = evaluateUnmovedEnemy(enemy, path, extremeRange, losRange); } - + // if we're not ignoring the enemy, we consider damage that we may do to them; // however, just because we're ignoring them doesn't mean they won't shoot at us. if (!getOwner().getBehaviorSettings().getIgnoredUnitTargets().contains(enemy.getId())) { @@ -528,7 +531,7 @@ protected RankedPath rankPath(MovePath path, Game game, int maxRange, double fal damageEstimate.physicalDamage = eval.getMyEstimatedPhysicalDamage(); } } - + expectedDamageTaken += eval.getEstimatedEnemyDamage(); } @@ -536,14 +539,14 @@ protected RankedPath rankPath(MovePath path, Game game, int maxRange, double fal if (!path.getEntity().isAirborne() && !path.getEntity().isAirborneVTOLorWIGE()) { double friendlyArtilleryDamage = 0; Map artyDamage = getOwner().getPathRankerState().getIncomingFriendlyArtilleryDamage(); - + if (!artyDamage.containsKey(path.getFinalCoords())) { friendlyArtilleryDamage = ArtilleryTargetingControl.evaluateIncomingArtilleryDamage(path.getFinalCoords(), getOwner()); artyDamage.put(path.getFinalCoords(), friendlyArtilleryDamage); } else { friendlyArtilleryDamage = artyDamage.get(path.getFinalCoords()); } - + expectedDamageTaken += friendlyArtilleryDamage; } @@ -556,12 +559,12 @@ protected RankedPath rankPath(MovePath path, Game game, int maxRange, double fal damageEstimate.physicalDamage = 0; } - // I can kick a different target than I shoot, so add physical to + // I can kick a different target than I shoot, so add physical to // total damage after I've looked at all enemies double maximumDamageDone = damageEstimate.firingDamage + damageEstimate.physicalDamage; - // My bravery modifier is based on my chance of getting to the - // firing position (successProbability), how much damage I can do + // My bravery modifier is based on my chance of getting to the + // firing position (successProbability), how much damage I can do // (weighted by bravery), less the damage I might take. double braveryValue = getOwner().getBehaviorSettings().getBraveryValue(); double braveryMod = successProbability * ((maximumDamageDone * braveryValue) - expectedDamageTaken); @@ -577,11 +580,11 @@ protected RankedPath rankPath(MovePath path, Game game, int maxRange, double fal // the only critters not subject to aggression and herding mods are // airborne aeros on ground maps, as they move incredibly fast if (!path.getEntity().isAirborneAeroOnGroundMap()) { - // The further I am from a target, the lower this path ranks + // The further I am from a target, the lower this path ranks // (weighted by Aggression slider). utility -= calculateAggressionMod(movingUnit, pathCopy, game, formula); - // The further I am from my teammates, the lower this path + // The further I am from my teammates, the lower this path // ranks (weighted by Herd Mentality). utility -= calculateHerdingMod(friendsCoords, pathCopy, formula); } @@ -592,10 +595,10 @@ protected RankedPath rankPath(MovePath path, Game game, int maxRange, double fal return new RankedPath(facingMod, pathCopy, formula.toString()); } utility -= facingMod; - + // If I need to flee the board, I want to get closer to my home edge. utility -= calculateSelfPreservationMod(movingUnit, pathCopy, game, formula); - + // if we're an aircraft, we want to de-value paths that will force us off the board // on the subsequent turn. utility -= utility * calculateOffBoardMod(pathCopy); @@ -604,7 +607,7 @@ protected RankedPath rankPath(MovePath path, Game game, int maxRange, double fal rankedPath.setExpectedDamage(maximumDamageDone); return rankedPath; } - + /** * Worker function that determines if a given enemy entity should be evaluated as if it has moved. */ @@ -613,7 +616,7 @@ protected boolean evaluateAsMoved(Entity enemy) { // somewhat pointless to try to predict their movement. return !enemy.isSelectableThisTurn() || enemy.isImmobile() || enemy.isAirborneAeroOnGroundMap(); } - + /** * Calculate who all other units would shoot at if I weren't around */ @@ -645,15 +648,15 @@ public void initUnitTurn(Entity unit, Game game) { protected void calcDamageToStrategicTargets(MovePath path, Game game, FireControlState fireControlState, FiringPhysicalDamage damageStructure) { - + for (int i = 0; i < fireControlState.getAdditionalTargets().size(); i++) { Targetable target = fireControlState.getAdditionalTargets().get(i); - + if (target.isOffBoard() || (target.getPosition() == null) || !game.getBoard().contains(target.getPosition())) { continue; // Skip targets not actually on the board. } - + FiringPlanCalculationParameters guess = new FiringPlanCalculationParameters.Builder() .buildGuess(path.getEntity(), @@ -663,12 +666,12 @@ protected void calcDamageToStrategicTargets(MovePath path, Game game, Entity.DOES_NOT_TRACK_HEAT, null); FiringPlan myFiringPlan = getFireControl(path.getEntity()).determineBestFiringPlan(guess); - + double myDamagePotential = myFiringPlan.getUtility(); if (myDamagePotential > damageStructure.firingDamage) { damageStructure.firingDamage = myDamagePotential; } - + if (path.getEntity() instanceof Mech) { PhysicalInfo myKick = new PhysicalInfo( path.getEntity(), new EntityState(path), target, @@ -684,7 +687,7 @@ PhysicalAttackType.RIGHT_KICK, game, getOwner(), } } } - + /** * Gives the distance to the closest enemy unit, or -1 if none exist. * The reason being that the closest enemy unit may be 0 away. @@ -721,6 +724,7 @@ int distanceToClosestEdge(Coords position, Game game) { return minimum; } + double checkPathForHazards(MovePath path, Entity movingUnit, Game game) { StringBuilder logMsg = new StringBuilder("Checking Path (") .append(path.toString()).append(") for hazards."); @@ -829,13 +833,13 @@ private double checkHexForHazards(Hex hex, Entity movingUnit, boolean endHex, Mo break; } } - + logMsg.append("\n\tTotal Hazard = ") .append(LOG_DECIMAL.format(hazardValue)); return hazardValue; } - + // Building collapse and basements are handled in PathRanker.validatePaths. private double calcBuildingHazard(MoveStep step, Entity movingUnit, boolean jumpLanding, Board board, StringBuilder logMsg) { @@ -869,11 +873,11 @@ private double calcBuildingHazard(MoveStep step, Entity movingUnit, boolean jump .append(LOG_DECIMAL.format(hazard)).append(")."); return hazard; } - + private double calcBridgeHazard(Entity movingUnit, Hex hex, MoveStep step, boolean jumpLanding, Board board, StringBuilder logMsg) { logMsg.append("\n\tCalculating bridge hazard: "); - + // if we are going to BWONGGG into a bridge from below, then it's treated as a building. // Otherwise, bridge collapse checks have already been handled in validatePaths int bridgeElevation = hex.terrainLevel(Terrains.BRIDGE_ELEV); @@ -881,7 +885,7 @@ private double calcBridgeHazard(Entity movingUnit, Hex hex, MoveStep step, boole (bridgeElevation <= (step.getElevation() + movingUnit.getHeight()))) { return calcBuildingHazard(step, movingUnit, jumpLanding, board, logMsg); } - + return 0; } @@ -896,7 +900,7 @@ private double calcIceHazard(Entity movingUnit, Hex hex, MoveStep step, MovePath return 0; } - // If there is no water under the ice, don't worry about breaking + // If there is no water under the ice, don't worry about breaking // through. if (hex.depth() < 1) { logMsg.append("No water under ice (0)."); @@ -912,7 +916,7 @@ private double calcIceHazard(Entity movingUnit, Hex hex, MoveStep step, MovePath breakthroughMod; logMsg.append("\n\t\tHazard value (") .append(LOG_DECIMAL.format(hazard)).append(")."); - return hazard; + return UNIT_DESTRUCTION_FACTOR; } private double calcWaterHazard(Entity movingUnit, Hex hex, MoveStep step, MovePath movePath, @@ -950,7 +954,7 @@ private double calcWaterHazard(Entity movingUnit, Hex hex, MoveStep step, MovePa return 0; } } - + // Most other units are automatically destroyed. if (!(movingUnit instanceof Mech || movingUnit instanceof Protomech || movingUnit instanceof BattleArmor)) { @@ -1008,7 +1012,7 @@ private double calcWaterHazard(Entity movingUnit, Hex hex, MoveStep step, MovePa continue; } - // Mechs or Protomechs having a head or torso breach is deadly. + // Mechs or Protomechs having a head or torso breach is deadly. // For other units, any breach is deadly. //noinspection ConstantConditions if (Mech.LOC_HEAD == loc || @@ -1158,7 +1162,7 @@ private double calcLavaHazard(boolean endHex, Entity movingUnit, return hazardValue; } - + /** * Simple data structure that holds a separate firing and physical damage number. *