use of games.strategy.triplea.attachments.UnitAttachment in project triplea by triplea-game.
the class ProTerritoryManager method findScrambleOptions.
private static void findScrambleOptions(final PlayerID player, final Map<Territory, ProTerritory> moveMap) {
final GameData data = ProData.getData();
if (!Properties.getScrambleRulesInEffect(data)) {
return;
}
// Find scramble properties
final boolean fromIslandOnly = Properties.getScrambleFromIslandOnly(data);
final boolean toSeaOnly = Properties.getScrambleToSeaOnly(data);
final int maxScrambleDistance = StreamSupport.stream(data.getUnitTypeList().spliterator(), false).map(UnitAttachment::get).filter(UnitAttachment::getCanScramble).mapToInt(UnitAttachment::getMaxScrambleDistance).max().orElse(0);
final Predicate<Unit> airbasesCanScramble = Matches.unitIsEnemyOf(data, player).and(Matches.unitIsAirBase()).and(Matches.unitIsNotDisabled()).and(Matches.unitIsBeingTransported().negate());
final Predicate<Territory> canScramble = PredicateBuilder.of(Matches.territoryIsWater().or(Matches.isTerritoryEnemy(player, data))).and(Matches.territoryHasUnitsThatMatch(Matches.unitCanScramble().and(Matches.unitIsEnemyOf(data, player)).and(Matches.unitIsNotDisabled()))).and(Matches.territoryHasUnitsThatMatch(airbasesCanScramble)).andIf(fromIslandOnly, Matches.territoryIsIsland()).build();
// Find potential territories to scramble from
final HashMap<Territory, HashSet<Territory>> scrambleTerrs = new HashMap<>();
for (final Territory t : moveMap.keySet()) {
if (t.isWater() || !toSeaOnly) {
final HashSet<Territory> canScrambleFrom = new HashSet<>(CollectionUtils.getMatches(data.getMap().getNeighbors(t, maxScrambleDistance), canScramble));
if (!canScrambleFrom.isEmpty()) {
scrambleTerrs.put(t, canScrambleFrom);
}
}
}
if (scrambleTerrs.isEmpty()) {
return;
}
// Find potential max units that can be scrambled to each territory
for (final Territory to : scrambleTerrs.keySet()) {
for (final Territory from : scrambleTerrs.get(to)) {
// Find potential scramble units from territory
final Collection<Unit> airbases = from.getUnits().getMatches(airbasesCanScramble);
final int maxCanScramble = getMaxScrambleCount(airbases);
final Route toBattleRoute = data.getMap().getRoute_IgnoreEnd(from, to, Matches.territoryIsNotImpassable());
List<Unit> canScrambleAir = from.getUnits().getMatches(Matches.unitIsEnemyOf(data, player).and(Matches.unitCanScramble()).and(Matches.unitIsNotDisabled()).and(Matches.unitWasScrambled().negate()).and(Matches.unitCanScrambleOnRouteDistance(toBattleRoute)));
// Add max scramble units
if (maxCanScramble > 0 && !canScrambleAir.isEmpty()) {
if (maxCanScramble < canScrambleAir.size()) {
canScrambleAir.sort(Comparator.comparingDouble(o -> ProBattleUtils.estimateStrength(to, Collections.singletonList(o), new ArrayList<>(), false)));
canScrambleAir = canScrambleAir.subList(0, maxCanScramble);
}
moveMap.get(to).getMaxScrambleUnits().addAll(canScrambleAir);
}
}
}
}
use of games.strategy.triplea.attachments.UnitAttachment in project triplea by triplea-game.
the class ProTerritoryManager method getMaxScrambleCount.
private static int getMaxScrambleCount(final Collection<Unit> airbases) {
if (airbases.isEmpty() || !airbases.stream().allMatch(Matches.unitIsAirBase().and(Matches.unitIsNotDisabled()))) {
throw new IllegalStateException("All units must be viable airbases");
}
// find how many is the max this territory can scramble
int maxScrambled = 0;
for (final Unit base : airbases) {
final UnitAttachment ua = UnitAttachment.get(base.getType());
final int baseMax = ua.getMaxScrambleCount();
if (baseMax == -1) {
return Integer.MAX_VALUE;
}
maxScrambled += baseMax;
}
return maxScrambled;
}
use of games.strategy.triplea.attachments.UnitAttachment in project triplea by triplea-game.
the class DiceRoll method rollDiceNormal.
/**
* Roll dice for units per normal rules.
*/
private static DiceRoll rollDiceNormal(final List<Unit> unitsList, final boolean defending, final PlayerID player, final IDelegateBridge bridge, final IBattle battle, final String annotation, final Collection<TerritoryEffect> territoryEffects, final List<Unit> allEnemyUnitsAliveOrWaitingToDie) {
final List<Unit> units = new ArrayList<>(unitsList);
{
final Set<Unit> duplicatesCheckSet = new HashSet<>(unitsList);
if (units.size() != duplicatesCheckSet.size()) {
throw new IllegalStateException("Duplicate Units Detected: Original List:" + units + " HashSet:" + duplicatesCheckSet);
}
}
final GameData data = bridge.getData();
sortByStrength(units, defending);
final Territory location = battle.getTerritory();
final boolean isAmphibiousBattle = battle.isAmphibious();
final Collection<Unit> amphibiousLandAttackers = battle.getAmphibiousLandAttackers();
final Map<Unit, Tuple<Integer, Integer>> unitPowerAndRollsMap = DiceRoll.getUnitPowerAndRollsForNormalBattles(units, allEnemyUnitsAliveOrWaitingToDie, defending, false, data, location, territoryEffects, isAmphibiousBattle, amphibiousLandAttackers);
final Tuple<Integer, Integer> totalPowerAndRolls = getTotalPowerAndRolls(unitPowerAndRollsMap, data);
final int totalPower = totalPowerAndRolls.getFirst();
final int rollCount = totalPowerAndRolls.getSecond();
if (rollCount == 0) {
return new DiceRoll(new ArrayList<>(), 0, 0);
}
final int[] random = bridge.getRandom(data.getDiceSides(), rollCount, player, DiceType.COMBAT, annotation);
final boolean lhtrBombers = Properties.getLhtrHeavyBombers(data);
final List<Die> dice = new ArrayList<>();
int hitCount = 0;
int diceIndex = 0;
for (final Unit current : units) {
final UnitAttachment ua = UnitAttachment.get(current.getType());
final Tuple<Integer, Integer> powerAndRolls = unitPowerAndRollsMap.get(current);
final int strength = powerAndRolls.getFirst();
final int rolls = powerAndRolls.getSecond();
// lhtr heavy bombers take best of n dice for both attack and defense
if (rolls <= 0 || strength <= 0) {
continue;
}
if (rolls > 1 && (lhtrBombers || ua.getChooseBestRoll())) {
int smallestDieIndex = 0;
int smallestDie = data.getDiceSides();
for (int i = 0; i < rolls; i++) {
if (random[diceIndex + i] < smallestDie) {
smallestDie = random[diceIndex + i];
smallestDieIndex = i;
}
}
// zero based
final boolean hit = strength > random[diceIndex + smallestDieIndex];
dice.add(new Die(random[diceIndex + smallestDieIndex], strength, hit ? DieType.HIT : DieType.MISS));
for (int i = 0; i < rolls; i++) {
if (i != smallestDieIndex) {
dice.add(new Die(random[diceIndex + i], strength, DieType.IGNORED));
}
}
if (hit) {
hitCount++;
}
diceIndex += rolls;
} else {
for (int i = 0; i < rolls; i++) {
// zero based
final boolean hit = strength > random[diceIndex];
dice.add(new Die(random[diceIndex], strength, hit ? DieType.HIT : DieType.MISS));
if (hit) {
hitCount++;
}
diceIndex++;
}
}
}
final double expectedHits = ((double) totalPower) / data.getDiceSides();
final DiceRoll diceRoll = new DiceRoll(dice, hitCount, expectedHits);
bridge.getHistoryWriter().addChildToEvent(annotation + " : " + MyFormatter.asDice(random), diceRoll);
return diceRoll;
}
use of games.strategy.triplea.attachments.UnitAttachment in project triplea by triplea-game.
the class DiceRoll method getAAattackAndMaxDiceSides.
/**
* Returns a Tuple with 2 values, the first is the max attack, the second is the max dice sides for the AA unit with
* that attack value.
*/
public static Tuple<Integer, Integer> getAAattackAndMaxDiceSides(final Collection<Unit> defendingEnemyAa, final GameData data, final boolean defending) {
int highestAttack = 0;
final int diceSize = data.getDiceSides();
int chosenDiceSize = diceSize;
for (final Unit u : defendingEnemyAa) {
final UnitAttachment ua = UnitAttachment.get(u.getType());
int uaDiceSides = defending ? ua.getAttackAaMaxDieSides() : ua.getOffensiveAttackAaMaxDieSides();
if (uaDiceSides < 1) {
uaDiceSides = diceSize;
}
int attack = defending ? ua.getAttackAa(u.getOwner()) : ua.getOffensiveAttackAa(u.getOwner());
if (attack > uaDiceSides) {
attack = uaDiceSides;
}
if ((((float) attack) / ((float) uaDiceSides)) > (((float) highestAttack) / ((float) chosenDiceSize))) {
highestAttack = attack;
chosenDiceSize = uaDiceSides;
}
}
if (highestAttack > chosenDiceSize / 2 && chosenDiceSize > 1) {
// TODO: sadly the whole low luck section falls apart if AA are hitting at greater than half the
// value of dice, and I don't feel like rewriting it
highestAttack = chosenDiceSize / 2;
}
return Tuple.of(highestAttack, chosenDiceSize);
}
use of games.strategy.triplea.attachments.UnitAttachment in project triplea by triplea-game.
the class DiceRoll method getUnitPowerAndRollsForNormalBattles.
/**
* @param unitsGettingPowerFor
* should be sorted from weakest to strongest, before the method is called, for the actual battle.
*/
protected static Map<Unit, Tuple<Integer, Integer>> getUnitPowerAndRollsForNormalBattles(final List<Unit> unitsGettingPowerFor, final List<Unit> allEnemyUnitsAliveOrWaitingToDie, final boolean defending, final boolean bombing, final GameData data, final Territory location, final Collection<TerritoryEffect> territoryEffects, final boolean isAmphibiousBattle, final Collection<Unit> amphibiousLandAttackers, final Map<Unit, IntegerMap<Unit>> unitSupportPowerMap, final Map<Unit, IntegerMap<Unit>> unitSupportRollsMap) {
final Map<Unit, Tuple<Integer, Integer>> unitPowerAndRolls = new HashMap<>();
if (unitsGettingPowerFor == null || unitsGettingPowerFor.isEmpty()) {
return unitPowerAndRolls;
}
// get all supports, friendly and enemy
final Set<List<UnitSupportAttachment>> supportRulesFriendly = new HashSet<>();
final IntegerMap<UnitSupportAttachment> supportLeftFriendly = new IntegerMap<>();
final Map<UnitSupportAttachment, LinkedIntegerMap<Unit>> supportUnitsLeftFriendly = new HashMap<>();
getSupport(unitsGettingPowerFor, supportRulesFriendly, supportLeftFriendly, supportUnitsLeftFriendly, data, defending, true);
final Set<List<UnitSupportAttachment>> supportRulesEnemy = new HashSet<>();
final IntegerMap<UnitSupportAttachment> supportLeftEnemy = new IntegerMap<>();
final Map<UnitSupportAttachment, LinkedIntegerMap<Unit>> supportUnitsLeftEnemy = new HashMap<>();
getSupport(allEnemyUnitsAliveOrWaitingToDie, supportRulesEnemy, supportLeftEnemy, supportUnitsLeftEnemy, data, !defending, false);
// copy for rolls
final IntegerMap<UnitSupportAttachment> supportLeftFriendlyRolls = new IntegerMap<>(supportLeftFriendly);
final IntegerMap<UnitSupportAttachment> supportLeftEnemyRolls = new IntegerMap<>(supportLeftEnemy);
final Map<UnitSupportAttachment, LinkedIntegerMap<Unit>> supportUnitsLeftFriendlyRolls = new HashMap<>();
for (final UnitSupportAttachment usa : supportUnitsLeftFriendly.keySet()) {
supportUnitsLeftFriendlyRolls.put(usa, new LinkedIntegerMap<>(supportUnitsLeftFriendly.get(usa)));
}
final Map<UnitSupportAttachment, LinkedIntegerMap<Unit>> supportUnitsLeftEnemyRolls = new HashMap<>();
for (final UnitSupportAttachment usa : supportUnitsLeftEnemy.keySet()) {
supportUnitsLeftEnemyRolls.put(usa, new LinkedIntegerMap<>(supportUnitsLeftEnemy.get(usa)));
}
final int diceSides = data.getDiceSides();
for (final Unit current : unitsGettingPowerFor) {
// find our initial strength
int strength;
final UnitAttachment ua = UnitAttachment.get(current.getType());
if (defending) {
strength = ua.getDefense(current.getOwner());
if (isFirstTurnLimitedRoll(current.getOwner(), data)) {
strength = Math.min(1, strength);
} else {
strength += getSupport(current, supportRulesFriendly, supportLeftFriendly, supportUnitsLeftFriendly, unitSupportPowerMap, true, false);
}
strength += getSupport(current, supportRulesEnemy, supportLeftEnemy, supportUnitsLeftEnemy, unitSupportPowerMap, true, false);
} else {
strength = ua.getAttack(current.getOwner());
if (ua.getIsMarine() != 0 && isAmphibiousBattle) {
if (amphibiousLandAttackers.contains(current)) {
strength += ua.getIsMarine();
}
}
if (ua.getIsSea() && isAmphibiousBattle && Matches.territoryIsLand().test(location)) {
// change the strength to be bombard, not attack/defense, because this is a
strength = ua.getBombard();
// bombarding naval unit
}
strength += getSupport(current, supportRulesFriendly, supportLeftFriendly, supportUnitsLeftFriendly, unitSupportPowerMap, true, false);
strength += getSupport(current, supportRulesEnemy, supportLeftEnemy, supportUnitsLeftEnemy, unitSupportPowerMap, true, false);
}
strength += TerritoryEffectHelper.getTerritoryCombatBonus(current.getType(), territoryEffects, defending);
strength = Math.min(Math.max(strength, 0), diceSides);
// now determine our rolls
int rolls;
if (!bombing && strength == 0) {
rolls = 0;
} else {
if (defending) {
rolls = ua.getDefenseRolls(current.getOwner());
} else {
rolls = ua.getAttackRolls(current.getOwner());
}
rolls += getSupport(current, supportRulesFriendly, supportLeftFriendlyRolls, supportUnitsLeftFriendlyRolls, unitSupportRollsMap, false, true);
rolls += getSupport(current, supportRulesEnemy, supportLeftEnemyRolls, supportUnitsLeftEnemyRolls, unitSupportRollsMap, false, true);
rolls = Math.max(0, rolls);
if (rolls == 0) {
strength = 0;
}
}
unitPowerAndRolls.put(current, Tuple.of(strength, rolls));
}
return unitPowerAndRolls;
}
Aggregations