Search in sources :

Example 11 with Tuple

use of games.strategy.util.Tuple in project triplea by triplea-game.

the class DiceRoll method rollDiceLowLuck.

/**
 * Roll dice for units using low luck rules. Low luck rules based on rules in DAAK.
 */
private static DiceRoll rollDiceLowLuck(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();
    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 int power = getTotalPower(unitPowerAndRollsMap, data);
    if (power == 0) {
        return new DiceRoll(new ArrayList<>(0), 0, 0);
    }
    int hitCount = power / data.getDiceSides();
    final List<Die> dice = new ArrayList<>();
    // We need to roll dice for the fractional part of the dice.
    final int rollFor = power % data.getDiceSides();
    final int[] random;
    if (rollFor == 0) {
        random = new int[0];
    } else {
        random = bridge.getRandom(data.getDiceSides(), 1, player, DiceType.COMBAT, annotation);
        // zero based
        final boolean hit = rollFor > random[0];
        if (hit) {
            hitCount++;
        }
        dice.add(new Die(random[0], rollFor, hit ? DieType.HIT : DieType.MISS));
    }
    // Create DiceRoll object
    final double expectedHits = ((double) power) / data.getDiceSides();
    final DiceRoll diceRoll = new DiceRoll(dice, hitCount, expectedHits);
    bridge.getHistoryWriter().addChildToEvent(annotation + " : " + MyFormatter.asDice(random), diceRoll);
    return diceRoll;
}
Also used : Territory(games.strategy.engine.data.Territory) HashSet(java.util.HashSet) Set(java.util.Set) GameData(games.strategy.engine.data.GameData) ArrayList(java.util.ArrayList) Unit(games.strategy.engine.data.Unit) Tuple(games.strategy.util.Tuple)

Example 12 with Tuple

use of games.strategy.util.Tuple 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;
}
Also used : Territory(games.strategy.engine.data.Territory) HashSet(java.util.HashSet) Set(java.util.Set) GameData(games.strategy.engine.data.GameData) ArrayList(java.util.ArrayList) Unit(games.strategy.engine.data.Unit) UnitAttachment(games.strategy.triplea.attachments.UnitAttachment) Tuple(games.strategy.util.Tuple)

Example 13 with Tuple

use of games.strategy.util.Tuple 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;
}
Also used : LinkedIntegerMap(games.strategy.util.LinkedIntegerMap) IntegerMap(games.strategy.util.IntegerMap) HashMap(java.util.HashMap) UnitSupportAttachment(games.strategy.triplea.attachments.UnitSupportAttachment) Unit(games.strategy.engine.data.Unit) UnitAttachment(games.strategy.triplea.attachments.UnitAttachment) LinkedIntegerMap(games.strategy.util.LinkedIntegerMap) ArrayList(java.util.ArrayList) List(java.util.List) Tuple(games.strategy.util.Tuple) HashSet(java.util.HashSet)

Example 14 with Tuple

use of games.strategy.util.Tuple in project triplea by triplea-game.

the class DiceRoll method getTotalPowerAndRolls.

private static Tuple<Integer, Integer> getTotalPowerAndRolls(final Map<Unit, Tuple<Integer, Integer>> unitPowerAndRollsMap, final GameData data) {
    final int diceSides = data.getDiceSides();
    final boolean lhtrBombers = Properties.getLhtrHeavyBombers(data);
    // bonus is normally 1 for most games
    final int extraRollBonus = Math.max(1, data.getDiceSides() / 6);
    int totalPower = 0;
    int totalRolls = 0;
    for (final Entry<Unit, Tuple<Integer, Integer>> entry : unitPowerAndRollsMap.entrySet()) {
        int unitStrength = Math.min(Math.max(0, entry.getValue().getFirst()), diceSides);
        final int unitRolls = entry.getValue().getSecond();
        if (unitStrength <= 0 || unitRolls <= 0) {
            continue;
        }
        if (unitRolls == 1) {
            totalPower += unitStrength;
            totalRolls += unitRolls;
        } else {
            final UnitAttachment ua = UnitAttachment.get(entry.getKey().getType());
            if (lhtrBombers || ua.getChooseBestRoll()) {
                // LHTR means pick the best dice roll, which doesn't really make sense in LL. So instead, we will just add
                // +1 onto the power to
                // simulate the gains of having the best die picked.
                unitStrength += extraRollBonus * (unitRolls - 1);
                totalPower += Math.min(unitStrength, diceSides);
                totalRolls += unitRolls;
            } else {
                totalPower += unitRolls * unitStrength;
                totalRolls += unitRolls;
            }
        }
    }
    return Tuple.of(totalPower, totalRolls);
}
Also used : UnitAttachment(games.strategy.triplea.attachments.UnitAttachment) Unit(games.strategy.engine.data.Unit) Tuple(games.strategy.util.Tuple)

