package games.strategy.triplea.delegate; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import games.strategy.engine.data.GameData; import games.strategy.engine.data.GameStep; import games.strategy.engine.data.PlayerID; import games.strategy.engine.data.RelationshipTracker; import games.strategy.engine.data.RelationshipTracker.Relationship; import games.strategy.engine.data.RelationshipType; import games.strategy.engine.data.Resource; import games.strategy.engine.data.Route; import games.strategy.engine.data.Territory; import games.strategy.engine.data.Unit; import games.strategy.engine.data.UnitType; import games.strategy.triplea.Constants; import games.strategy.triplea.Properties; import games.strategy.triplea.TripleAUnit; import games.strategy.triplea.attachments.AbstractUserActionAttachment; import games.strategy.triplea.attachments.ICondition; import games.strategy.triplea.attachments.PlayerAttachment; import games.strategy.triplea.attachments.PoliticalActionAttachment; import games.strategy.triplea.attachments.RulesAttachment; import games.strategy.triplea.attachments.TechAttachment; import games.strategy.triplea.attachments.TerritoryAttachment; import games.strategy.triplea.attachments.UnitAttachment; import games.strategy.triplea.attachments.UnitSupportAttachment; import games.strategy.triplea.util.TransportUtils; import games.strategy.triplea.util.UnitCategory; import games.strategy.triplea.util.UnitSeperator; import games.strategy.util.CompositeMatch; import games.strategy.util.CompositeMatchAnd; import games.strategy.util.CompositeMatchOr; import games.strategy.util.IntegerMap; import games.strategy.util.InverseMatch; import games.strategy.util.Match; import games.strategy.util.Tuple; import games.strategy.util.Util; /** * Useful match interfaces. * * <p> * Rather than writing code like, * </p> * * <pre> * boolean hasLand = false; * Iterator iter = someCollection.iterator(); * while (iter.hasNext()) { * Unit unit = (Unit) iter.next(); * UnitAttachment ua = UnitAttachment.get(unit.getType()); * if (ua.isAir) { * hasAir = true; * break; * } * } * </pre> * * <p> * You can write code like, * </p> * * <pre> * boolean hasLand = Match.someMatch(someCollection, Matches.UnitIsAir); * </pre> * * <p> * The benefits should be obvious to any right minded person. * </p> */ public class Matches { public static final Match<Object> IsTerritory = new Match<Object>() { @Override public boolean match(final Object o) { return o != null && o instanceof Territory; } }; public static final Match<Unit> UnitHasMoreThanOneHitPointTotal = new Match<Unit>() { @Override public boolean match(final Unit unit) { return UnitTypeHasMoreThanOneHitPointTotal.match(unit.getType()); } }; public static final Match<UnitType> UnitTypeHasMoreThanOneHitPointTotal = new Match<UnitType>() { @Override public boolean match(final UnitType ut) { final UnitAttachment ua = UnitAttachment.get(ut); return ua.getHitPoints() > 1; } }; public static final Match<Unit> UnitHasTakenSomeDamage = new Match<Unit>() { @Override public boolean match(final Unit unit) { return unit.getHits() > 0; } }; public static final Match<Unit> UnitHasNotTakenAnyDamage = new InverseMatch<>(UnitHasTakenSomeDamage); public static final Match<Unit> UnitHasOnlyOneHitPointLeft = new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getHitPoints() - unit.getHits() <= 1; } }; public static final Match<Unit> UnitIsSea = new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getIsSea(); } }; public static final Match<Unit> UnitIsSub = new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getIsSub(); } }; public static final Match<Unit> UnitIsNotSub = new InverseMatch<>(UnitIsSub); public static final Match<Unit> UnitIsCombatTransport = new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); return (ua.getIsCombatTransport() && ua.getIsSea()); } }; public static final Match<Unit> UnitIsNotCombatTransport = new InverseMatch<>(UnitIsCombatTransport); public static final Match<Unit> UnitIsTransportButNotCombatTransport = new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); return (ua.getTransportCapacity() != -1 && ua.getIsSea() && !ua.getIsCombatTransport()); } }; public static final Match<Unit> UnitIsNotTransportButCouldBeCombatTransport = new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); if (ua.getTransportCapacity() == -1) { return true; } else { return ua.getIsCombatTransport() && ua.getIsSea(); } } }; public static final Match<Unit> UnitIsDestroyer = new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getIsDestroyer(); } }; public static final Match<UnitType> UnitTypeIsDestroyer = new Match<UnitType>() { @Override public boolean match(final UnitType type) { final UnitAttachment ua = UnitAttachment.get(type); return ua.getIsDestroyer(); } }; public static final Match<Unit> UnitIsTransport = new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); return (ua.getTransportCapacity() != -1 && ua.getIsSea()); } }; public static final Match<Unit> UnitIsNotTransport = UnitIsTransport.invert(); public static final Match<Unit> UnitIsTransportAndNotDestroyer = new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); return (!Matches.UnitIsDestroyer.match(unit) && ua.getTransportCapacity() != -1 && ua.getIsSea()); } }; public static final Match<UnitType> UnitTypeIsStrategicBomber = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitAttachment ua = UnitAttachment.get(obj); if (ua == null) { return false; } return ua.getIsStrategicBomber(); } }; public static final Match<Unit> UnitIsStrategicBomber = new Match<Unit>() { @Override public boolean match(final Unit obj) { return UnitTypeIsStrategicBomber.match(obj.getType()); } }; public static final Match<Unit> UnitIsNotStrategicBomber = new InverseMatch<>(UnitIsStrategicBomber); public static final Match<UnitType> UnitTypeCanLandOnCarrier = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitAttachment ua = UnitAttachment.get(obj); if (ua == null) { return false; } return ua.getCarrierCost() != -1; } }; public static final Match<UnitType> UnitTypeCannotLandOnCarrier = new InverseMatch<>(UnitTypeCanLandOnCarrier); public static final Match<Unit> unitHasMoved = new Match<Unit>() { @Override public boolean match(final Unit obj) { final Unit unit = obj; return TripleAUnit.get(unit).getAlreadyMoved() > 0; } }; public static final Match<Unit> unitHasNotMoved = new InverseMatch<>(unitHasMoved); public static Match<Unit> unitCanAttack(final PlayerID id) { return new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); if (ua.getMovement(id) <= 0) { return false; } return ua.getAttack(id) > 0; } }; } public static Match<UnitType> unitTypeCanAttack(final PlayerID id) { return new Match<UnitType>() { @Override public boolean match(final UnitType uT) { final UnitAttachment ua = UnitAttachment.get(uT); if (ua.getMovement(id) <= 0) { return false; } return ua.getAttack(id) > 0; } }; } public static Match<Unit> unitHasAttackValueOfAtLeast(final int attackValue) { return new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getAttack(unit.getOwner()) >= attackValue; } }; } public static Match<Unit> unitHasDefendValueOfAtLeast(final int defendValue) { return new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getDefense(unit.getOwner()) >= defendValue; } }; } public static Match<Unit> unitIsEnemyOf(final GameData data, final PlayerID player) { return new Match<Unit>() { @Override public boolean match(final Unit u) { return data.getRelationshipTracker().isAtWar(u.getOwner(), player); } }; } public static final Match<Unit> UnitIsNotSea = new Match<Unit>() { @Override public boolean match(final Unit obj) { final Unit unit = obj; final UnitAttachment ua = UnitAttachment.get(unit.getType()); return !ua.getIsSea(); } }; public static final Match<UnitType> UnitTypeIsSea = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitAttachment ua = UnitAttachment.get(obj); return ua.getIsSea(); } }; public static final Match<UnitType> UnitTypeIsNotSea = new Match<UnitType>() { @Override public boolean match(final UnitType type) { final UnitAttachment ua = UnitAttachment.get(type); return !ua.getIsSea(); } }; public static final Match<UnitType> UnitTypeIsSeaOrAir = new Match<UnitType>() { @Override public boolean match(final UnitType type) { final UnitAttachment ua = UnitAttachment.get(type); return ua.getIsSea() || ua.getIsAir(); } }; public static final Match<UnitType> UnitTypeIsCarrier = new Match<UnitType>() { @Override public boolean match(final UnitType type) { final UnitAttachment ua = UnitAttachment.get(type); return (ua.getCarrierCapacity() != -1); } }; public static final Match<Unit> UnitIsAir = new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getIsAir(); } }; public static final Match<Unit> UnitIsNotAir = new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); return !ua.getIsAir(); } }; public static Match<UnitType> unitTypeCanBombard(final PlayerID id) { return new Match<UnitType>() { @Override public boolean match(final UnitType type) { final UnitAttachment ua = UnitAttachment.get(type); return ua.getCanBombard(id); } }; } public static Match<Unit> UnitCanBeGivenByTerritoryTo(final PlayerID player) { return new Match<Unit>() { @Override public boolean match(final Unit o) { final Unit unit = o; final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getCanBeGivenByTerritoryTo().contains(player); } }; } public static Match<Unit> UnitCanBeCapturedOnEnteringToInThisTerritory(final PlayerID player, final Territory terr, final GameData data) { return new Match<Unit>() { @Override public boolean match(final Unit o) { if (!games.strategy.triplea.Properties.getCaptureUnitsOnEnteringTerritory(data)) { return false; } final Unit unit = o; final PlayerID unitOwner = unit.getOwner(); final UnitAttachment ua = UnitAttachment.get(unit.getType()); final boolean unitCanBeCapturedByPlayer = ua.getCanBeCapturedOnEnteringBy().contains(player); final TerritoryAttachment ta = TerritoryAttachment.get(terr); if (ta == null) { return false; } if (ta.getCaptureUnitOnEnteringBy() == null) { return false; } final boolean territoryCanHaveUnitsThatCanBeCapturedByPlayer = ta.getCaptureUnitOnEnteringBy().contains(player); final PlayerAttachment pa = PlayerAttachment.get(unitOwner); if (pa == null) { return false; } if (pa.getCaptureUnitOnEnteringBy() == null) { return false; } final boolean unitOwnerCanLetUnitsBeCapturedByPlayer = pa.getCaptureUnitOnEnteringBy().contains(player); return (unitCanBeCapturedByPlayer && territoryCanHaveUnitsThatCanBeCapturedByPlayer && unitOwnerCanLetUnitsBeCapturedByPlayer); } }; } public static Match<Unit> UnitDestroyedWhenCapturedByOrFrom(final PlayerID playerBY) { return new Match<Unit>() { @Override public boolean match(final Unit o) { final Match<Unit> byOrFrom = new CompositeMatchOr<>(UnitDestroyedWhenCapturedBy(playerBY), UnitDestroyedWhenCapturedFrom()); return byOrFrom.match(o); } }; } private static Match<Unit> UnitDestroyedWhenCapturedBy(final PlayerID playerBY) { return new Match<Unit>() { @Override public boolean match(final Unit u) { final UnitAttachment ua = UnitAttachment.get(u.getType()); if (ua.getDestroyedWhenCapturedBy().isEmpty()) { return false; } for (final Tuple<String, PlayerID> tuple : ua.getDestroyedWhenCapturedBy()) { if (tuple.getFirst().equals("BY") && tuple.getSecond().equals(playerBY)) { return true; } } return false; } }; } private static Match<Unit> UnitDestroyedWhenCapturedFrom() { return new Match<Unit>() { @Override public boolean match(final Unit u) { final UnitAttachment ua = UnitAttachment.get(u.getType()); if (ua.getDestroyedWhenCapturedBy().isEmpty()) { return false; } for (final Tuple<String, PlayerID> tuple : ua.getDestroyedWhenCapturedBy()) { if (tuple.getFirst().equals("FROM") && tuple.getSecond().equals(u.getOwner())) { return true; } } return false; } }; } public static final Match<Unit> UnitIsAirBase = new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getIsAirBase(); } }; public static final Match<Unit> UnitCanBeDamaged = new Match<Unit>() { @Override public boolean match(final Unit unit) { return UnitTypeCanBeDamaged.match(unit.getType()); } }; public static final Match<UnitType> UnitTypeCanBeDamaged = new Match<UnitType>() { @Override public boolean match(final UnitType ut) { final UnitAttachment ua = UnitAttachment.get(ut); return ua.getCanBeDamaged(); } }; public static Match<Unit> UnitIsAtMaxDamageOrNotCanBeDamaged(final Territory t) { return new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); if (!ua.getCanBeDamaged()) { return true; } if (games.strategy.triplea.Properties.getDamageFromBombingDoneToUnitsInsteadOfTerritories(unit.getData())) { final TripleAUnit taUnit = (TripleAUnit) unit; return taUnit.getUnitDamage() >= taUnit.getHowMuchDamageCanThisUnitTakeTotal(unit, t); } else { return false; } } }; } public static Match<Unit> UnitIsLegalBombingTargetBy(final Unit bomberOrRocket) { return new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(bomberOrRocket.getType()); final HashSet<UnitType> allowedTargets = ua.getBombingTargets(bomberOrRocket.getData()); return allowedTargets == null || allowedTargets.contains(unit.getType()); } }; } public static Match<Unit> UnitHasTakenSomeBombingUnitDamage = new Match<Unit>() { @Override public boolean match(final Unit unit) { final TripleAUnit taUnit = (TripleAUnit) unit; return taUnit.getUnitDamage() > 0; } }; public static Match<Unit> UnitHasNotTakenAnyBombingUnitDamage = new InverseMatch<>(UnitHasTakenSomeBombingUnitDamage); public static Match<Unit> UnitIsDisabled = new Match<Unit>() { @Override public boolean match(final Unit unit) { if (!UnitCanBeDamaged.match(unit)) { return false; } if (!games.strategy.triplea.Properties.getDamageFromBombingDoneToUnitsInsteadOfTerritories(unit.getData())) { return false; } final UnitAttachment ua = UnitAttachment.get(unit.getType()); final TripleAUnit taUnit = (TripleAUnit) unit; if (ua.getMaxOperationalDamage() < 0) { // factories may or may not have max operational damage set, so we must still determine here // assume that if maxOperationalDamage < 0, then the max damage must be based on the territory value (if the // damage >= production of // territory, then we are disabled) // TerritoryAttachment ta = TerritoryAttachment.get(t); // return taUnit.getUnitDamage() >= ta.getProduction(); return false; } // only greater than. if == then we can still operate return taUnit.getUnitDamage() > ua.getMaxOperationalDamage(); } }; public static Match<Unit> UnitIsNotDisabled = new InverseMatch<>(UnitIsDisabled); public static final Match<Unit> UnitCanDieFromReachingMaxDamage = new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); if (!ua.getCanBeDamaged()) { return false; } return ua.getCanDieFromReachingMaxDamage(); } }; public static final Match<Unit> UnitIsInfrastructure = new Match<Unit>() { @Override public boolean match(final Unit unit) { return UnitTypeIsInfrastructure.match(unit.getType()); } }; public static final Match<Unit> UnitIsNotInfrastructure = new InverseMatch<>(UnitIsInfrastructure); public static final Match<UnitType> UnitTypeIsInfrastructure = new Match<UnitType>() { @Override public boolean match(final UnitType ut) { final UnitAttachment ua = UnitAttachment.get(ut); return ua.getIsInfrastructure(); } }; /** * Checks for having attack/defense and for providing support. Does not check for having AA ability. */ public static Match<Unit> UnitIsSupporterOrHasCombatAbility(final boolean attack) { return new Match<Unit>() { @Override public boolean match(final Unit unit) { return Matches.UnitTypeIsSupporterOrHasCombatAbility(attack, unit.getOwner()).match(unit.getType()); } }; } /** * Checks for having attack/defense and for providing support. Does not check for having AA ability. */ private static Match<UnitType> UnitTypeIsSupporterOrHasCombatAbility(final boolean attack, final PlayerID player) { return new Match<UnitType>() { @Override public boolean match(final UnitType ut) { // if unit has attack or defense, return true final UnitAttachment ua = UnitAttachment.get(ut); if (attack && ua.getAttack(player) > 0) { return true; } if (!attack && ua.getDefense(player) > 0) { return true; } // if unit can support other units, return true return !UnitSupportAttachment.get(ut).isEmpty(); } }; } public static Match<UnitSupportAttachment> UnitSupportAttachmentCanBeUsedByPlayer(final PlayerID player) { return new Match<UnitSupportAttachment>() { @Override public boolean match(final UnitSupportAttachment usa) { return usa.getPlayers().contains(player); } }; } public static final Match<Unit> UnitCanScramble = new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getCanScramble(); } }; public static final Match<Unit> UnitWasScrambled = new Match<Unit>() { @Override public boolean match(final Unit obj) { final TripleAUnit taUnit = (TripleAUnit) obj; return taUnit.getWasScrambled(); } }; public static final Match<Unit> UnitWasInAirBattle = new Match<Unit>() { @Override public boolean match(final Unit obj) { final TripleAUnit taUnit = (TripleAUnit) obj; return taUnit.getWasInAirBattle(); } }; public static final Match<Territory> TerritoryIsIsland = new Match<Territory>() { @Override public boolean match(final Territory t) { final Collection<Territory> neighbors = t.getData().getMap().getNeighbors(t); return neighbors.size() == 1 && TerritoryIsWater.match(neighbors.iterator().next()); } }; public static Match<Unit> unitCanBombard(final PlayerID id) { return new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getCanBombard(id); } }; } public static final Match<Unit> UnitCanBlitz = new Match<Unit>() { @Override public boolean match(final Unit obj) { final Unit unit = obj; final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getCanBlitz(obj.getOwner()); } }; public static final Match<Unit> UnitIsLandTransport = new Match<Unit>() { @Override public boolean match(final Unit obj) { final Unit unit = obj; final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getIsLandTransport(); } }; public static Match<Unit> UnitIsNotInfrastructureAndNotCapturedOnEntering(final PlayerID player, final Territory terr, final GameData data) { return new Match<Unit>() { @Override public boolean match(final Unit obj) { final Unit unit = obj; final UnitAttachment ua = UnitAttachment.get(unit.getType()); return !ua.getIsInfrastructure() && !UnitCanBeCapturedOnEnteringToInThisTerritory(player, terr, data).match(unit); } }; } public static final Match<Unit> UnitIsSuicide = new Match<Unit>() { @Override public boolean match(final Unit obj) { final Unit unit = obj; final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getIsSuicide(); } }; public static final Match<Unit> UnitIsKamikaze = new Match<Unit>() { @Override public boolean match(final Unit obj) { final Unit unit = obj; final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getIsKamikaze(); } }; public static final Match<UnitType> UnitTypeIsAir = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitType type = obj; final UnitAttachment ua = UnitAttachment.get(type); return ua.getIsAir(); } }; public static final Match<UnitType> UnitTypeIsNotAir = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitType type = obj; final UnitAttachment ua = UnitAttachment.get(type); return !ua.getIsAir(); } }; public static final Match<Unit> UnitCanLandOnCarrier = new Match<Unit>() { @Override public boolean match(final Unit obj) { final Unit unit = obj; final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getCarrierCost() != -1; } }; public static final Match<Unit> UnitIsCarrier = new Match<Unit>() { @Override public boolean match(final Unit obj) { final Unit unit = obj; final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getCarrierCapacity() != -1; } }; public static Match<Territory> TerritoryHasOwnedCarrier(final PlayerID player) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return t.getUnits() .someMatch(new CompositeMatchAnd<>(Matches.unitIsOwnedBy(player), Matches.UnitIsCarrier)); } }; } public static Match<Unit> UnitIsAlliedCarrier(final PlayerID player, final GameData data) { return new Match<Unit>() { @Override public boolean match(final Unit obj) { final Unit unit = obj; final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getCarrierCapacity() != -1 && data.getRelationshipTracker().isAllied(player, obj.getOwner()); } }; } public static final Match<Unit> UnitCanBeTransported = new Match<Unit>() { @Override public boolean match(final Unit obj) { final Unit unit = obj; final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getTransportCost() != -1; } }; public static final Match<Unit> UnitCanNotBeTransported = new InverseMatch<>(UnitCanBeTransported); public static final Match<Unit> UnitWasAmphibious = new Match<Unit>() { @Override public boolean match(final Unit obj) { final TripleAUnit taUnit = (TripleAUnit) obj; return taUnit.getWasAmphibious(); } }; public static final Match<Unit> UnitWasNotAmphibious = new InverseMatch<>(UnitWasAmphibious); public static final Match<Unit> UnitWasInCombat = new Match<Unit>() { @Override public boolean match(final Unit obj) { final TripleAUnit taUnit = (TripleAUnit) obj; return taUnit.getWasInCombat(); } }; public static final Match<Unit> UnitWasUnloadedThisTurn = new Match<Unit>() { @Override public boolean match(final Unit obj) { final TripleAUnit taUnit = (TripleAUnit) obj; return taUnit.getUnloadedTo() != null; } }; public static final Match<Unit> UnitWasLoadedThisTurn = new Match<Unit>() { @Override public boolean match(final Unit obj) { final TripleAUnit taUnit = (TripleAUnit) obj; return taUnit.getWasLoadedThisTurn(); } }; public static final Match<Unit> UnitWasNotLoadedThisTurn = new InverseMatch<>(UnitWasLoadedThisTurn); public static final Match<Unit> UnitCanTransport = new Match<Unit>() { @Override public boolean match(final Unit obj) { final Unit unit = obj; final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getTransportCapacity() != -1; } }; public static final Match<UnitType> UnitTypeCanTransport = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitType type = obj; final UnitAttachment ua = UnitAttachment.get(type); return ua.getTransportCapacity() != -1; } }; public static final Match<UnitType> UnitTypeCanBeTransported = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitType type = obj; final UnitAttachment ua = UnitAttachment.get(type); return ua.getTransportCost() != -1; } }; public static final Match<Unit> UnitCanProduceUnits = new Match<Unit>() { @Override public boolean match(final Unit obj) { return UnitTypeCanProduceUnits.match(obj.getType()); } }; public static final Match<UnitType> UnitTypeCanProduceUnits = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitAttachment ua = UnitAttachment.get(obj); return ua.getCanProduceUnits(); } }; public static final Match<Unit> UnitCanNotProduceUnits = new InverseMatch<>(UnitCanProduceUnits); public static final Match<UnitType> UnitTypeIsInfrastructureButNotAAofAnyKind = new Match<UnitType>() { @Override public boolean match(final UnitType type) { return UnitTypeIsInfrastructure.match(type) && !UnitTypeIsAAforAnything.match(type); } }; public static final Match<UnitType> UnitTypeIsInfantry = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitType type = obj; final UnitAttachment ua = UnitAttachment.get(type); return ua.getIsInfantry(); } }; public static final Match<UnitType> UnitTypeIsArtillery = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitType type = obj; final UnitAttachment ua = UnitAttachment.get(type); return ua.getArtillery(); } }; public static final Match<Unit> UnitHasMaxBuildRestrictions = new Match<Unit>() { @Override public boolean match(final Unit obj) { final UnitType type = obj.getType(); return UnitTypeHasMaxBuildRestrictions.match(type); } }; public static final Match<UnitType> UnitTypeHasMaxBuildRestrictions = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitType type = obj; final UnitAttachment ua = UnitAttachment.get(type); return ua.getMaxBuiltPerPlayer() >= 0; } }; public static final Match<Unit> UnitIsRocket = new Match<Unit>() { @Override public boolean match(final Unit obj) { return UnitTypeIsRocket.match(obj.getType()); } }; public static final Match<UnitType> UnitTypeIsRocket = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitAttachment ua = UnitAttachment.get(obj); return ua.getIsRocket(); } }; public static final Match<Unit> UnitHasPlacementLimit = new Match<Unit>() { @Override public boolean match(final Unit obj) { final UnitType type = obj.getUnitType(); final UnitAttachment ua = UnitAttachment.get(type); return ua.getPlacementLimit() != null; } }; public static final Match<Unit> UnitHasMovementLimit = new Match<Unit>() { @Override public boolean match(final Unit obj) { final UnitType type = obj.getUnitType(); final UnitAttachment ua = UnitAttachment.get(type); return ua.getMovementLimit() != null; } }; public static final Match<Unit> UnitHasAttackingLimit = new Match<Unit>() { @Override public boolean match(final Unit obj) { final UnitType type = obj.getUnitType(); final UnitAttachment ua = UnitAttachment.get(type); return ua.getAttackingLimit() != null; } }; public static final Match<Unit> UnitCanNotMoveDuringCombatMove = new Match<Unit>() { @Override public boolean match(final Unit obj) { return UnitTypeCanNotMoveDuringCombatMove.match(obj.getType()); } }; public static final Match<UnitType> UnitTypeCanNotMoveDuringCombatMove = new Match<UnitType>() { @Override public boolean match(final UnitType type) { final UnitAttachment ua = UnitAttachment.get(type); return ua.getCanNotMoveDuringCombatMove(); } }; public static Match<Unit> UnitIsAAthatCanHitTheseUnits(final Collection<Unit> targets, final Match<Unit> typeOfAA, final HashMap<String, HashSet<UnitType>> airborneTechTargetsAllowed) { return new Match<Unit>() { @Override public boolean match(final Unit obj) { if (!typeOfAA.match(obj)) { return false; } final UnitAttachment ua = UnitAttachment.get(obj.getType()); final Set<UnitType> targetsAA = ua.getTargetsAA(obj.getData()); for (final Unit u : targets) { if (targetsAA.contains(u.getType())) { return true; } } return Match.someMatch(targets, new CompositeMatchAnd<>(Matches.UnitIsAirborne, Matches.unitIsOfTypes(airborneTechTargetsAllowed.get(ua.getTypeAA())))); } }; } public static Match<Unit> UnitIsAAofTypeAA(final String typeAA) { return new Match<Unit>() { @Override public boolean match(final Unit obj) { return UnitAttachment.get(obj.getType()).getTypeAA().matches(typeAA); } }; } public static final Match<Unit> UnitAAShotDamageableInsteadOfKillingInstantly = new Match<Unit>() { @Override public boolean match(final Unit obj) { return UnitAttachment.get(obj.getType()).getDamageableAA(); } }; private static Match<Unit> UnitIsAAthatWillNotFireIfPresentEnemyUnits(final Collection<Unit> enemyUnitsPresent) { return new Match<Unit>() { @Override public boolean match(final Unit obj) { final UnitAttachment ua = UnitAttachment.get(obj.getType()); for (final Unit u : enemyUnitsPresent) { if (ua.getWillNotFireIfPresent().contains(u.getType())) { return true; } } return false; } }; } private static Match<UnitType> UnitTypeIsAAthatCanFireOnRound(final int battleRoundNumber) { return new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final int maxRoundsAA = UnitAttachment.get(obj).getMaxRoundsAA(); return maxRoundsAA < 0 || maxRoundsAA >= battleRoundNumber; } }; } private static Match<Unit> UnitIsAAthatCanFireOnRound(final int battleRoundNumber) { return new Match<Unit>() { @Override public boolean match(final Unit obj) { return UnitTypeIsAAthatCanFireOnRound(battleRoundNumber).match(obj.getType()); } }; } public static Match<Unit> UnitIsAAthatCanFire(final Collection<Unit> unitsMovingOrAttacking, final HashMap<String, HashSet<UnitType>> airborneTechTargetsAllowed, final PlayerID playerMovingOrAttacking, final Match<Unit> typeOfAA, final int battleRoundNumber, final boolean defending, final GameData data) { return new CompositeMatchAnd<>(Matches.enemyUnit(playerMovingOrAttacking, data), Matches.unitIsBeingTransported().invert(), Matches.UnitIsAAthatCanHitTheseUnits(unitsMovingOrAttacking, typeOfAA, airborneTechTargetsAllowed), Matches.UnitIsAAthatWillNotFireIfPresentEnemyUnits(unitsMovingOrAttacking).invert(), Matches.UnitIsAAthatCanFireOnRound(battleRoundNumber), (defending ? UnitAttackAAisGreaterThanZeroAndMaxAAattacksIsNotZero : UnitOffensiveAttackAAisGreaterThanZeroAndMaxAAattacksIsNotZero)); } public static final Match<Unit> UnitIsAAforCombatOnly = new Match<Unit>() { @Override public boolean match(final Unit obj) { return UnitTypeIsAAforCombatOnly.match(obj.getType()); } }; public static final Match<UnitType> UnitTypeIsAAforCombatOnly = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitAttachment ua = UnitAttachment.get(obj); return ua.getIsAAforCombatOnly(); } }; public static final Match<Unit> UnitIsAAforBombingThisUnitOnly = new Match<Unit>() { @Override public boolean match(final Unit obj) { return UnitTypeIsAAforBombingThisUnitOnly.match(obj.getType()); } }; public static final Match<UnitType> UnitTypeIsAAforBombingThisUnitOnly = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitAttachment ua = UnitAttachment.get(obj); return ua.getIsAAforBombingThisUnitOnly(); } }; public static final Match<Unit> UnitIsAAforFlyOverOnly = new Match<Unit>() { @Override public boolean match(final Unit obj) { return UnitTypeIsAAforFlyOverOnly.match(obj.getType()); } }; public static final Match<UnitType> UnitTypeIsAAforFlyOverOnly = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitAttachment ua = UnitAttachment.get(obj); return ua.getIsAAforFlyOverOnly(); } }; public static final Match<Unit> UnitIsAAforAnything = new Match<Unit>() { @Override public boolean match(final Unit obj) { return UnitTypeIsAAforAnything.match(obj.getType()); } }; public static final Match<UnitType> UnitTypeIsAAforAnything = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitAttachment ua = UnitAttachment.get(obj); return ua.getIsAAforBombingThisUnitOnly() || ua.getIsAAforCombatOnly() || ua.getIsAAforFlyOverOnly(); } }; public static final Match<Unit> UnitIsNotAA = new InverseMatch<>(UnitIsAAforAnything); public static final Match<Unit> UnitMaxAAattacksIsInfinite = new Match<Unit>() { @Override public boolean match(final Unit obj) { return UnitTypeMaxAAattacksIsInfinite.match(obj.getType()); } }; public static final Match<UnitType> UnitTypeMaxAAattacksIsInfinite = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitAttachment ua = UnitAttachment.get(obj); return ua.getMaxAAattacks() == -1; } }; public static final Match<Unit> UnitMayOverStackAA = new Match<Unit>() { @Override public boolean match(final Unit obj) { return UnitTypeMayOverStackAA.match(obj.getType()); } }; public static final Match<UnitType> UnitTypeMayOverStackAA = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitAttachment ua = UnitAttachment.get(obj); return ua.getMayOverStackAA(); } }; public static final Match<Unit> UnitAttackAAisGreaterThanZeroAndMaxAAattacksIsNotZero = new Match<Unit>() { @Override public boolean match(final Unit obj) { final UnitAttachment ua = UnitAttachment.get(obj.getType()); return ua.getAttackAA(obj.getOwner()) > 0 && ua.getMaxAAattacks() != 0; } }; public static final Match<Unit> UnitOffensiveAttackAAisGreaterThanZeroAndMaxAAattacksIsNotZero = new Match<Unit>() { @Override public boolean match(final Unit obj) { final UnitAttachment ua = UnitAttachment.get(obj.getType()); return ua.getOffensiveAttackAA(obj.getOwner()) > 0 && ua.getMaxAAattacks() != 0; } }; public static final Match<Unit> UnitIsInfantry = new Match<Unit>() { @Override public boolean match(final Unit obj) { final UnitType type = obj.getUnitType(); final UnitAttachment ua = UnitAttachment.get(type); return ua.getIsInfantry(); } }; public static final Match<Unit> UnitIsNotInfantry = new InverseMatch<>(UnitIsInfantry); public static final Match<Unit> UnitHasMarinePositiveBonus = new Match<Unit>() { @Override public boolean match(final Unit obj) { final UnitType type = obj.getUnitType(); final UnitAttachment ua = UnitAttachment.get(type); return ua.getIsMarine() > 0; } }; public static final Match<Unit> UnitHasMarineNegativeBonus = new Match<Unit>() { @Override public boolean match(final Unit obj) { final UnitType type = obj.getUnitType(); final UnitAttachment ua = UnitAttachment.get(type); return ua.getIsMarine() < 0; } }; public static final Match<Unit> UnitIsNotMarine = new Match<Unit>() { @Override public boolean match(final Unit obj) { final UnitType type = obj.getUnitType(); final UnitAttachment ua = UnitAttachment.get(type); return ua.getIsMarine() == 0; } }; public static final Match<Unit> UnitIsAirTransportable = new Match<Unit>() { @Override public boolean match(final Unit obj) { final TechAttachment ta = TechAttachment.get(obj.getOwner()); if (ta == null || !ta.getParatroopers()) { return false; } final UnitType type = obj.getUnitType(); final UnitAttachment ua = UnitAttachment.get(type); return ua.getIsAirTransportable(); } }; public static final Match<Unit> UnitIsNotAirTransportable = new InverseMatch<>(UnitIsAirTransportable); public static final Match<Unit> UnitIsAirTransport = new Match<Unit>() { @Override public boolean match(final Unit obj) { final TechAttachment ta = TechAttachment.get(obj.getOwner()); if (ta == null || !ta.getParatroopers()) { return false; } final UnitType type = obj.getUnitType(); final UnitAttachment ua = UnitAttachment.get(type); return ua.getIsAirTransport(); } }; public static final Match<Unit> UnitIsNotAirTransport = new InverseMatch<>(UnitIsAirTransport); public static final Match<Unit> UnitIsArtillery = new Match<Unit>() { @Override public boolean match(final Unit obj) { final UnitType type = obj.getUnitType(); final UnitAttachment ua = UnitAttachment.get(type); return ua.getArtillery(); } }; public static final Match<Unit> UnitIsArtillerySupportable = new Match<Unit>() { @Override public boolean match(final Unit obj) { final UnitType type = obj.getUnitType(); final UnitAttachment ua = UnitAttachment.get(type); return ua.getArtillerySupportable(); } }; // TODO: CHECK whether this makes any sense public static final Match<Territory> TerritoryIsLandOrWater = new Match<Territory>() { @Override public boolean match(final Territory t) { return t != null; } }; public static final Match<Territory> TerritoryIsWater = new Match<Territory>() { @Override public boolean match(final Territory t) { return t.isWater(); } }; public static final Match<Territory> TerritoryIsVictoryCity = new Match<Territory>() { @Override public boolean match(final Territory t) { final TerritoryAttachment ta = TerritoryAttachment.get(t); if (ta == null) { return false; } return ta.getVictoryCity() != 0; } }; public static final Match<Territory> TerritoryIsLand = new InverseMatch<>(TerritoryIsWater); public static final Match<Territory> TerritoryIsEmpty = new Match<Territory>() { @Override public boolean match(final Territory t) { return t.getUnits().size() == 0; } }; /** * Tests for Land, Convoys Centers and Convoy Routes, and Contested Territories. * Assumes player is either the owner of the territory we are testing, or about to become the owner (ie: this doesn't * test ownership). * If the game option for contested territories not producing is on, then will also remove any contested territories. */ public static Match<Territory> territoryCanCollectIncomeFrom(final PlayerID player, final GameData data) { final boolean contestedDoNotProduce = games.strategy.triplea.Properties.getContestedTerritoriesProduceNoIncome(data); return new Match<Territory>() { @Override public boolean match(final Territory t) { final TerritoryAttachment ta = TerritoryAttachment.get(t); if (ta == null) { return false; } final PlayerID origOwner = OriginalOwnerTracker.getOriginalOwner(t); if (t.isWater()) { // if it's water, it is a Convoy Center // Can't get PUs for capturing a CC, only original owner can get them. (Except capturing null player CCs) if (!(origOwner == null || origOwner == PlayerID.NULL_PLAYERID || origOwner == player)) { return false; } } if (ta.getConvoyRoute() && !ta.getConvoyAttached().isEmpty()) { // Determine if at least one part of the convoy route is owned by us or an ally boolean atLeastOne = false; for (final Territory convoy : ta.getConvoyAttached()) { if (data.getRelationshipTracker().isAllied(convoy.getOwner(), player) && TerritoryAttachment.get(convoy).getConvoyRoute()) { atLeastOne = true; } } if (!atLeastOne) { return false; } } return !(contestedDoNotProduce && !Matches.territoryHasNoEnemyUnits(player, data).match(t)); } }; } public static Match<Territory> territoryHasNeighborMatching(final GameData data, final Match<Territory> match) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return data.getMap().getNeighbors(t, match).size() > 0; } }; } public static Match<Territory> territoryHasEnemyLandNeighbor(final GameData data, final PlayerID player) { return new Match<Territory>() { @Override public boolean match(final Territory t) { // This method will still return true if territory t is an impassable or restricted territory With enemy // neighbors. Makes sure your // AI does not include any impassable or restricted territories by using this: // CompositeMatch<Territory> territoryHasEnemyLandNeighborAndIsNotImpassableOrRestricted = new // CompositeMatchAnd<Territory>(Matches.TerritoryIsPassableAndNotRestricted(player), // Matches.territoryHasEnemyLandNeighbor(data, // player)); final CompositeMatch<Territory> condition = new CompositeMatchAnd<>(Matches.TerritoryIsLand, Matches.isTerritoryEnemyAndNotUnownedWaterOrImpassableOrRestricted(player, data)); return data.getMap().getNeighbors(t, condition).size() > 0; } }; } public static Match<Territory> territoryHasAlliedNeighborWithAlliedUnitMatching(final GameData data, final PlayerID player, final Match<Unit> unitMatch) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return data.getMap().getNeighbors(t, Matches.territoryIsAlliedAndHasAlliedUnitMatching(data, player, unitMatch)) .size() > 0; } }; } public static Match<Territory> territoryHasValidLandRouteTo(final GameData data, final Territory goTerr) { return new Match<Territory>() { @Override public boolean match(final Territory t) { final CompositeMatch<Territory> validLandRoute = new CompositeMatchAnd<>(Matches.TerritoryIsLand, Matches.TerritoryIsNotImpassable); return data.getMap().getRoute(t, goTerr, validLandRoute) != null; } }; } public static Match<Territory> territoryIsInList(final Collection<Territory> list) { return new Match<Territory>() { @Override public boolean match(final Territory ter) { return list.contains(ter); } }; } public static Match<Territory> territoryIsNotInList(final Collection<Territory> list) { return new Match<Territory>() { @Override public boolean match(final Territory ter) { return !list.contains(ter); } }; } /** * @param data * game data * @return Match<Territory> that tests if there is a route to an enemy capital from the given territory. */ public static Match<Territory> territoryHasRouteToEnemyCapital(final GameData data, final PlayerID player) { return new Match<Territory>() { @Override public boolean match(final Territory t) { for (final PlayerID ePlayer : data.getPlayerList().getPlayers()) { final List<Territory> capitalsListOwned = new ArrayList<>(TerritoryAttachment.getAllCurrentlyOwnedCapitals(ePlayer, data)); for (final Territory current : capitalsListOwned) { if (!data.getRelationshipTracker().isAtWar(player, current.getOwner())) { continue; } if (data.getMap().getDistance(t, current, Matches.TerritoryIsPassableAndNotRestricted(player, data)) != -1) { return true; } } } return false; } }; } /** * @param data * game data. * @return true only if the route is land */ public static Match<Territory> territoryHasLandRouteToEnemyCapital(final GameData data, final PlayerID player) { return new Match<Territory>() { @Override public boolean match(final Territory t) { for (final PlayerID ePlayer : data.getPlayerList().getPlayers()) { final List<Territory> capitalsListOwned = new ArrayList<>(TerritoryAttachment.getAllCurrentlyOwnedCapitals(ePlayer, data)); for (final Territory current : capitalsListOwned) { if (!data.getRelationshipTracker().isAtWar(player, current.getOwner())) { continue; } if (data.getMap().getDistance(t, current, Matches.TerritoryIsNotImpassableToLandUnits(player, data)) != -1) { return true; } } } return false; } }; } public static Match<Territory> territoryHasEnemyNonNeutralNeighborWithEnemyUnitMatching(final GameData data, final PlayerID player, final Match<Unit> unitMatch) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return data.getMap() .getNeighbors(t, Matches.territoryIsEnemyNonNeutralAndHasEnemyUnitMatching(data, player, unitMatch)) .size() > 0; } }; } public static Match<Territory> territoryHasOwnedNeighborWithOwnedUnitMatching(final GameData data, final PlayerID player, final Match<Unit> unitMatch) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return data.getMap().getNeighbors(t, Matches.territoryIsOwnedAndHasOwnedUnitMatching(player, unitMatch)) .size() > 0; } }; } public static Match<Territory> territoryHasOwnedAtBeginningOfTurnIsFactoryOrCanProduceUnitsNeighbor( final GameData data, final PlayerID player) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return data.getMap() .getNeighbors(t, Matches.territoryHasOwnedAtBeginningOfTurnIsFactoryOrCanProduceUnits(data, player)) .size() > 0; } }; } public static Match<Territory> territoryHasWaterNeighbor(final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return data.getMap().getNeighbors(t, Matches.TerritoryIsWater).size() > 0; } }; } public static Match<Territory> territoryIsAlliedAndHasAlliedUnitMatching(final GameData data, final PlayerID player, final Match<Unit> unitMatch) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (!data.getRelationshipTracker().isAllied(t.getOwner(), player)) { return false; } return t.getUnits().someMatch(new CompositeMatchAnd<>(Matches.alliedUnit(player, data), unitMatch)); } }; } public static Match<Territory> territoryIsOwnedAndHasOwnedUnitMatching(final PlayerID player, final Match<Unit> unitMatch) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (!t.getOwner().equals(player)) { return false; } return t.getUnits().someMatch(new CompositeMatchAnd<>(Matches.unitIsOwnedBy(player), unitMatch)); } }; } public static Match<Territory> territoryHasOwnedIsFactoryOrCanProduceUnits(final PlayerID player) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (!t.getOwner().equals(player)) { return false; } return t.getUnits().someMatch(Matches.UnitCanProduceUnits); } }; } private static Match<Territory> territoryHasOwnedAtBeginningOfTurnIsFactoryOrCanProduceUnits(final GameData data, final PlayerID player) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (!t.getOwner().equals(player)) { return false; } if (!t.getUnits().someMatch(Matches.UnitCanProduceUnits)) { return false; } final BattleTracker bt = AbstractMoveDelegate.getBattleTracker(data); return !(bt == null || bt.wasConquered(t)); } }; } public static Match<Territory> territoryHasAlliedIsFactoryOrCanProduceUnits(final GameData data, final PlayerID player) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (!isTerritoryAllied(player, data).match(t)) { return false; } return t.getUnits().someMatch(Matches.UnitCanProduceUnits); } }; } public static Match<Territory> territoryIsEnemyNonNeutralAndHasEnemyUnitMatching(final GameData data, final PlayerID player, final Match<Unit> unitMatch) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (!data.getRelationshipTracker().isAtWar(player, t.getOwner())) { return false; } if (t.getOwner().isNull()) { return false; } return t.getUnits().someMatch(new CompositeMatchAnd<>(Matches.enemyUnit(player, data), unitMatch)); } }; } public static Match<Territory> territoryIsEmptyOfCombatUnits(final GameData data, final PlayerID player) { return new Match<Territory>() { @Override public boolean match(final Territory t) { final CompositeMatch<Unit> nonCom = new CompositeMatchOr<>(); nonCom.add(UnitIsInfrastructure); nonCom.add(enemyUnit(player, data).invert()); // this is causing issues where the newly captured // nonCom.add(UnitCanBeCapturedOnEnteringToInThisTerritory(player, t, data)); // units fight against themselves return t.getUnits().allMatch(nonCom); } }; } public static final Match<Territory> TerritoryIsNeutralButNotWater = new Match<Territory>() { @Override public boolean match(final Territory t) { if (t.isWater()) { return false; } return t.getOwner().equals(PlayerID.NULL_PLAYERID); } }; public static final Match<Territory> TerritoryIsNotNeutralButCouldBeWater = new InverseMatch<>(TerritoryIsNeutralButNotWater); public static final Match<Territory> TerritoryIsImpassable = new Match<Territory>() { @Override public boolean match(final Territory t) { if (t.isWater()) { return false; } final TerritoryAttachment ta = TerritoryAttachment.get(t); return ta != null && ta.getIsImpassable(); } }; public static final Match<Territory> TerritoryIsNotImpassable = new InverseMatch<>(TerritoryIsImpassable); public static Match<Territory> seaCanMoveOver(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (!TerritoryIsWater.match(t)) { return false; } return TerritoryIsPassableAndNotRestricted(player, data).match(t); } }; } public static Match<Territory> airCanFlyOver(final PlayerID player, final GameData data, final boolean areNeutralsPassableByAir) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (!areNeutralsPassableByAir && TerritoryIsNeutralButNotWater.match(t)) { return false; } if (!TerritoryIsPassableAndNotRestricted(player, data).match(t)) { return false; } return !(TerritoryIsLand.match(t) && !data.getRelationshipTracker().canMoveAirUnitsOverOwnedLand(player, t.getOwner())); } }; } public static Match<Territory> TerritoryIsPassableAndNotRestricted(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (Matches.TerritoryIsImpassable.match(t)) { return false; } if (!Properties.getMovementByTerritoryRestricted(data)) { return true; } final RulesAttachment ra = (RulesAttachment) player.getAttachment(Constants.RULES_ATTACHMENT_NAME); if (ra == null || ra.getMovementRestrictionTerritories() == null) { return true; } final String movementRestrictionType = ra.getMovementRestrictionType(); final Collection<Territory> listedTerritories = ra.getListedTerritories(ra.getMovementRestrictionTerritories(), true, true); return (movementRestrictionType.equals("allowed") == listedTerritories.contains(t)); } }; } public static Match<Territory> TerritoryIsImpassableToLandUnits(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (t.isWater()) { return true; } else if (Matches.TerritoryIsPassableAndNotRestricted(player, data).invert().match(t)) { return true; } return false; } }; } public static Match<Territory> TerritoryIsNotImpassableToLandUnits(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return TerritoryIsImpassableToLandUnits(player, data).invert().match(t); } }; } /** * Does NOT check for Canals, Blitzing, Loading units on transports, TerritoryEffects that disallow units, Stacking * Limits, Unit movement * left, Fuel available, etc.<br> * <br> * Does check for: Impassable, ImpassableNeutrals, ImpassableToAirNeutrals, RestrictedTerritories, Land units moving * on water, Sea units * moving on land, * and territories that are disallowed due to a relationship attachment (canMoveLandUnitsOverOwnedLand, * canMoveAirUnitsOverOwnedLand, * canLandAirUnitsOnOwnedLand, canMoveIntoDuringCombatMove, etc). */ public static Match<Territory> TerritoryIsPassableAndNotRestrictedAndOkByRelationships( final PlayerID playerWhoOwnsAllTheUnitsMoving, final GameData data, final boolean isCombatMovePhase, final boolean hasLandUnitsNotBeingTransportedOrBeingLoaded, final boolean hasSeaUnitsNotBeingTransported, final boolean hasAirUnitsNotBeingTransported, final boolean isLandingZoneOnLandForAirUnits) { final boolean neutralsPassable = !games.strategy.triplea.Properties.getNeutralsImpassable(data); final boolean areNeutralsPassableByAir = neutralsPassable && games.strategy.triplea.Properties.getNeutralFlyoverAllowed(data); return new Match<Territory>() { @Override public boolean match(final Territory t) { if (Matches.TerritoryIsImpassable.match(t)) { return false; } if ((!neutralsPassable || (hasAirUnitsNotBeingTransported && !areNeutralsPassableByAir)) && TerritoryIsNeutralButNotWater.match(t)) { return false; } if (Properties.getMovementByTerritoryRestricted(data)) { final RulesAttachment ra = (RulesAttachment) playerWhoOwnsAllTheUnitsMoving.getAttachment(Constants.RULES_ATTACHMENT_NAME); if (ra != null && ra.getMovementRestrictionTerritories() != null) { final String movementRestrictionType = ra.getMovementRestrictionType(); final Collection<Territory> listedTerritories = ra.getListedTerritories(ra.getMovementRestrictionTerritories(), true, true); if (!(movementRestrictionType.equals("allowed") == listedTerritories.contains(t))) { return false; } } } final boolean isWater = Matches.TerritoryIsWater.match(t); final boolean isLand = Matches.TerritoryIsLand.match(t); if (hasLandUnitsNotBeingTransportedOrBeingLoaded && !isLand) { return false; } if (hasSeaUnitsNotBeingTransported && !isWater) { return false; } if (isLand) { if (hasLandUnitsNotBeingTransportedOrBeingLoaded && !data.getRelationshipTracker() .canMoveLandUnitsOverOwnedLand(playerWhoOwnsAllTheUnitsMoving, t.getOwner())) { return false; } if (hasAirUnitsNotBeingTransported && !data.getRelationshipTracker() .canMoveAirUnitsOverOwnedLand(playerWhoOwnsAllTheUnitsMoving, t.getOwner())) { return false; } } if (isLandingZoneOnLandForAirUnits && !data.getRelationshipTracker() .canLandAirUnitsOnOwnedLand(playerWhoOwnsAllTheUnitsMoving, t.getOwner())) { return false; } return !(isCombatMovePhase && !data.getRelationshipTracker() .canMoveIntoDuringCombatMove(playerWhoOwnsAllTheUnitsMoving, t.getOwner())); } }; } public static final Match<IBattle> BattleIsEmpty = new Match<IBattle>() { @Override public boolean match(final IBattle battle) { return battle.isEmpty(); } }; public static final Match<IBattle> BattleIsAmphibious = new Match<IBattle>() { @Override public boolean match(final IBattle battle) { return battle.isAmphibious(); } }; public static Match<Unit> UnitHasEnoughMovementForRoutes(final List<Route> route) { return UnitHasEnoughMovementForRoute(Route.create(route)); } public static Match<Unit> UnitHasEnoughMovementForRoute(final List<Territory> territories) { return UnitHasEnoughMovementForRoute(new Route(territories)); } public static Match<Unit> UnitHasEnoughMovementForRoute(final Route route) { return new Match<Unit>() { @Override public boolean match(final Unit unit) { int left = TripleAUnit.get(unit).getMovementLeft(); int movementcost = route.getMovementCost(unit); final UnitAttachment ua = UnitAttachment.get(unit.getType()); final PlayerID player = unit.getOwner(); if (ua.getIsAir()) { TerritoryAttachment taStart = null; TerritoryAttachment taEnd = null; if (route.getStart() != null) { taStart = TerritoryAttachment.get(route.getStart()); } if (route.getEnd() != null) { taEnd = TerritoryAttachment.get(route.getEnd()); } movementcost = route.getMovementCost(unit); if (taStart != null && taStart.getAirBase()) { left++; } if (taEnd != null && taEnd.getAirBase()) { left++; } } final GameStep stepName = unit.getData().getSequence().getStep(); if (ua.getIsSea() && stepName.getDisplayName().equals("Non Combat Move")) { movementcost = route.getMovementCost(unit); // If a zone adjacent to the starting and ending sea zones // are allied navalbases, increase the range. // TODO Still need to be able to handle stops on the way // (history to get route.getStart() for (final Territory terrNext : unit.getData().getMap().getNeighbors(route.getStart(), 1)) { final TerritoryAttachment taNeighbor = TerritoryAttachment.get(terrNext); if (taNeighbor != null && taNeighbor.getNavalBase() && unit.getData().getRelationshipTracker().isAllied(terrNext.getOwner(), player)) { for (final Territory terrEnd : unit.getData().getMap().getNeighbors(route.getEnd(), 1)) { final TerritoryAttachment taEndNeighbor = TerritoryAttachment.get(terrEnd); if (taEndNeighbor != null && taEndNeighbor.getNavalBase() && unit.getData().getRelationshipTracker().isAllied(terrEnd.getOwner(), player)) { left++; break; } } } } } return !(left < 0 || left < movementcost); } }; } /** * Match units that have at least 1 movement left. */ public static final Match<Unit> unitHasMovementLeft = new Match<Unit>() { @Override public boolean match(final Unit o) { return TripleAUnit.get(o).getMovementLeft() >= 1; } }; public static final Match<Unit> UnitCanMove = new Match<Unit>() { @Override public boolean match(final Unit u) { return UnitTypeCanMove(u.getOwner()).match(u.getType()); } }; private static Match<UnitType> UnitTypeCanMove(final PlayerID player) { return new Match<UnitType>() { @Override public boolean match(final UnitType obj) { return UnitAttachment.get(obj).getMovement(player) > 0; } }; } public static final Match<Unit> UnitIsStatic = new InverseMatch<>(UnitCanMove); public static Match<UnitType> UnitTypeIsStatic(final PlayerID id) { return new Match<UnitType>() { @Override public boolean match(final UnitType uT) { return !UnitTypeCanMove(id).match(uT); } }; } public static Match<Unit> unitIsLandAndOwnedBy(final PlayerID player) { return new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); return !ua.getIsSea() && !ua.getIsAir() && unit.getOwner().equals(player); } }; } public static Match<Unit> unitIsOwnedBy(final PlayerID player) { return new Match<Unit>() { @Override public boolean match(final Unit unit) { return unit.getOwner().equals(player); } }; } public static Match<Unit> unitIsOwnedByOfAnyOfThesePlayers(final Collection<PlayerID> players) { return new Match<Unit>() { @Override public boolean match(final Unit unit) { return players.contains(unit.getOwner()); } }; } public static Match<Unit> unitHasDefenseThatIsMoreThanOrEqualTo(final int minDefense) { return new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.getDefense(unit.getOwner()) >= minDefense; } }; } public static Match<Unit> unitIsTransporting() { return new Match<Unit>() { @Override public boolean match(final Unit unit) { final Collection<Unit> transporting = TripleAUnit.get(unit).getTransporting(); return !(transporting == null || transporting.isEmpty()); } }; } public static Match<Unit> unitIsTransportingSomeCategories(final Collection<Unit> units) { final Collection<UnitCategory> unitCategories = UnitSeperator.categorize(units); return new Match<Unit>() { @Override public boolean match(final Unit unit) { final Collection<Unit> transporting = TripleAUnit.get(unit).getTransporting(); if (transporting == null) { return false; } return Util.someIntersect(UnitSeperator.categorize(transporting), unitCategories); } }; } public static Match<Territory> isTerritoryAllied(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return data.getRelationshipTracker().isAllied(player, t.getOwner()); } }; } public static Match<Territory> isTerritoryOwnedBy(final PlayerID player) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return t.getOwner().equals(player); } }; } public static Match<Territory> isTerritoryOwnedBy(final Collection<PlayerID> players) { return new Match<Territory>() { @Override public boolean match(final Territory t) { for (final PlayerID player : players) { if (t.getOwner().equals(player)) { return true; } } return false; } }; } public static Match<Unit> isUnitAllied(final PlayerID player, final GameData data) { return new Match<Unit>() { @Override public boolean match(final Unit t) { return data.getRelationshipTracker().isAllied(player, t.getOwner()); } }; } public static Match<Territory> isTerritoryFriendly(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (t.isWater()) { return true; } if (t.getOwner().equals(player)) { return true; } return data.getRelationshipTracker().isAllied(player, t.getOwner()); } }; } private static Match<Unit> unitIsEnemyAAforAnything(final PlayerID player, final GameData data) { final CompositeMatch<Unit> comp = new CompositeMatchAnd<>(); comp.add(UnitIsAAforAnything); comp.add(enemyUnit(player, data)); return comp; } private static Match<Unit> unitIsEnemyAAforCombat(final PlayerID player, final GameData data) { final CompositeMatch<Unit> comp = new CompositeMatchAnd<>(); comp.add(UnitIsAAforCombatOnly); comp.add(enemyUnit(player, data)); return comp; } public static Match<Unit> unitIsInTerritory(final Territory territory) { return new Match<Unit>() { @Override public boolean match(final Unit o) { return territory.getUnits().getUnits().contains(o); } }; } public static Match<Territory> isTerritoryEnemy(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (t.getOwner().equals(player)) { return false; } return data.getRelationshipTracker().isAtWar(player, t.getOwner()); } }; } public static Match<Territory> isTerritoryEnemyAndNotUnownedWater(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (t.getOwner().equals(player)) { return false; } // if we look at territory attachments, may have funny results for blockades or other things that are passable // and not owned. better // to check them by alliance. (veqryn) // OLD code included: if(t.isWater() && t.getOwner().isNull() && TerritoryAttachment.get(t) == null){return // false;} if (t.getOwner().equals(PlayerID.NULL_PLAYERID) && t.isWater()) { return false; } return data.getRelationshipTracker().isAtWar(player, t.getOwner()); } }; } public static Match<Territory> isTerritoryEnemyAndNotUnownedWaterOrImpassableOrRestricted(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (t.getOwner().equals(player)) { return false; } // if we look at territory attachments, may have funny results for blockades or other things that are passable // and not owned. better // to check them by alliance. (veqryn) // OLD code included: if(t.isWater() && t.getOwner().isNull() && TerritoryAttachment.get(t) == null){return // false;} if (t.getOwner().equals(PlayerID.NULL_PLAYERID) && t.isWater()) { return false; } if (!Matches.TerritoryIsPassableAndNotRestricted(player, data).match(t)) { return false; } return data.getRelationshipTracker().isAtWar(player, t.getOwner()); } }; } public static Match<Territory> TerritoryIsBlitzable(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { // cant blitz water if (t.isWater()) { return false; } // cant blitz on neutrals if (t.getOwner().equals(PlayerID.NULL_PLAYERID) && !games.strategy.triplea.Properties.getNeutralsBlitzable(data)) { return false; } // was conquered but not blitzed if (AbstractMoveDelegate.getBattleTracker(data).wasConquered(t) && !AbstractMoveDelegate.getBattleTracker(data).wasBlitzed(t)) { return false; } final CompositeMatch<Unit> blitzableUnits = new CompositeMatchOr<>(); // we ignore neutral units blitzableUnits.add(Matches.enemyUnit(player, data).invert()); // WW2V2, cant blitz through factories and aa guns // WW2V1, you can if (!games.strategy.triplea.Properties.getWW2V2(data) && !games.strategy.triplea.Properties.getBlitzThroughFactoriesAndAARestricted(data)) { blitzableUnits.add(Matches.UnitIsInfrastructure); } return t.getUnits().allMatch(blitzableUnits); } }; } public static Match<Territory> isTerritoryFreeNeutral(final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return (t.getOwner().equals(PlayerID.NULL_PLAYERID) && Properties.getNeutralCharge(data) <= 0); } }; } public static Match<Territory> territoryDoesNotCostMoneyToEnter(final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return Matches.TerritoryIsLand.invert().match(t) || !t.getOwner().equals(PlayerID.NULL_PLAYERID) || Properties.getNeutralCharge(data) <= 0; } }; } public static Match<Unit> enemyUnit(final PlayerID player, final GameData data) { return new Match<Unit>() { @Override public boolean match(final Unit unit) { return data.getRelationshipTracker().isAtWar(player, unit.getOwner()); } }; } public static Match<Unit> enemyUnitOfAnyOfThesePlayers(final Collection<PlayerID> players, final GameData data) { return new Match<Unit>() { @Override public boolean match(final Unit unit) { return data.getRelationshipTracker().isAtWarWithAnyOfThesePlayers(unit.getOwner(), players); } }; } public static Match<Unit> unitOwnedBy(final PlayerID player) { return new Match<Unit>() { @Override public boolean match(final Unit o) { final Unit unit = o; return unit.getOwner().equals(player); } }; } public static Match<Unit> unitOwnedBy(final List<PlayerID> players) { return new Match<Unit>() { @Override public boolean match(final Unit o) { for (final PlayerID p : players) { if (o.getOwner().equals(p)) { return true; } } return false; } }; } public static Match<Unit> alliedUnit(final PlayerID player, final GameData data) { return new Match<Unit>() { @Override public boolean match(final Unit unit) { if (unit.getOwner().equals(player)) { return true; } return data.getRelationshipTracker().isAllied(player, unit.getOwner()); } }; } public static Match<Unit> alliedUnitOfAnyOfThesePlayers(final Collection<PlayerID> players, final GameData data) { return new Match<Unit>() { @Override public boolean match(final Unit unit) { if (Matches.unitIsOwnedByOfAnyOfThesePlayers(players).match(unit)) { return true; } return data.getRelationshipTracker().isAlliedWithAnyOfThesePlayers(unit.getOwner(), players); } }; } public static Match<Territory> territoryIs(final Territory test) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return t.equals(test); } }; } public static Match<Territory> territoryHasLandUnitsOwnedBy(final PlayerID player) { final CompositeMatch<Unit> unitOwnedBy = new CompositeMatchAnd<>(unitIsOwnedBy(player), Matches.UnitIsLand); return new Match<Territory>() { @Override public boolean match(final Territory t) { return t.getUnits().someMatch(unitOwnedBy); } }; } public static Match<Territory> territoryHasUnitsOwnedBy(final PlayerID player) { final Match<Unit> unitOwnedBy = unitIsOwnedBy(player); return new Match<Territory>() { @Override public boolean match(final Territory t) { return t.getUnits().someMatch(unitOwnedBy); } }; } public static Match<Territory> territoryHasUnitsThatMatch(final Match<Unit> cond) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return t.getUnits().someMatch(cond); } }; } public static Match<Territory> territoryHasEnemyAAforAnything(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return t.getUnits().someMatch(unitIsEnemyAAforAnything(player, data)); } }; } public static Match<Territory> territoryHasEnemyAAforCombatOnly(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return t.getUnits().someMatch(Matches.unitIsEnemyAAforCombat(player, data)); } }; } public static Match<Territory> territoryHasNoEnemyUnits(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return !t.getUnits().someMatch(enemyUnit(player, data)); } }; } public static Match<Territory> territoryHasNoAlliedUnits(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return !t.getUnits().someMatch(alliedUnit(player, data)); } }; } public static Match<Territory> territoryHasAlliedUnits(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return t.getUnits().someMatch(alliedUnit(player, data)); } }; } public static Match<Territory> territoryHasNonSubmergedEnemyUnits(final PlayerID player, final GameData data) { final CompositeMatch<Unit> match = new CompositeMatchAnd<>(); match.add(enemyUnit(player, data)); match.add(UnitIsSubmerged.invert()); return new Match<Territory>() { @Override public boolean match(final Territory t) { return t.getUnits().someMatch(match); } }; } public static Match<Territory> territoryHasEnemyLandUnits(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return t.getUnits().someMatch(new CompositeMatchAnd<>(enemyUnit(player, data), UnitIsLand)); } }; } public static Match<Territory> territoryHasEnemySeaUnits(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return t.getUnits().someMatch(new CompositeMatchAnd<>(enemyUnit(player, data), UnitIsSea)); } }; } public static Match<Territory> territoryHasEnemyBlitzUnits(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return t.getUnits().someMatch(enemyUnit(player, data)) && t.getUnits().someMatch(Matches.UnitCanBlitz); } }; } public static Match<Territory> territoryHasEnemyUnits(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return t.getUnits().someMatch(enemyUnit(player, data)); } }; } /** * The territory is owned by the enemy of those enemy units (ie: probably owned by you or your ally, but not * necessarily so in an FFA type * game) and is not unowned water. */ public static Match<Territory> territoryHasEnemyUnitsThatCanCaptureTerritoryAndTerritoryOwnedByTheirEnemyAndIsNotUnownedWater( final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (t.getOwner() == null) { return false; } if (t.isWater() && TerritoryAttachment.get(t) == null && t.getOwner().isNull()) { return false; } final Set<PlayerID> enemies = new HashSet<>(); for (final Unit u : t.getUnits() .getMatches(new CompositeMatchAnd<>(enemyUnit(player, data), UnitIsNotAir, UnitIsNotInfrastructure))) { enemies.add(u.getOwner()); } return (Matches.isAtWarWithAnyOfThesePlayers(enemies, data)).match(t.getOwner()); } }; } public static Match<Territory> territoryHasOwnedTransportingUnits(final PlayerID player) { return new Match<Territory>() { @Override public boolean match(final Territory t) { final CompositeMatch<Unit> match = new CompositeMatchAnd<>(); match.add(unitIsOwnedBy(player)); match.add(transportIsTransporting()); return t.getUnits().someMatch(match); } }; } public static Match<Unit> transportCannotUnload(final Territory territory) { return new Match<Unit>() { @Override public boolean match(final Unit transport) { if (TransportTracker.hasTransportUnloadedInPreviousPhase(transport)) { return true; } if (TransportTracker.isTransportUnloadRestrictedToAnotherTerritory(transport, territory)) { return true; } return TransportTracker.isTransportUnloadRestrictedInNonCombat(transport); } }; } public static Match<Unit> transportIsNotTransporting() { return new Match<Unit>() { @Override public boolean match(final Unit transport) { return !TransportTracker.isTransporting(transport); } }; } public static Match<Unit> transportIsTransporting() { return new Match<Unit>() { @Override public boolean match(final Unit transport) { return TransportTracker.isTransporting(transport); } }; } /** * @return Match that tests the TripleAUnit getTransportedBy value * which is normally set for sea transport movement of land units, * and sometimes set for other things like para-troopers and dependent allied fighters sitting as cargo on a * ship. (not sure if * set for mech inf or not) */ public static Match<Unit> unitIsBeingTransported() { return new Match<Unit>() { @Override public boolean match(final Unit dependent) { return ((TripleAUnit) dependent).getTransportedBy() != null; } }; } /** * @param units * referring unit. * @param route * referring route * @param currentPlayer * current player * @param data * game data * @param forceLoadParatroopersIfPossible * should we load paratroopers? (if not, we assume they are already loaded) * @return Match that tests the TripleAUnit getTransportedBy value * (also tests for para-troopers, and for dependent allied fighters sitting as cargo on a ship) */ public static Match<Unit> unitIsBeingTransportedByOrIsDependentOfSomeUnitInThisList(final Collection<Unit> units, final Route route, final PlayerID currentPlayer, final GameData data, final boolean forceLoadParatroopersIfPossible) { return new Match<Unit>() { @Override public boolean match(final Unit dependent) { // transported on a sea transport final Unit transportedBy = ((TripleAUnit) dependent).getTransportedBy(); if (transportedBy != null && units.contains(transportedBy)) { return true; } // cargo on a carrier final Map<Unit, Collection<Unit>> carrierMustMoveWith = MoveValidator.carrierMustMoveWith(units, units, data, currentPlayer); if (carrierMustMoveWith != null) { for (final Unit unit : carrierMustMoveWith.keySet()) { if (carrierMustMoveWith.get(unit).contains(dependent)) { return true; } } } // paratrooper on an air transport if (forceLoadParatroopersIfPossible) { final Collection<Unit> airTransports = Match.getMatches(units, Matches.UnitIsAirTransport); final Collection<Unit> paratroops = Match.getMatches(units, Matches.UnitIsAirTransportable); if (!airTransports.isEmpty() && !paratroops.isEmpty()) { if (TransportUtils.mapTransportsToLoad(paratroops, airTransports) .containsKey(dependent)) { return true; } } } return false; } }; } public static final Match<Unit> UnitIsLand = new CompositeMatchAnd<>(UnitIsNotSea, UnitIsNotAir); public static final Match<UnitType> UnitTypeIsLand = new CompositeMatchAnd<>(UnitTypeIsNotSea, UnitTypeIsNotAir); public static final Match<Unit> UnitIsNotLand = new InverseMatch<>(UnitIsLand); public static Match<Unit> unitIsOfType(final UnitType type) { return new Match<Unit>() { @Override public boolean match(final Unit unit) { return unit.getType().equals(type); } }; } public static Match<Unit> unitIsOfTypes(final Set<UnitType> types) { return new Match<Unit>() { @Override public boolean match(final Unit unit) { if (types == null || types.isEmpty()) { return false; } return types.contains(unit.getType()); } }; } public static Match<Territory> territoryWasFoughOver(final BattleTracker tracker) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return tracker.wasBattleFought(t) || tracker.wasBlitzed(t); } }; } public static final Match<Unit> UnitIsSubmerged = new Match<Unit>() { @Override public boolean match(final Unit u) { return TripleAUnit.get(u).getSubmerged(); } }; public static final Match<UnitType> UnitTypeIsSub = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitType type = obj; final UnitAttachment ua = UnitAttachment.get(type); return ua.getIsSub(); } }; public static Match<Unit> unitOwnerHasImprovedArtillerySupportTech() { return new Match<Unit>() { @Override public boolean match(final Unit u) { return TechTracker.hasImprovedArtillerySupport(u.getOwner()); } }; } public static Match<Territory> territoryHasNonAllowedCanal(final PlayerID player, final Collection<Unit> unitsMoving, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { return MoveValidator.validateCanal(t, null, unitsMoving, player, data).isPresent(); } }; } public static Match<Territory> territoryIsBlockedSea(final PlayerID player, final GameData data) { final CompositeMatch<Unit> ignore = new CompositeMatchAnd<>(Matches.UnitIsInfrastructure.invert(), Matches.alliedUnit(player, data).invert()); final CompositeMatch<Unit> sub = new CompositeMatchAnd<>(Matches.UnitIsSub.invert()); final CompositeMatch<Unit> transport = new CompositeMatchAnd<>(Matches.UnitIsTransportButNotCombatTransport.invert(), Matches.UnitIsLand.invert()); final CompositeMatch<Unit> unitCond = ignore; if (Properties.getIgnoreTransportInMovement(data)) { unitCond.add(transport); } if (Properties.getIgnoreSubInMovement(data)) { unitCond.add(sub); } final CompositeMatch<Territory> routeCondition = new CompositeMatchAnd<>( Matches.territoryHasUnitsThatMatch(unitCond).invert(), Matches.TerritoryIsWater); return routeCondition; } public static final Match<Unit> UnitCanRepairOthers = new Match<Unit>() { @Override public boolean match(final Unit unit) { if (UnitIsDisabled.match(unit)) { return false; } if (Matches.unitIsBeingTransported().match(unit)) { return false; } final UnitAttachment ua = UnitAttachment.get(unit.getType()); if (ua.getRepairsUnits() == null) { return false; } return !ua.getRepairsUnits().isEmpty(); } }; public static Match<Unit> UnitCanRepairThisUnit(final Unit damagedUnit) { return new Match<Unit>() { @Override public boolean match(final Unit unitCanRepair) { final UnitType type = unitCanRepair.getUnitType(); final UnitAttachment ua = UnitAttachment.get(type); // TODO: make sure the unit is operational if (ua.getRepairsUnits() != null && ua.getRepairsUnits().keySet().contains(damagedUnit.getType())) { return true; } return false; } }; } /** * @param territory * referring territory * @param player * referring player * @param data * game data * @return Match that will return true if the territory contains a unit that can repair this unit * (It will also return true if this unit is Sea and an adjacent land territory has a land unit that can * repair this unit.) */ public static Match<Unit> UnitCanBeRepairedByFacilitiesInItsTerritory(final Territory territory, final PlayerID player, final GameData data) { return new Match<Unit>() { @Override public boolean match(final Unit damagedUnit) { final Match<Unit> damaged = new CompositeMatchAnd<>(Matches.UnitHasMoreThanOneHitPointTotal, Matches.UnitHasTakenSomeDamage); if (!damaged.match(damagedUnit)) { return false; } final Match<Unit> repairUnit = new CompositeMatchAnd<>(Matches.alliedUnit(player, data), Matches.UnitCanRepairOthers, Matches.UnitCanRepairThisUnit(damagedUnit)); if (Match.someMatch(territory.getUnits().getUnits(), repairUnit)) { return true; } if (Matches.UnitIsSea.match(damagedUnit)) { final Match<Unit> repairUnitLand = new CompositeMatchAnd<>(repairUnit, Matches.UnitIsLand); final List<Territory> neighbors = new ArrayList<>(data.getMap().getNeighbors(territory, Matches.TerritoryIsLand)); for (final Territory current : neighbors) { if (Match.someMatch(current.getUnits().getUnits(), repairUnitLand)) { return true; } } } else if (Matches.UnitIsLand.match(damagedUnit)) { final Match<Unit> repairUnitSea = new CompositeMatchAnd<>(repairUnit, Matches.UnitIsSea); final List<Territory> neighbors = new ArrayList<>(data.getMap().getNeighbors(territory, Matches.TerritoryIsWater)); for (final Territory current : neighbors) { if (Match.someMatch(current.getUnits().getUnits(), repairUnitSea)) { return true; } } } return false; } }; } public static final Match<Unit> UnitCanGiveBonusMovement = new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); if (ua == null) { return false; } return ua.getGivesMovement().size() > 0 && Matches.unitIsBeingTransported().invert().match(unit); } }; public static Match<Unit> UnitCanGiveBonusMovementToThisUnit(final Unit unitWhichWillGetBonus) { return new Match<Unit>() { @Override public boolean match(final Unit unitCanGiveBonusMovement) { if (UnitIsDisabled.match(unitCanGiveBonusMovement)) { return false; } final UnitType type = unitCanGiveBonusMovement.getUnitType(); final UnitAttachment ua = UnitAttachment.get(type); // TODO: make sure the unit is operational return UnitCanGiveBonusMovement.match(unitCanGiveBonusMovement) && ua.getGivesMovement().getInt(unitWhichWillGetBonus.getType()) != 0; } }; } /** * @param territory * referring territory * @param player * referring player * @param data * game data * @return Match that will return true if the territory contains a unit that can give bonus movement to this unit * (It will also return true if this unit is Sea and an adjacent land territory has a land unit that can give * bonus movement to * this unit.) */ public static Match<Unit> UnitCanBeGivenBonusMovementByFacilitiesInItsTerritory(final Territory territory, final PlayerID player, final GameData data) { return new Match<Unit>() { @Override public boolean match(final Unit unitWhichWillGetBonus) { final Match<Unit> givesBonusUnit = new CompositeMatchAnd<>(Matches.alliedUnit(player, data), UnitCanGiveBonusMovementToThisUnit(unitWhichWillGetBonus)); if (Match.someMatch(territory.getUnits().getUnits(), givesBonusUnit)) { return true; } if (Matches.UnitIsSea.match(unitWhichWillGetBonus)) { final Match<Unit> givesBonusUnitLand = new CompositeMatchAnd<>(givesBonusUnit, Matches.UnitIsLand); final List<Territory> neighbors = new ArrayList<>(data.getMap().getNeighbors(territory, Matches.TerritoryIsLand)); for (final Territory current : neighbors) { if (Match.someMatch(current.getUnits().getUnits(), givesBonusUnitLand)) { return true; } } } return false; } }; } public static final Match<Unit> UnitCreatesUnits = new Match<Unit>() { @Override public boolean match(final Unit obj) { final Unit unit = obj; final UnitAttachment ua = UnitAttachment.get(unit.getType()); if (ua == null) { return false; } return (ua.getCreatesUnitsList() != null && ua.getCreatesUnitsList().size() > 0); } }; public static final Match<Unit> UnitCreatesResources = new Match<Unit>() { @Override public boolean match(final Unit unit) { final UnitAttachment ua = UnitAttachment.get(unit.getType()); if (ua == null) { return false; } return (ua.getCreatesResourcesList() != null && ua.getCreatesResourcesList().size() > 0); } }; /** Any unit that creates at least a single positive resource. */ public static final Match<Unit> UnitCreatesResourcesPositive = new Match<Unit>() { @Override public boolean match(final Unit unit) { if (!UnitCreatesResources.match(unit)) { return false; } final UnitAttachment ua = UnitAttachment.get(unit.getType()); if (ua == null || ua.getCreatesResourcesList() == null) { return false; } final IntegerMap<Resource> resources = ua.getCreatesResourcesList(); for (final Entry<Resource, Integer> entry : resources.entrySet()) { if (entry.getValue() > 0) { return true; } } return false; } }; /** Any unit that does not create a single positive resource, but does create at least a single negative resource. */ public static final Match<Unit> UnitCreatesResourcesNegative = new Match<Unit>() { @Override public boolean match(final Unit unit) { if (!UnitCreatesResources.match(unit) || UnitCreatesResourcesPositive.match(unit)) { return false; } final UnitAttachment ua = UnitAttachment.get(unit.getType()); if (ua == null || ua.getCreatesResourcesList() == null) { return false; } final IntegerMap<Resource> resources = ua.getCreatesResourcesList(); for (final Entry<Resource, Integer> entry : resources.entrySet()) { if (entry.getValue() < 0) { return true; } } return false; } }; public static final Match<UnitType> UnitTypeConsumesUnitsOnCreation = new Match<UnitType>() { @Override public boolean match(final UnitType obj) { final UnitType unit = obj; final UnitAttachment ua = UnitAttachment.get(unit); if (ua == null) { return false; } return (ua.getConsumesUnits() != null && ua.getConsumesUnits().size() > 0); } }; public static final Match<Unit> UnitConsumesUnitsOnCreation = new Match<Unit>() { @Override public boolean match(final Unit obj) { final Unit unit = obj; final UnitAttachment ua = UnitAttachment.get(unit.getType()); if (ua == null) { return false; } return (ua.getConsumesUnits() != null && ua.getConsumesUnits().size() > 0); } }; public static Match<Unit> UnitWhichConsumesUnitsHasRequiredUnits( final Collection<Unit> unitsInTerritoryAtStartOfTurn) { return new Match<Unit>() { @Override public boolean match(final Unit unitWhichRequiresUnits) { if (!Matches.UnitConsumesUnitsOnCreation.match(unitWhichRequiresUnits)) { return true; } final UnitAttachment ua = UnitAttachment.get(unitWhichRequiresUnits.getType()); final IntegerMap<UnitType> requiredUnitsMap = ua.getConsumesUnits(); final Collection<UnitType> requiredUnits = requiredUnitsMap.keySet(); boolean canBuild = true; for (final UnitType ut : requiredUnits) { final Match<Unit> unitIsOwnedByAndOfTypeAndNotDamaged = new CompositeMatchAnd<>( Matches.unitIsOwnedBy(unitWhichRequiresUnits.getOwner()), Matches.unitIsOfType(ut), Matches.UnitHasNotTakenAnyBombingUnitDamage, Matches.UnitHasNotTakenAnyDamage, Matches.UnitIsNotDisabled); final int requiredNumber = requiredUnitsMap.getInt(ut); final int numberInTerritory = Match.countMatches(unitsInTerritoryAtStartOfTurn, unitIsOwnedByAndOfTypeAndNotDamaged); if (numberInTerritory < requiredNumber) { canBuild = false; } if (!canBuild) { break; } } return canBuild; } }; } public static final Match<Unit> UnitRequiresUnitsOnCreation = new Match<Unit>() { @Override public boolean match(final Unit obj) { final Unit unit = obj; final UnitAttachment ua = UnitAttachment.get(unit.getType()); if (ua == null) { return false; } return (ua.getRequiresUnits() != null && ua.getRequiresUnits().size() > 0); } }; public static Match<Unit> UnitWhichRequiresUnitsHasRequiredUnitsInList( final Collection<Unit> unitsInTerritoryAtStartOfTurn) { return new Match<Unit>() { @Override public boolean match(final Unit unitWhichRequiresUnits) { if (!Matches.UnitRequiresUnitsOnCreation.match(unitWhichRequiresUnits)) { return true; } final Match<Unit> unitIsOwnedByAndNotDisabled = new CompositeMatchAnd<>( Matches.unitIsOwnedBy(unitWhichRequiresUnits.getOwner()), Matches.UnitIsNotDisabled); unitsInTerritoryAtStartOfTurn .retainAll(Match.getMatches(unitsInTerritoryAtStartOfTurn, unitIsOwnedByAndNotDisabled)); boolean canBuild = false; final UnitAttachment ua = UnitAttachment.get(unitWhichRequiresUnits.getType()); final ArrayList<String[]> unitComboPossibilities = ua.getRequiresUnits(); for (final String[] combo : unitComboPossibilities) { if (combo != null) { boolean haveAll = true; final Collection<UnitType> requiredUnits = ua.getListedUnits(combo); for (final UnitType ut : requiredUnits) { if (Match.countMatches(unitsInTerritoryAtStartOfTurn, Matches.unitIsOfType(ut)) < 1) { haveAll = false; } if (!haveAll) { break; } } if (haveAll) { canBuild = true; } } if (canBuild) { break; } } return canBuild; } }; } public static final Match<Territory> territoryIsBlockadeZone = new Match<Territory>() { @Override public boolean match(final Territory t) { final TerritoryAttachment ta = TerritoryAttachment.get(t); if (ta != null) { return ta.getBlockadeZone(); } return false; } }; public static final Match<Unit> UnitIsConstruction = new Match<Unit>() { @Override public boolean match(final Unit obj) { return UnitTypeIsConstruction.match(obj.getType()); } }; public static final Match<UnitType> UnitTypeIsConstruction = new Match<UnitType>() { @Override public boolean match(final UnitType type) { final UnitAttachment ua = UnitAttachment.get(type); if (ua == null) { return false; } return ua.getIsConstruction(); } }; public static final Match<Unit> UnitIsNotConstruction = new InverseMatch<>(UnitIsConstruction); public static final Match<Unit> UnitCanProduceUnitsAndIsConstruction = new CompositeMatchAnd<>(UnitCanProduceUnits, UnitIsConstruction); public static final Match<UnitType> UnitTypeCanProduceUnitsAndIsConstruction = new CompositeMatchAnd<>(UnitTypeCanProduceUnits, UnitTypeIsConstruction); public static final Match<Unit> UnitCanProduceUnitsAndIsInfrastructure = new CompositeMatchAnd<>(UnitCanProduceUnits, UnitIsInfrastructure); public static final Match<Unit> UnitCanProduceUnitsAndCanBeDamaged = new CompositeMatchAnd<>(UnitCanProduceUnits, UnitCanBeDamaged); /** * See if a unit can invade. Units with canInvadeFrom not set, or set to "all", can invade from any other unit. * Otherwise, units must have * a specific unit in this list to be able to invade from that unit. */ public static final Match<Unit> UnitCanInvade = new Match<Unit>() { @Override public boolean match(final Unit unit) { // is the unit being transported? final Unit transport = TripleAUnit.get(unit).getTransportedBy(); if (transport == null) { // Unit isn't transported so can Invade return true; } final UnitAttachment ua = UnitAttachment.get(unit.getType()); return ua.canInvadeFrom(transport.getUnitType().getName()); } }; public static final Match<RelationshipType> RelationshipTypeIsAllied = new Match<RelationshipType>() { @Override public boolean match(final RelationshipType relationship) { return relationship.getRelationshipTypeAttachment().isAllied(); } }; public static final Match<Relationship> RelationshipIsAllied = new Match<Relationship>() { @Override public boolean match(final Relationship relationship) { return relationship.getRelationshipType().getRelationshipTypeAttachment().isAllied(); } }; public static final Match<RelationshipType> RelationshipTypeIsNeutral = new Match<RelationshipType>() { @Override public boolean match(final RelationshipType relationship) { return relationship.getRelationshipTypeAttachment().isNeutral(); } }; public static final Match<Relationship> RelationshipIsNeutral = new Match<Relationship>() { @Override public boolean match(final Relationship relationship) { return relationship.getRelationshipType().getRelationshipTypeAttachment().isNeutral(); } }; public static final Match<RelationshipType> RelationshipTypeIsAtWar = new Match<RelationshipType>() { @Override public boolean match(final RelationshipType relationship) { return relationship.getRelationshipTypeAttachment().isWar(); } }; public static final Match<Relationship> RelationshipIsAtWar = new Match<Relationship>() { @Override public boolean match(final Relationship relationship) { return relationship.getRelationshipType().getRelationshipTypeAttachment().isWar(); } }; public static final Match<RelationshipType> RelationshipTypeCanMoveLandUnitsOverOwnedLand = new Match<RelationshipType>() { @Override public boolean match(final RelationshipType relationship) { return relationship.getRelationshipTypeAttachment().getCanMoveLandUnitsOverOwnedLand(); } }; /** * If the territory is not land, returns true. Else, tests relationship of the owners. */ public static Match<Territory> TerritoryAllowsCanMoveLandUnitsOverOwnedLand(final PlayerID ownerOfUnitsMoving, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (!Matches.TerritoryIsLand.match(t)) { return true; } final PlayerID tOwner = t.getOwner(); if (tOwner == null) { return true; } return data.getRelationshipTracker().canMoveLandUnitsOverOwnedLand(tOwner, ownerOfUnitsMoving); } }; } public static final Match<RelationshipType> RelationshipTypeCanMoveAirUnitsOverOwnedLand = new Match<RelationshipType>() { @Override public boolean match(final RelationshipType relationship) { return relationship.getRelationshipTypeAttachment().getCanMoveAirUnitsOverOwnedLand(); } }; /** * If the territory is not land, returns true. Else, tests relationship of the owners. */ public static Match<Territory> TerritoryAllowsCanMoveAirUnitsOverOwnedLand(final PlayerID ownerOfUnitsMoving, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (!Matches.TerritoryIsLand.match(t)) { return true; } final PlayerID tOwner = t.getOwner(); if (tOwner == null) { return true; } return data.getRelationshipTracker().canMoveAirUnitsOverOwnedLand(tOwner, ownerOfUnitsMoving); } }; } public static final Match<RelationshipType> RelationshipTypeCanLandAirUnitsOnOwnedLand = new Match<RelationshipType>() { @Override public boolean match(final RelationshipType relationship) { return relationship.getRelationshipTypeAttachment().getCanLandAirUnitsOnOwnedLand(); } }; public static final Match<RelationshipType> RelationshipTypeCanTakeOverOwnedTerritory = new Match<RelationshipType>() { @Override public boolean match(final RelationshipType relationship) { return relationship.getRelationshipTypeAttachment().getCanTakeOverOwnedTerritory(); } }; public static final Match<RelationshipType> RelationshipTypeGivesBackOriginalTerritories = new Match<RelationshipType>() { @Override public boolean match(final RelationshipType relationship) { return relationship.getRelationshipTypeAttachment().getGivesBackOriginalTerritories(); } }; public static final Match<RelationshipType> RelationshipTypeCanMoveIntoDuringCombatMove = new Match<RelationshipType>() { @Override public boolean match(final RelationshipType relationship) { return relationship.getRelationshipTypeAttachment().getCanMoveIntoDuringCombatMove(); } }; public static final Match<RelationshipType> RelationshipTypeCanMoveThroughCanals = new Match<RelationshipType>() { @Override public boolean match(final RelationshipType relationship) { return relationship.getRelationshipTypeAttachment().getCanMoveThroughCanals(); } }; public static final Match<RelationshipType> RelationshipTypeRocketsCanFlyOver = new Match<RelationshipType>() { @Override public boolean match(final RelationshipType relationship) { return relationship.getRelationshipTypeAttachment().getRocketsCanFlyOver(); } }; public static Match<String> isValidRelationshipName(final GameData data) { return new Match<String>() { @Override public boolean match(final String relationshipName) { return data.getRelationshipTypeList().getRelationshipType(relationshipName) != null; } }; } public static Match<PlayerID> isAtWar(final PlayerID player, final GameData data) { return new Match<PlayerID>() { @Override public boolean match(final PlayerID player2) { return Matches.RelationshipTypeIsAtWar .match(data.getRelationshipTracker().getRelationshipType(player, player2)); } }; } public static Match<PlayerID> isAtWarWithAnyOfThesePlayers(final Collection<PlayerID> players, final GameData data) { return new Match<PlayerID>() { @Override public boolean match(final PlayerID player2) { return data.getRelationshipTracker().isAtWarWithAnyOfThesePlayers(player2, players); } }; } public static Match<PlayerID> isAllied(final PlayerID player, final GameData data) { return new Match<PlayerID>() { @Override public boolean match(final PlayerID player2) { return Matches.RelationshipTypeIsAllied .match(data.getRelationshipTracker().getRelationshipType(player, player2)); } }; } public static Match<PlayerID> isAlliedWithAnyOfThesePlayers(final Collection<PlayerID> players, final GameData data) { return new Match<PlayerID>() { @Override public boolean match(final PlayerID player2) { return data.getRelationshipTracker().isAlliedWithAnyOfThesePlayers(player2, players); } }; } public static Match<PlayerID> isNeutral(final PlayerID player, final GameData data) { return new Match<PlayerID>() { @Override public boolean match(final PlayerID player2) { return Matches.RelationshipTypeIsNeutral .match(data.getRelationshipTracker().getRelationshipType(player, player2)); } }; } public static Match<PlayerID> isNeutralWithAnyOfThesePlayers(final Collection<PlayerID> players, final GameData data) { return new Match<PlayerID>() { @Override public boolean match(final PlayerID player2) { return data.getRelationshipTracker().isNeutralWithAnyOfThesePlayers(player2, players); } }; } public static Match<Unit> UnitIsOwnedAndIsFactoryOrCanProduceUnits(final PlayerID player) { return new Match<Unit>() { @Override public boolean match(final Unit u) { return (UnitCanProduceUnits.match(u) && unitIsOwnedBy(player).match(u)); } }; } public static Match<Unit> UnitCanReceivesAbilityWhenWith() { return new Match<Unit>() { @Override public boolean match(final Unit u) { return !UnitAttachment.get(u.getType()).getReceivesAbilityWhenWith().isEmpty(); } }; } public static Match<Unit> UnitCanReceivesAbilityWhenWith(final String filterForAbility, final String filterForUnitType) { return new Match<Unit>() { @Override public boolean match(final Unit u) { for (final String receives : UnitAttachment.get(u.getType()).getReceivesAbilityWhenWith()) { final String[] s = receives.split(":"); if (s[0].equals(filterForAbility) && s[1].equals(filterForUnitType)) { return true; } } return false; } }; } public static Match<Unit> UnitHasWhenCombatDamagedEffect() { return new Match<Unit>() { @Override public boolean match(final Unit u) { return !UnitAttachment.get(u.getType()).getWhenCombatDamaged().isEmpty(); } }; } public static Match<Unit> UnitHasWhenCombatDamagedEffect(final String filterForEffect) { return new Match<Unit>() { @Override public boolean match(final Unit u) { if (!UnitHasWhenCombatDamagedEffect().match(u)) { return false; } final TripleAUnit taUnit = (TripleAUnit) u; final int currentDamage = taUnit.getHits(); final ArrayList<Tuple<Tuple<Integer, Integer>, Tuple<String, String>>> whenCombatDamagedList = UnitAttachment.get(u.getType()).getWhenCombatDamaged(); for (final Tuple<Tuple<Integer, Integer>, Tuple<String, String>> key : whenCombatDamagedList) { final String effect = key.getSecond().getFirst(); if (!effect.equals(filterForEffect)) { continue; } final int damagedFrom = key.getFirst().getFirst(); final int damagedTo = key.getFirst().getSecond(); if (currentDamage >= damagedFrom && currentDamage <= damagedTo) { return true; } } return false; } }; } public static Match<Territory> TerritoryHasWhenCapturedByGoesTo() { return new Match<Territory>() { @Override public boolean match(final Territory t) { final TerritoryAttachment ta = TerritoryAttachment.get(t); if (ta == null) { return false; } return !ta.getWhenCapturedByGoesTo().isEmpty(); } }; } public static Match<Unit> UnitWhenCapturedChangesIntoDifferentUnitType() { return new Match<Unit>() { @Override public boolean match(final Unit u) { return !UnitAttachment.get(u.getType()).getWhenCapturedChangesInto().isEmpty(); } }; } public static Match<AbstractUserActionAttachment> AbstractUserActionAttachmentCanBeAttempted( final HashMap<ICondition, Boolean> testedConditions) { return new Match<AbstractUserActionAttachment>() { @Override public boolean match(final AbstractUserActionAttachment paa) { return paa.hasAttemptsLeft() && paa.canPerform(testedConditions); } }; } public static Match<PoliticalActionAttachment> PoliticalActionHasCostBetween(final int greaterThanEqualTo, final int lessThanEqualTo) { return new Match<PoliticalActionAttachment>() { @Override public boolean match(final PoliticalActionAttachment paa) { return (paa.getCostPU() >= greaterThanEqualTo && paa.getCostPU() <= lessThanEqualTo); } }; } public static final Match<Unit> UnitCanOnlyPlaceInOriginalTerritories = new Match<Unit>() { @Override public boolean match(final Unit u) { final UnitAttachment ua = UnitAttachment.get(u.getType()); final Set<String> specialOptions = ua.getSpecial(); for (final String option : specialOptions) { if (option.equals("canOnlyPlaceInOriginalTerritories")) { return true; } } return false; } }; /** * Accounts for OccupiedTerrOf. Returns false if there is no territory attachment (like if it is water). */ public static Match<Territory> TerritoryIsOriginallyOwnedBy(final PlayerID player) { return new Match<Territory>() { @Override public boolean match(final Territory t) { final TerritoryAttachment ta = TerritoryAttachment.get(t); if (ta == null) { return false; } final PlayerID originalOwner = ta.getOriginalOwner(); if (originalOwner == null) { return player == null; } return originalOwner.equals(player); } }; } public static Match<PlayerID> isAlliedAndAlliancesCanChainTogether(final PlayerID player, final GameData data) { return new Match<PlayerID>() { @Override public boolean match(final PlayerID player2) { return RelationshipTypeIsAlliedAndAlliancesCanChainTogether .match(data.getRelationshipTracker().getRelationshipType(player, player2)); } }; } public static final Match<RelationshipType> RelationshipTypeIsAlliedAndAlliancesCanChainTogether = new Match<RelationshipType>() { @Override public boolean match(final RelationshipType rt) { return RelationshipTypeIsAllied.match(rt) && rt.getRelationshipTypeAttachment().getAlliancesCanChainTogether(); } }; public static final Match<RelationshipType> RelationshipTypeIsDefaultWarPosition = new Match<RelationshipType>() { @Override public boolean match(final RelationshipType rt) { return rt.getRelationshipTypeAttachment().getIsDefaultWarPosition(); } }; /** * If player is null, this match Will return true if ANY of the relationship changes match the conditions. (since * paa's can have more than * 1 change). * * @param player * CAN be null * @param currentRelation * cannot be null * @param newRelation * cannot be null * @param data * cannot be null */ public static Match<PoliticalActionAttachment> politicalActionIsRelationshipChangeOf(final PlayerID player, final Match<RelationshipType> currentRelation, final Match<RelationshipType> newRelation, final GameData data) { return new Match<PoliticalActionAttachment>() { @Override public boolean match(final PoliticalActionAttachment paa) { for (final String relationshipChangeString : paa.getRelationshipChange()) { final String[] relationshipChange = relationshipChangeString.split(":"); final PlayerID p1 = data.getPlayerList().getPlayerID(relationshipChange[0]); final PlayerID p2 = data.getPlayerList().getPlayerID(relationshipChange[1]); if (player != null && !(p1.equals(player) || p2.equals(player))) { continue; } final RelationshipType currentType = data.getRelationshipTracker().getRelationshipType(p1, p2); final RelationshipType newType = data.getRelationshipTypeList().getRelationshipType(relationshipChange[2]); if (currentRelation.match(currentType) && newRelation.match(newType)) { return true; } } return false; } }; } public static Match<PoliticalActionAttachment> politicalActionAffectsAtLeastOneAlivePlayer( final PlayerID currentPlayer, final GameData data) { return new Match<PoliticalActionAttachment>() { @Override public boolean match(final PoliticalActionAttachment paa) { for (final String relationshipChangeString : paa.getRelationshipChange()) { final String[] relationshipChange = relationshipChangeString.split(":"); final PlayerID p1 = data.getPlayerList().getPlayerID(relationshipChange[0]); final PlayerID p2 = data.getPlayerList().getPlayerID(relationshipChange[1]); if (!currentPlayer.equals(p1)) { if (p1.amNotDeadYet(data)) { return true; } } if (!currentPlayer.equals(p2)) { if (p2.amNotDeadYet(data)) { return true; } } } return false; } }; } public static Match<Territory> airCanLandOnThisAlliedNonConqueredLandTerritory(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (!Matches.TerritoryIsLand.match(t)) { return false; } final BattleTracker bt = AbstractMoveDelegate.getBattleTracker(data); if (bt.wasConquered(t)) { return false; } final PlayerID owner = t.getOwner(); if (owner == null || owner.isNull()) { return false; } final RelationshipTracker rt = data.getRelationshipTracker(); return !(!rt.canMoveAirUnitsOverOwnedLand(player, owner) || !rt.canLandAirUnitsOnOwnedLand(player, owner)); } }; } public static Match<Territory> territoryAllowsRocketsCanFlyOver(final PlayerID player, final GameData data) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (!Matches.TerritoryIsLand.match(t)) { return true; } final PlayerID owner = t.getOwner(); if (owner == null || owner.isNull()) { return true; } final RelationshipTracker rt = data.getRelationshipTracker(); return rt.rocketsCanFlyOver(player, owner); } }; } public static Match<Unit> unitCanScrambleOnRouteDistance(final Route route) { return new Match<Unit>() { @Override public boolean match(final Unit u) { return UnitAttachment.get(u.getType()).getMaxScrambleDistance() >= route.getMovementCost(u); } }; } public static final Match<Unit> unitCanIntercept = new Match<Unit>() { @Override public boolean match(final Unit u) { return UnitAttachment.get(u.getType()).getCanIntercept(); } }; public static final Match<Unit> unitCanEscort = new Match<Unit>() { @Override public boolean match(final Unit u) { return UnitAttachment.get(u.getType()).getCanEscort(); } }; public static final Match<Unit> unitCanAirBattle = new Match<Unit>() { @Override public boolean match(final Unit u) { return UnitAttachment.get(u.getType()).getCanAirBattle(); } }; public static Match<Territory> territoryIsOwnedByPlayerWhosRelationshipTypeCanTakeOverOwnedTerritoryAndPassableAndNotWater( final PlayerID attacker) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (t.getOwner().equals(attacker)) { return false; } if (t.getOwner().equals(PlayerID.NULL_PLAYERID) && t.isWater()) { return false; } if (!Matches.TerritoryIsPassableAndNotRestricted(attacker, t.getData()).match(t)) { return false; } return RelationshipTypeCanTakeOverOwnedTerritory .match(t.getData().getRelationshipTracker().getRelationshipType(attacker, t.getOwner())); } }; } public static Match<Territory> territoryOwnerRelationshipTypeCanMoveIntoDuringCombatMove( final PlayerID movingPlayer) { return new Match<Territory>() { @Override public boolean match(final Territory t) { if (t.getOwner().equals(movingPlayer)) { return true; } if (t.getOwner().equals(PlayerID.NULL_PLAYERID) && t.isWater()) { return true; } return t.getData().getRelationshipTracker().canMoveIntoDuringCombatMove(movingPlayer, t.getOwner()); } }; } public static Match<Unit> UnitCanBeInBattle(final boolean attack, final boolean isLandBattle, final int battleRound, final boolean includeAttackersThatCanNotMove, final boolean doNotIncludeAA, final boolean doNotIncludeBombardingSeaUnits) { return new Match<Unit>() { @Override public boolean match(final Unit u) { return Matches.UnitTypeCanBeInBattle(attack, isLandBattle, u.getOwner(), battleRound, includeAttackersThatCanNotMove, doNotIncludeAA, doNotIncludeBombardingSeaUnits).match(u.getType()); } }; } public static Match<UnitType> UnitTypeCanBeInBattle(final boolean attack, final boolean isLandBattle, final PlayerID player, final int battleRound, final boolean includeAttackersThatCanNotMove, final boolean doNotIncludeAA, final boolean doNotIncludeBombardingSeaUnits) { return new Match<UnitType>() { @Override public boolean match(final UnitType ut) { // we want to filter out anything like factories, or units that have no combat ability AND cannot be taken // casualty. // in addition, as of right now AA guns cannot fire on the offensive side, so we want to take them out too, // unless they have other // combat abilities. final Match<UnitType> supporterOrNotInfrastructure = new CompositeMatchOr<>(Matches.UnitTypeIsInfrastructure.invert(), Matches.UnitTypeIsSupporterOrHasCombatAbility(attack, player)); final Match<UnitType> combat; if (attack) { // AND match final CompositeMatch<UnitType> attackMatchAND = new CompositeMatchAnd<>(); attackMatchAND.add(supporterOrNotInfrastructure); if (!includeAttackersThatCanNotMove) { attackMatchAND.add(Matches.UnitTypeCanNotMoveDuringCombatMove.invert()); attackMatchAND.add(Matches.UnitTypeCanMove(player)); } if (isLandBattle) { if (doNotIncludeBombardingSeaUnits) { attackMatchAND.add(Matches.UnitTypeIsSea.invert()); } } else { // is sea battle attackMatchAND.add(Matches.UnitTypeIsLand.invert()); } // assign it combat = attackMatchAND; } else { // defense // AND match final CompositeMatch<UnitType> defenseMatchAND = new CompositeMatchAnd<>(); { // OR match final CompositeMatch<UnitType> defenseMatchOR = new CompositeMatchOr<>(); if (!doNotIncludeAA) { defenseMatchOR.add(new CompositeMatchAnd<>(Matches.UnitTypeIsAAforCombatOnly, Matches.UnitTypeIsAAthatCanFireOnRound(battleRound))); } defenseMatchOR.add(supporterOrNotInfrastructure); defenseMatchAND.add(defenseMatchOR); } if (isLandBattle) { defenseMatchAND.add(Matches.UnitTypeIsSea.invert()); } else { // is sea battle defenseMatchAND.add(Matches.UnitTypeIsLand.invert()); } // assign it combat = defenseMatchAND; } return combat.match(ut); } }; } public static final Match<Unit> UnitIsAirborne = new Match<Unit>() { @Override public boolean match(final Unit obj) { return ((TripleAUnit) obj).getAirborne(); } }; public static <T> Match<T> isNotInList(final List<T> list) { return new Match<T>() { @Override public boolean match(final T ter) { return !list.contains(ter); } }; } /** Creates new Matches. */ private Matches() {} }