Search in sources :

Example 51 with Unit

use of games.strategy.engine.data.Unit in project triplea by triplea-game.

the class MoveValidator method validateBasic.

private static MoveValidationResult validateBasic(final GameData data, final Collection<Unit> units, final Route route, final PlayerID player, final Collection<Unit> transportsToLoad, final Map<Unit, Collection<Unit>> newDependents, final MoveValidationResult result) {
    final boolean isEditMode = getEditMode(data);
    // make sure transports in the destination
    if (route.getEnd() != null && !route.getEnd().getUnits().containsAll(transportsToLoad) && !units.containsAll(transportsToLoad)) {
        return result.setErrorReturnResult("Transports not found in route end");
    }
    if (!isEditMode) {
        // Make sure all units are at least friendly
        for (final Unit unit : CollectionUtils.getMatches(units, Matches.enemyUnit(player, data))) {
            result.addDisallowedUnit("Can only move friendly units", unit);
        }
        // Ensure all air transports are included
        for (final Unit airTransport : newDependents.keySet()) {
            if (!units.contains(airTransport)) {
                for (final Unit unit : newDependents.get(airTransport)) {
                    if (units.contains(unit)) {
                        result.addDisallowedUnit("Not all units have enough movement", unit);
                    }
                }
            }
        }
        // Ignore transported units
        Collection<Unit> moveTest = new ArrayList<>(units);
        if (route.getStart().isWater()) {
            moveTest = MoveValidator.getNonLand(units);
        }
        final Map<Unit, Collection<Unit>> dependentsMap = getDependents(CollectionUtils.getMatches(units, Matches.unitCanTransport()));
        final Set<Unit> dependents = dependentsMap.values().stream().flatMap(Collection::stream).collect(Collectors.toSet());
        dependents.addAll(newDependents.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()));
        moveTest.removeAll(dependents);
        // Can only move owned units except transported units or allied air on carriers
        for (final Unit unit : CollectionUtils.getMatches(moveTest, Matches.unitIsOwnedBy(player).negate())) {
            if (!(UnitAttachment.get(unit.getType()).getCarrierCost() > 0 && data.getRelationshipTracker().isAllied(player, unit.getOwner()))) {
                result.addDisallowedUnit("Can only move own troops", unit);
            }
        }
        // Check if units have enough movement accounting for land transports
        // Land transports can either:
        // 1. Transport units on a 1-to-1 basis (have no capacity set)
        // 2. Transport like sea transports using capacity and cost
        data.acquireReadLock();
        try {
            int numLandTransportsWithoutCapacity = getNumLandTransportsWithoutCapacity(units, player);
            final IntegerMap<Unit> landTransportsWithCapacity = getLandTransportsWithCapacity(units, player);
            moveTest = TransportUtils.sortByTransportCostDescending(moveTest);
            for (final Unit unit : moveTest) {
                if (!Matches.unitHasEnoughMovementForRoute(route).test(unit)) {
                    boolean unitOk = false;
                    if (Matches.unitIsOwnedBy(player).negate().test(unit) && Matches.alliedUnit(player, data).test(unit) && Matches.unitTypeCanLandOnCarrier().test(unit.getType()) && moveTest.stream().anyMatch(Matches.unitIsAlliedCarrier(unit.getOwner(), data))) {
                        // this is so that if the unit is owned by any ally and it is cargo, then it will not count.
                        // (shouldn't it be a dependent in this case??)
                        unitOk = true;
                    } else if (Matches.unitHasNotMoved().test(unit) && Matches.unitIsLandTransportable().test(unit)) {
                        if (numLandTransportsWithoutCapacity > 0) {
                            numLandTransportsWithoutCapacity--;
                            unitOk = true;
                        } else {
                            for (final Unit transport : landTransportsWithCapacity.keySet()) {
                                final int cost = UnitAttachment.get((unit).getType()).getTransportCost();
                                if (cost <= landTransportsWithCapacity.getInt(transport)) {
                                    landTransportsWithCapacity.add(transport, -cost);
                                    unitOk = true;
                                    break;
                                }
                            }
                        }
                    }
                    if (!unitOk) {
                        result.addDisallowedUnit("Not all units have enough movement", unit);
                    }
                }
            }
        } finally {
            data.releaseReadLock();
        }
        // if there is a neutral in the middle must stop unless all are air or getNeutralsBlitzable
        if (route.hasNeutralBeforeEnd()) {
            if ((units.isEmpty() || !units.stream().allMatch(Matches.unitIsAir())) && !isNeutralsBlitzable(data)) {
                return result.setErrorReturnResult("Must stop land units when passing through neutral territories");
            }
        }
        // a territory effect can disallow unit types in
        if (units.stream().anyMatch(Matches.unitIsOfTypes(TerritoryEffectHelper.getUnitTypesForUnitsNotAllowedIntoTerritory(route.getSteps())))) {
            return result.setErrorReturnResult("Territory Effects disallow some units into " + (route.numberOfSteps() > 1 ? "these territories" : "this territory"));
        }
        // Check requiresUnitsToMove conditions
        Collection<Unit> requiresUnitsToMoveList = moveTest;
        if (route.isUnload()) {
            requiresUnitsToMoveList = units;
        }
        for (final Territory t : route.getAllTerritories()) {
            if (!requiresUnitsToMoveList.stream().allMatch(Matches.unitHasRequiredUnitsToMove(t, data))) {
                return result.setErrorReturnResult(t.getName() + " doesn't have the required units to allow moving the selected units into it");
            }
        }
    }
    // make sure that no non sea non transportable no carriable units end at sea
    if (route.getEnd() != null && route.getEnd().isWater()) {
        for (final Unit unit : MoveValidator.getUnitsThatCantGoOnWater(units)) {
            result.addDisallowedUnit("Not all units can end at water", unit);
        }
    }
    // if we are water make sure no land
    if (units.stream().anyMatch(Matches.unitIsSea())) {
        if (route.hasLand()) {
            for (final Unit unit : CollectionUtils.getMatches(units, Matches.unitIsSea())) {
                result.addDisallowedUnit("Sea units cannot go on land", unit);
            }
        }
    }
    // test for stack limits per unit
    if (route.getEnd() != null) {
        final Collection<Unit> unitsWithStackingLimits = CollectionUtils.getMatches(units, Matches.unitHasMovementLimit().or(Matches.unitHasAttackingLimit()));
        for (final Territory t : route.getSteps()) {
            final Collection<Unit> unitsAllowedSoFar = new ArrayList<>();
            if (Matches.isTerritoryEnemyAndNotUnownedWater(player, data).test(t) || t.getUnits().anyMatch(Matches.unitIsEnemyOf(data, player))) {
                for (final Unit unit : unitsWithStackingLimits) {
                    final UnitType ut = unit.getType();
                    int maxAllowed = UnitAttachment.getMaximumNumberOfThisUnitTypeToReachStackingLimit("attackingLimit", ut, t, player, data);
                    maxAllowed -= CollectionUtils.countMatches(unitsAllowedSoFar, Matches.unitIsOfType(ut));
                    if (maxAllowed > 0) {
                        unitsAllowedSoFar.add(unit);
                    } else {
                        result.addDisallowedUnit("UnitType " + ut.getName() + " has reached stacking limit", unit);
                    }
                }
                if (!PlayerAttachment.getCanTheseUnitsMoveWithoutViolatingStackingLimit("attackingLimit", units, t, player, data)) {
                    return result.setErrorReturnResult("Units Cannot Go Over Stacking Limit");
                }
            } else {
                for (final Unit unit : unitsWithStackingLimits) {
                    final UnitType ut = unit.getType();
                    int maxAllowed = UnitAttachment.getMaximumNumberOfThisUnitTypeToReachStackingLimit("movementLimit", ut, t, player, data);
                    maxAllowed -= CollectionUtils.countMatches(unitsAllowedSoFar, Matches.unitIsOfType(ut));
                    if (maxAllowed > 0) {
                        unitsAllowedSoFar.add(unit);
                    } else {
                        result.addDisallowedUnit("UnitType " + ut.getName() + " has reached stacking limit", unit);
                    }
                }
                if (!PlayerAttachment.getCanTheseUnitsMoveWithoutViolatingStackingLimit("movementLimit", units, t, player, data)) {
                    return result.setErrorReturnResult("Units Cannot Go Over Stacking Limit");
                }
            }
        }
    }
    // Don't allow move through impassable territories
    if (!isEditMode && route.anyMatch(Matches.territoryIsImpassable())) {
        return result.setErrorReturnResult(CANT_MOVE_THROUGH_IMPASSABLE);
    }
    if (canCrossNeutralTerritory(data, route, player, result).getError() != null) {
        return result;
    }
    if (isNeutralsImpassable(data) && !isNeutralsBlitzable(data) && !route.getMatches(Matches.territoryIsNeutralButNotWater()).isEmpty()) {
        return result.setErrorReturnResult(CANNOT_VIOLATE_NEUTRALITY);
    }
    return result;
}
Also used : Territory(games.strategy.engine.data.Territory) UnitType(games.strategy.engine.data.UnitType) ArrayList(java.util.ArrayList) Collection(java.util.Collection) ResourceCollection(games.strategy.engine.data.ResourceCollection) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit)