Example 15 with Tuple

use of games.strategy.util.Tuple in project triplea by triplea-game.

the class AbstractPlaceDelegate method freePlacementCapacity.

/**
 * frees the requested amount of capacity for the given producer by trying to hand over already made placements to
 * other territories.
 * This only works if one of the placements is done for another territory, more specific for a sea zone.
 * If such placements exists it will be tried to let them be done by other adjacent territories.
 *
 * @param producer
 *        territory that needs more placement capacity
 * @param freeSize
 *        amount of capacity that is requested
 */
protected void freePlacementCapacity(final Territory producer, final int freeSize, final Collection<Unit> unitsLeftToPlace, final Territory at, final PlayerID player) {
    // placements of the producer that could be redone by other territories
    final List<UndoablePlacement> redoPlacements = new ArrayList<>();
    // territories the producer produced for (but not itself) and the amount of units it produced
    final HashMap<Territory, Integer> redoPlacementsCount = new HashMap<>();
    // find map place territory -> possible free space for producer
    for (final UndoablePlacement placement : placements) {
        // find placement move of producer that can be taken over
        if (placement.getProducerTerritory().equals(producer)) {
            final Territory placeTerritory = placement.getPlaceTerritory();
            // units with requiresUnits are too difficult to mess with logically, so do not move them around at all
            if (placeTerritory.isWater() && !placeTerritory.equals(producer) && (!isUnitPlacementRestrictions() || placement.getUnits().stream().noneMatch(Matches.unitRequiresUnitsOnCreation()))) {
                // found placement move of producer that can be taken over
                // remember move and amount of placements in that territory
                redoPlacements.add(placement);
                redoPlacementsCount.merge(placeTerritory, placement.getUnits().size(), (a, b) -> a + b);
            }
        }
    }
    // let other producers take over placements of producer
    // remember placement move and new territory if a placement has to be split up
    final Collection<Tuple<UndoablePlacement, Territory>> splitPlacements = new ArrayList<>();
    int foundSpaceTotal = 0;
    for (final Entry<Territory, Integer> entry : redoPlacementsCount.entrySet()) {
        final Territory placeTerritory = entry.getKey();
        final int maxProductionThatCanBeTakenOverFromThisPlacement = entry.getValue();
        // find other producers that could produce for the placeTerritory
        final List<Territory> potentialNewProducers = getAllProducers(placeTerritory, player, unitsLeftToPlace);
        potentialNewProducers.remove(producer);
        potentialNewProducers.sort(getBestProducerComparator(placeTerritory, unitsLeftToPlace, player));
        // we can just free a certain amount or still need a certain amount of space
        final int maxSpaceToBeFree = Math.min(maxProductionThatCanBeTakenOverFromThisPlacement, freeSize - foundSpaceTotal);
        // space that got free this on this placeTerritory
        int spaceAlreadyFree = 0;
        for (final Territory potentialNewProducerTerritory : potentialNewProducers) {
            int leftToPlace = getMaxUnitsToBePlacedFrom(potentialNewProducerTerritory, unitsPlacedInTerritorySoFar(placeTerritory), placeTerritory, player);
            if (leftToPlace == -1) {
                leftToPlace = maxProductionThatCanBeTakenOverFromThisPlacement;
            }
            // find placements of the producer the potentialNewProducerTerritory can take over
            for (final UndoablePlacement placement : redoPlacements) {
                if (!placement.getPlaceTerritory().equals(placeTerritory)) {
                    continue;
                }
                final Collection<Unit> placedUnits = placement.getUnits();
                final int placementSize = placedUnits.size();
                // System.out.println("UndoPlacement: " + placement.getMoveLabel());
                if (placementSize <= leftToPlace) {
                    // potentialNewProducerTerritory can take over complete production
                    placement.setProducerTerritory(potentialNewProducerTerritory);
                    removeFromProducedMap(producer, placedUnits);
                    updateProducedMap(potentialNewProducerTerritory, placedUnits);
                    spaceAlreadyFree += placementSize;
                } else {
                    // potentialNewProducerTerritory can take over ONLY parts of the production
                    // remember placement and potentialNewProducerTerritory but try to avoid splitting a placement
                    splitPlacements.add(Tuple.of(placement, potentialNewProducerTerritory));
                }
                if (spaceAlreadyFree >= maxSpaceToBeFree) {
                    break;
                }
            }
            if (spaceAlreadyFree >= maxSpaceToBeFree) {
                break;
            }
        }
        foundSpaceTotal += spaceAlreadyFree;
        if (foundSpaceTotal >= freeSize) {
            break;
        }
    }
    // we had a bug where we tried to split the same undoable placement twice (it can only be undone once!)
    boolean unusedSplitPlacments = false;
    if (foundSpaceTotal < freeSize) {
        // we need to split some placement moves
        final Collection<UndoablePlacement> usedUnoablePlacements = new ArrayList<>();
        for (final Tuple<UndoablePlacement, Territory> tuple : splitPlacements) {
            final UndoablePlacement placement = tuple.getFirst();
            if (usedUnoablePlacements.contains(placement)) {
                unusedSplitPlacments = true;
                continue;
            }
            final Territory newProducer = tuple.getSecond();
            int leftToPlace = getMaxUnitsToBePlacedFrom(newProducer, unitsLeftToPlace, at, player);
            foundSpaceTotal += leftToPlace;
            // divide set of units that get placed
            final Collection<Unit> unitsForOldProducer = new ArrayList<>(placement.getUnits());
            final Collection<Unit> unitsForNewProducer = new ArrayList<>();
            for (final Unit unit : unitsForOldProducer) {
                if (leftToPlace == 0) {
                    break;
                }
                unitsForNewProducer.add(unit);
                --leftToPlace;
            }
            unitsForOldProducer.removeAll(unitsForNewProducer);
            // split move, by undo and creating two new ones
            if (!unitsForNewProducer.isEmpty()) {
                // there is a chance we have 2 or more splitPlacements that are using the same placement (trying to split the
                // same placement).
                // So we must make sure that after we undo it the first time, it can never be undone again.
                usedUnoablePlacements.add(placement);
                undoMove(placement.getIndex());
                performPlaceFrom(newProducer, unitsForNewProducer, placement.getPlaceTerritory(), player);
                performPlaceFrom(producer, unitsForOldProducer, placement.getPlaceTerritory(), player);
            }
        }
    }
    if (foundSpaceTotal < freeSize && unusedSplitPlacments) {
        freePlacementCapacity(producer, (freeSize - foundSpaceTotal), unitsLeftToPlace, at, player);
    }
}
Also used : Territory(games.strategy.engine.data.Territory) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit) Tuple(games.strategy.util.Tuple)

Aggregations

Tuple (games.strategy.util.Tuple)24 ArrayList (java.util.ArrayList)20 Unit (games.strategy.engine.data.Unit)18 Territory (games.strategy.engine.data.Territory)11 HashSet (java.util.HashSet)11 UnitType (games.strategy.engine.data.UnitType)10 IntegerMap (games.strategy.util.IntegerMap)10 HashMap (java.util.HashMap)9 GameData (games.strategy.engine.data.GameData)8 PlayerID (games.strategy.engine.data.PlayerID)8 TripleAUnit (games.strategy.triplea.TripleAUnit)8 UnitAttachment (games.strategy.triplea.attachments.UnitAttachment)7 Collection (java.util.Collection)7 List (java.util.List)7 Set (java.util.Set)7 CompositeChange (games.strategy.engine.data.CompositeChange)6 Change (games.strategy.engine.data.Change)5 ProductionRule (games.strategy.engine.data.ProductionRule)4 ResourceCollection (games.strategy.engine.data.ResourceCollection)4 GridBagConstraints (java.awt.GridBagConstraints)4