Example 52 with Unit

use of games.strategy.engine.data.Unit in project triplea by triplea-game.

the class MoveValidator method validateMove.

public static MoveValidationResult validateMove(final Collection<Unit> units, final Route route, final PlayerID player, final Collection<Unit> transportsToLoad, final Map<Unit, Collection<Unit>> newDependents, final boolean isNonCombat, final List<UndoableMove> undoableMoves, final GameData data) {
    final MoveValidationResult result = new MoveValidationResult();
    if (route.hasNoSteps()) {
        return result;
    }
    if (validateFirst(data, units, route, player, result).getError() != null) {
        return result;
    }
    if (isNonCombat) {
        if (validateNonCombat(data, units, route, player, result).getError() != null) {
            return result;
        }
    } else {
        if (validateCombat(data, units, route, player, result).getError() != null) {
            return result;
        }
    }
    if (validateNonEnemyUnitsOnPath(data, units, route, player, result).getError() != null) {
        return result;
    }
    if (validateBasic(data, units, route, player, transportsToLoad, newDependents, result).getError() != null) {
        return result;
    }
    if (AirMovementValidator.validateAirCanLand(data, units, route, player, result).getError() != null) {
        return result;
    }
    if (validateTransport(isNonCombat, data, undoableMoves, units, route, player, transportsToLoad, result).getError() != null) {
        return result;
    }
    if (validateParatroops(isNonCombat, data, units, route, player, result).getError() != null) {
        return result;
    }
    if (validateCanal(data, units, route, player, result).getError() != null) {
        return result;
    }
    if (validateFuel(data, units, route, player, result).getError() != null) {
        return result;
    }
    // Don't let the user move out of a battle zone, the exception is air units and unloading units into a battle zone
    if (AbstractMoveDelegate.getBattleTracker(data).hasPendingBattle(route.getStart(), false) && units.stream().anyMatch(Matches.unitIsNotAir())) {
        // if the units did not move into the territory, then they can move out this will happen if there is a submerged
        // sub in the area, and a different unit moved into the sea zone setting up a battle but the original unit can
        // still remain
        boolean unitsStartedInTerritory = true;
        for (final Unit unit : units) {
            if (AbstractMoveDelegate.getRouteUsedToMoveInto(undoableMoves, unit, route.getEnd()) != null) {
                unitsStartedInTerritory = false;
                break;
            }
        }
        if (!unitsStartedInTerritory) {
            final boolean unload = route.isUnload();
            final PlayerID endOwner = route.getEnd().getOwner();
            final boolean attack = !data.getRelationshipTracker().isAllied(endOwner, player) || AbstractMoveDelegate.getBattleTracker(data).wasConquered(route.getEnd());
            // unless they are unloading into another battle
            if (!(unload && attack)) {
                return result.setErrorReturnResult("Cannot move units out of battle zone");
            }
        }
    }
    return result;
}
Also used : PlayerID(games.strategy.engine.data.PlayerID) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit) MoveValidationResult(games.strategy.triplea.delegate.dataObjects.MoveValidationResult)

Example 53 with Unit

use of games.strategy.engine.data.Unit in project triplea by triplea-game.

the class MoveValidator method getMaxMovement.

public static int getMaxMovement(final Collection<Unit> units) {
    if (units.size() == 0) {
        throw new IllegalArgumentException("no units");
    }
    int max = 0;
    for (final Unit unit : units) {
        final int left = TripleAUnit.get(unit).getMovementLeft();
        max = Math.max(left, max);
    }
    return max;
}
Also used : TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit)

Example 54 with Unit

use of games.strategy.engine.data.Unit in project triplea by triplea-game.

the class MoveValidator method onlyIgnoredUnitsOnPath.

/**
 * Checks that there only transports, subs and/or allies on the route except at the end. AA and factory dont count as
 * enemy.
 */
static boolean onlyIgnoredUnitsOnPath(final Route route, final PlayerID player, final GameData data, final boolean ignoreRouteEnd) {
    final Predicate<Unit> subOnly = Matches.unitIsInfrastructure().or(Matches.unitIsSub()).or(Matches.enemyUnit(player, data).negate());
    final Predicate<Unit> transportOnly = Matches.unitIsInfrastructure().or(Matches.unitIsTransportButNotCombatTransport()).or(Matches.unitIsLand()).or(Matches.enemyUnit(player, data).negate());
    final Predicate<Unit> transportOrSubOnly = Matches.unitIsInfrastructure().or(Matches.unitIsTransportButNotCombatTransport()).or(Matches.unitIsLand()).or(Matches.unitIsSub()).or(Matches.enemyUnit(player, data).negate());
    final boolean getIgnoreTransportInMovement = isIgnoreTransportInMovement(data);
    final boolean getIgnoreSubInMovement = isIgnoreSubInMovement(data);
    final List<Territory> steps;
    if (ignoreRouteEnd) {
        steps = route.getMiddleSteps();
    } else {
        steps = route.getSteps();
    }
    // this sea zone (not sure if we need !ignoreRouteEnd here).
    if (steps.isEmpty() && route.numberOfStepsIncludingStart() == 1 && !ignoreRouteEnd) {
        steps.add(route.getStart());
    }
    boolean validMove = false;
    for (final Territory current : steps) {
        if (current.isWater()) {
            if (getIgnoreTransportInMovement && getIgnoreSubInMovement && current.getUnits().allMatch(transportOrSubOnly)) {
                validMove = true;
                continue;
            }
            if (getIgnoreTransportInMovement && !getIgnoreSubInMovement && current.getUnits().allMatch(transportOnly)) {
                validMove = true;
                continue;
            }
            if (!getIgnoreTransportInMovement && getIgnoreSubInMovement && current.getUnits().allMatch(subOnly)) {
                validMove = true;
                continue;
            }
            return false;
        }
    }
    return validMove;
}
Also used : Territory(games.strategy.engine.data.Territory) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit)

Example 55 with Unit

use of games.strategy.engine.data.Unit in project triplea by triplea-game.

the class MoveValidator method getCanCarry.

private static Collection<Unit> getCanCarry(final Unit carrier, final Collection<Unit> selectFrom, final PlayerID playerWhoIsDoingTheMovement, final GameData data) {
    final UnitAttachment ua = UnitAttachment.get(carrier.getType());
    final Collection<Unit> canCarry = new ArrayList<>();
    int available = ua.getCarrierCapacity();
    final TripleAUnit taCarrier = (TripleAUnit) carrier;
    for (final Unit plane : selectFrom) {
        final TripleAUnit taPlane = (TripleAUnit) plane;
        final UnitAttachment planeAttachment = UnitAttachment.get(plane.getType());
        final int cost = planeAttachment.getCarrierCost();
        if (available >= cost) {
            // this is to test if they started in the same sea zone or not, and its not a very good way of testing it.
            if ((taCarrier.getAlreadyMoved() == taPlane.getAlreadyMoved()) || (Matches.unitHasNotMoved().test(plane) && Matches.unitHasNotMoved().test(carrier)) || (Matches.unitIsOwnedBy(playerWhoIsDoingTheMovement).negate().test(plane) && Matches.alliedUnit(playerWhoIsDoingTheMovement, data).test(plane))) {
                available -= cost;
                canCarry.add(plane);
            }
        }
        if (available == 0) {
            break;
        }
    }
    return canCarry;
}
Also used : UnitAttachment(games.strategy.triplea.attachments.UnitAttachment) ArrayList(java.util.ArrayList) TripleAUnit(games.strategy.triplea.TripleAUnit) Unit(games.strategy.engine.data.Unit) TripleAUnit(games.strategy.triplea.TripleAUnit)

Aggregations

Unit (games.strategy.engine.data.Unit)447 TripleAUnit (games.strategy.triplea.TripleAUnit)301 Territory (games.strategy.engine.data.Territory)255 ArrayList (java.util.ArrayList)204 PlayerID (games.strategy.engine.data.PlayerID)135 GameData (games.strategy.engine.data.GameData)103 HashSet (java.util.HashSet)92 Test (org.junit.jupiter.api.Test)91 Route (games.strategy.engine.data.Route)89 UnitType (games.strategy.engine.data.UnitType)85 CompositeChange (games.strategy.engine.data.CompositeChange)64 HashMap (java.util.HashMap)64 IntegerMap (games.strategy.util.IntegerMap)61 UnitAttachment (games.strategy.triplea.attachments.UnitAttachment)58 Collection (java.util.Collection)58 ITestDelegateBridge (games.strategy.engine.data.ITestDelegateBridge)56 List (java.util.List)48 ScriptedRandomSource (games.strategy.engine.random.ScriptedRandomSource)47 Change (games.strategy.engine.data.Change)44 Set (java.util.Set)43