diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/delegate/battle/MustFightBattle.java b/game-app/game-core/src/main/java/games/strategy/triplea/delegate/battle/MustFightBattle.java index a02f7dd5396..a040865b496 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/delegate/battle/MustFightBattle.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/delegate/battle/MustFightBattle.java @@ -1272,6 +1272,7 @@ private List removeNonCombatants( final boolean attacking, final boolean removeForNextRound) { int battleRound = (removeForNextRound ? round + 1 : round); + // Note: Also done in FiringGroupSplitterGeneral when determining step names. They must match. return CollectionUtils.getMatches( units, Matches.unitCanParticipateInCombat( diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/delegate/battle/steps/fire/general/FiringGroupSplitterGeneral.java b/game-app/game-core/src/main/java/games/strategy/triplea/delegate/battle/steps/fire/general/FiringGroupSplitterGeneral.java index 9bf9ccca39a..1b3d4e925f2 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/delegate/battle/steps/fire/general/FiringGroupSplitterGeneral.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/delegate/battle/steps/fire/general/FiringGroupSplitterGeneral.java @@ -56,10 +56,28 @@ public enum Type { @Override public List apply(final BattleState battleState) { + final Collection enemyUnits = + CollectionUtils.getMatches( + battleState.filterUnits(ALIVE, side.getOpposite()), + PredicateBuilder.of(Matches.unitIsNotInfrastructure()) + .andIf(side == DEFENSE, Matches.unitIsSuicideOnAttack().negate()) + .andIf(side == OFFENSE, Matches.unitIsSuicideOnDefense().negate()) + .build()); + + // Filter participants (same as is done in MustFightBattle.removeNonCombatants()), so that we + // don't end up generating combat step names for units that will be excluded. + final Predicate canParticipateInCombat = + Matches.unitCanParticipateInCombat( + side == OFFENSE, + battleState.getPlayer(OFFENSE), + battleState.getBattleSite(), + 1, + enemyUnits); final Collection canFire = CollectionUtils.getMatches( battleState.filterUnits(ACTIVE, side), PredicateBuilder.of(getFiringUnitPredicate(battleState)) + .and(canParticipateInCombat) // Remove offense allied units if allied air can not participate .andIf( side == OFFENSE @@ -68,14 +86,6 @@ public List apply(final BattleState battleState) { Matches.unitIsOwnedBy(battleState.getPlayer(side))) .build()); - final Collection enemyUnits = - CollectionUtils.getMatches( - battleState.filterUnits(ALIVE, side.getOpposite()), - PredicateBuilder.of(Matches.unitIsNotInfrastructure()) - .andIf(side == DEFENSE, Matches.unitIsSuicideOnAttack().negate()) - .andIf(side == OFFENSE, Matches.unitIsSuicideOnDefense().negate()) - .build()); - final List firingGroups = new ArrayList<>(); final List targetGroups = TargetGroup.newTargetGroups(canFire, enemyUnits); diff --git a/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/BattleStepsTest.java b/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/BattleStepsTest.java index 2d9b02dd5e8..6a940293b81 100644 --- a/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/BattleStepsTest.java +++ b/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/BattleStepsTest.java @@ -65,6 +65,10 @@ public class BattleStepsTest { @Mock GamePlayer defender; @Mock TechAttachment techAttachment; + public static MockGameData givenGameDataWithLenientProperties() { + return givenGameData().withLenientProperties(); + } + @BeforeEach public void givenPlayers() { lenient().when(attacker.getName()).thenReturn("mockAttacker"); @@ -79,8 +83,8 @@ public static Territory givenSeaBattleSite() { @Value public static class UnitAndAttachment { - private Unit unit; - private UnitAttachment unitAttachment; + Unit unit; + UnitAttachment unitAttachment; } public static UnitAndAttachment newUnitAndAttachment() { @@ -93,6 +97,12 @@ public static UnitAndAttachment newUnitAndAttachment() { return new UnitAndAttachment(unit, unitAttachment); } + public static UnitAndAttachment newSeaUnitAndAttachment() { + final var result = newUnitAndAttachment(); + lenient().when(result.unitAttachment.getIsSea()).thenReturn(true); + return result; + } + public static Unit givenAnyUnit() { return newUnitAndAttachment().unit; } @@ -109,15 +119,21 @@ public static Unit givenUnitFirstStrike() { return unitAndAttachment.unit; } - public static Unit givenUnitFirstStrikeSuicideOnAttack() { - final UnitAndAttachment unitAndAttachment = newUnitAndAttachment(); + public static Unit givenSeaUnitFirstStrike() { + final UnitAndAttachment unitAndAttachment = newSeaUnitAndAttachment(); + lenient().when(unitAndAttachment.unitAttachment.getIsFirstStrike()).thenReturn(true); + return unitAndAttachment.unit; + } + + public static Unit givenSeaUnitFirstStrikeSuicideOnAttack() { + final UnitAndAttachment unitAndAttachment = newSeaUnitAndAttachment(); lenient().when(unitAndAttachment.unitAttachment.getIsFirstStrike()).thenReturn(true); lenient().when(unitAndAttachment.unitAttachment.getIsSuicideOnAttack()).thenReturn(true); return unitAndAttachment.unit; } - public static Unit givenUnitFirstStrikeSuicideOnDefense() { - final UnitAndAttachment unitAndAttachment = newUnitAndAttachment(); + public static Unit givenSeaUnitFirstStrikeSuicideOnDefense() { + final UnitAndAttachment unitAndAttachment = newSeaUnitAndAttachment(); lenient().when(unitAndAttachment.unitAttachment.getIsFirstStrike()).thenReturn(true); lenient().when(unitAndAttachment.unitAttachment.getIsSuicideOnDefense()).thenReturn(true); return unitAndAttachment.unit; @@ -130,38 +146,46 @@ public static Unit givenUnitFirstStrikeAndEvade() { return unitAndAttachment.unit; } - public static Unit givenUnitFirstStrikeAndEvadeAndCanNotBeTargetedBy(final UnitType otherType) { - final UnitAndAttachment unitAndAttachment = newUnitAndAttachment(); + public static Unit givenSeaUnitFirstStrikeAndEvade() { + final UnitAndAttachment unitAndAttachment = newSeaUnitAndAttachment(); + when(unitAndAttachment.unitAttachment.getIsFirstStrike()).thenReturn(true); + when(unitAndAttachment.unitAttachment.getCanEvade()).thenReturn(true); + return unitAndAttachment.unit; + } + + public static Unit givenSeaUnitFirstStrikeAndEvadeAndCanNotBeTargetedBy( + final UnitType otherType) { + final UnitAndAttachment unitAndAttachment = newSeaUnitAndAttachment(); when(unitAndAttachment.unitAttachment.getIsFirstStrike()).thenReturn(true); when(unitAndAttachment.unitAttachment.getCanEvade()).thenReturn(true); when(unitAndAttachment.unitAttachment.getCanNotBeTargetedBy()).thenReturn(Set.of(otherType)); return unitAndAttachment.unit; } - public static Unit givenUnitCanEvadeAndCanNotBeTargetedByRandomUnit() { - final UnitAndAttachment unitAndAttachment = newUnitAndAttachment(); + public static Unit givenSeaUnitCanEvadeAndCanNotBeTargetedByRandomUnit() { + final UnitAndAttachment unitAndAttachment = newSeaUnitAndAttachment(); when(unitAndAttachment.unitAttachment.getCanEvade()).thenReturn(true); when(unitAndAttachment.unitAttachment.getCanNotBeTargetedBy()) .thenReturn(Set.of(mock(UnitType.class))); return unitAndAttachment.unit; } - public static Unit givenUnitCanNotBeTargetedBy(final UnitType otherType) { - final UnitAndAttachment unitAndAttachment = newUnitAndAttachment(); + public static Unit givenSeaUnitCanNotBeTargetedBy(final UnitType otherType) { + final UnitAndAttachment unitAndAttachment = newSeaUnitAndAttachment(); when(unitAndAttachment.unitAttachment.getCanNotBeTargetedBy()).thenReturn(Set.of(otherType)); return unitAndAttachment.unit; } public static Unit givenUnitDestroyer() { - final UnitAndAttachment unitAndAttachment = newUnitAndAttachment(); + final UnitAndAttachment unitAndAttachment = newSeaUnitAndAttachment(); lenient().when(unitAndAttachment.unitAttachment.getIsDestroyer()).thenReturn(true); + lenient().when(unitAndAttachment.unitAttachment.getIsSea()).thenReturn(true); return unitAndAttachment.unit; } public static Unit givenUnitSeaTransport() { - final UnitAndAttachment unitAndAttachment = newUnitAndAttachment(); + final UnitAndAttachment unitAndAttachment = newSeaUnitAndAttachment(); when(unitAndAttachment.unitAttachment.getTransportCapacity()).thenReturn(2); - when(unitAndAttachment.unitAttachment.getIsSea()).thenReturn(true); return unitAndAttachment.unit; } @@ -211,9 +235,7 @@ public static Unit givenUnitIsAir() { } public static Unit givenUnitIsSea() { - final UnitAndAttachment unitAndAttachment = newUnitAndAttachment(); - when(unitAndAttachment.unitAttachment.getIsSea()).thenReturn(true); - return unitAndAttachment.unit; + return newSeaUnitAndAttachment().unit; } public static Unit givenUnitWasAmphibious() { @@ -315,11 +337,7 @@ void basicLandBattle() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) @@ -342,13 +360,7 @@ void bombardOnFirstRun() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withNavalBombardCasualtiesReturnFire(false) - .withCaptureUnitsOnEnteringTerritory(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) @@ -375,11 +387,7 @@ void bombardOnSubsequentRun() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .attacker(attacker) .defender(defender) .battleRound(2) @@ -395,18 +403,13 @@ void bombardOnSubsequentRun() { @Test @DisplayName("Verify impossible sea battle with bombarding will not add a bombarding step") void impossibleSeaBattleWithBombarding() { - final Unit unit1 = givenAnyUnit(); - final Unit unit2 = givenAnyUnit(); + final Unit unit1 = givenUnitIsSea(); + final Unit unit2 = givenUnitIsSea(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withTransportCasualtiesRestricted(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .battleRound(1) .attacker(attacker) .defender(defender) @@ -462,11 +465,7 @@ void noAirTransportTech() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .battleRound(1) .attacker(attacker) .defender(defender) @@ -488,11 +487,7 @@ void paratroopersSubsequentRun() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .attacker(attacker) .defender(defender) .battleRound(2) @@ -538,18 +533,13 @@ void emptyParatroopersFirstRun() { @Test @DisplayName("Verify impossible sea battle with paratroopers will not add a paratrooper step") void impossibleSeaBattleWithParatroopers() { - final Unit unit1 = givenAnyUnit(); - final Unit unit2 = givenAnyUnit(); + final Unit unit1 = givenUnitIsSea(); + final Unit unit2 = givenUnitIsSea(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withTransportCasualtiesRestricted(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .battleRound(1) .attacker(attacker) .defender(defender) @@ -681,8 +671,7 @@ void defendingSubsRetreatIfNoDestroyersAndCanRetreatBeforeBattle() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) + givenGameDataWithLenientProperties() .withSubRetreatBeforeBattle(true) .withSubmersibleSubs(true) .withAlliedAirIndependent(true) @@ -710,8 +699,7 @@ void defendingSubsNotRetreatIfDestroyersAndCanRetreatBeforeBattle() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) + givenGameDataWithLenientProperties() .withSubRetreatBeforeBattle(true) .withSubmersibleSubs(true) .withAlliedAirIndependent(true) @@ -739,11 +727,7 @@ void defendingSubsRetreatIfCanNotRetreatBeforeBattle() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) @@ -759,18 +743,16 @@ void defendingSubsRetreatIfCanNotRetreatBeforeBattle() { "Verify defending firstStrike submerge before battle " + "if SUB_RETREAT_BEFORE_BATTLE and SUBMERSIBLE_SUBS are true") void defendingFirstStrikeSubmergeBeforeBattleIfSubmersibleSubsAndRetreatBeforeBattle() { - final Unit unit1 = givenAnyUnit(); - final Unit unit2 = givenUnitFirstStrikeAndEvade(); + final Unit unit1 = givenUnitIsSea(); + final Unit unit2 = givenSeaUnitFirstStrikeAndEvade(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() + givenGameDataWithLenientProperties() .withSubRetreatBeforeBattle(true) .withSubmersibleSubs(true) - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withWW2V2(false) .withDefendingSubsSneakAttack(true) .withAlliedAirIndependent(true) .build()) @@ -778,7 +760,7 @@ void defendingFirstStrikeSubmergeBeforeBattleIfSubmersibleSubsAndRetreatBeforeBa .defender(defender) .attackingUnits(List.of(unit1)) .defendingUnits(List.of(unit2)) - .battleSite(battleSite) + .battleSite(givenSeaBattleSite()) .build()); assertThat( @@ -796,15 +778,13 @@ void defendingFirstStrikeSubmergeBeforeBattleIfSubmersibleSubsAndRetreatBeforeBa @DisplayName("Verify unescorted attacking transports are removed if casualties are restricted") void unescortedAttackingTransportsAreRemovedWhenCasualtiesAreRestricted() { final Unit unit1 = givenUnitSeaTransport(); - final Unit unit2 = givenAnyUnit(); + final Unit unit2 = givenUnitIsSea(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) + givenGameDataWithLenientProperties() .withTransportCasualtiesRestricted(true) .withAlliedAirIndependent(true) .build()) @@ -823,19 +803,14 @@ void unescortedAttackingTransportsAreRemovedWhenCasualtiesAreRestricted() { @DisplayName( "Verify unescorted attacking transports are not removed if casualties are not restricted") void unescortedAttackingTransportsAreNotRemovedWhenCasualtiesAreNotRestricted() { - final UnitAndAttachment unitAndAttachment = newUnitAndAttachment(); + final UnitAndAttachment unitAndAttachment = newSeaUnitAndAttachment(); final Unit unit1 = unitAndAttachment.unit; - final Unit unit2 = givenAnyUnit(); + final Unit unit2 = givenUnitIsSea(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withTransportCasualtiesRestricted(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) @@ -850,16 +825,14 @@ void unescortedAttackingTransportsAreNotRemovedWhenCasualtiesAreNotRestricted() @Test @DisplayName("Verify unescorted defending transports are removed if casualties are restricted") void unescortedDefendingTransportsAreRemovedWhenCasualtiesAreRestricted() { - final Unit unit1 = givenAnyUnit(); + final Unit unit1 = givenUnitIsSea(); final Unit unit2 = givenUnitSeaTransport(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) + givenGameDataWithLenientProperties() .withTransportCasualtiesRestricted(true) .withAlliedAirIndependent(true) .build()) @@ -878,20 +851,15 @@ void unescortedDefendingTransportsAreRemovedWhenCasualtiesAreRestricted() { @DisplayName( "Verify unescorted defending transports are removed if casualties are not restricted") void unescortedDefendingTransportsAreNotRemovedWhenCasualtiesAreNotRestricted() { - final Unit unit1 = givenAnyUnit(); - final UnitAndAttachment unitAndAttachment = newUnitAndAttachment(); + final Unit unit1 = givenUnitIsSea(); + final UnitAndAttachment unitAndAttachment = newSeaUnitAndAttachment(); final Unit unit2 = unitAndAttachment.unit; final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withTransportCasualtiesRestricted(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) @@ -908,19 +876,14 @@ void unescortedDefendingTransportsAreNotRemovedWhenCasualtiesAreNotRestricted() "Verify basic attacker firstStrike " + "(no other attackers, no special defenders, all options false)") void attackingFirstStrikeBasic() { - final Unit unit1 = givenUnitFirstStrikeAndEvade(); + final Unit unit1 = givenSeaUnitFirstStrikeAndEvade(); final Unit unit2 = givenAnyUnit(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withWW2V2(false) - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) @@ -941,24 +904,19 @@ void attackingFirstStrikeBasic() { @Test @DisplayName("Verify attacker firstStrike with destroyers") void attackingFirstStrikeWithDestroyers() { - final Unit unit1 = givenUnitFirstStrike(); + final Unit unit1 = givenSeaUnitFirstStrike(); final Unit unit2 = givenUnitDestroyer(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withWW2V2(false) - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) .defendingUnits(List.of(unit2)) - .battleSite(battleSite) + .battleSite(givenSeaBattleSite()) .build()); assertThat( @@ -982,13 +940,7 @@ void defendingFirstStrikeBasic() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withWW2V2(false) - .withDefendingSubsSneakAttack(false) - .withSubRetreatBeforeBattle(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) @@ -1008,26 +960,22 @@ void defendingFirstStrikeBasic() { @Test @DisplayName("Verify defender firstStrike with DEFENDING_SUBS_SNEAK_ATTACK true") void defendingFirstStrikeWithSneakAttackAllowed() { - final Unit unit1 = givenAnyUnit(); - final Unit unit2 = givenUnitFirstStrikeAndEvade(); + final Unit unit1 = givenUnitIsSea(); + final Unit unit2 = givenSeaUnitFirstStrikeAndEvade(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withSubRetreatBeforeBattle(false) - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) + givenGameDataWithLenientProperties() .withAlliedAirIndependent(true) - .withTransportCasualtiesRestricted(false) - .withWW2V2(false) .withDefendingSubsSneakAttack(true) .build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) .defendingUnits(List.of(unit2)) - .battleSite(battleSite) + .battleSite(givenSeaBattleSite()) .build()); assertThat( @@ -1050,11 +998,8 @@ void defendingFirstStrikeWithWW2v2() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withSubRetreatBeforeBattle(false) - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) + givenGameDataWithLenientProperties() .withAlliedAirIndependent(true) - .withTransportCasualtiesRestricted(false) .withWW2V2(true) .build()) .attacker(attacker) @@ -1078,24 +1023,21 @@ void defendingFirstStrikeWithWW2v2() { @DisplayName("Verify defender firstStrike with WW2v2 true and attacker destroyers") void defendingFirstStrikeWithWW2v2AndDestroyers() { final Unit unit1 = givenUnitDestroyer(); - final Unit unit2 = givenUnitFirstStrikeAndEvade(); + final Unit unit2 = givenSeaUnitFirstStrikeAndEvade(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withSubRetreatBeforeBattle(false) - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) + givenGameDataWithLenientProperties() .withAlliedAirIndependent(true) - .withTransportCasualtiesRestricted(false) .withWW2V2(true) .build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) .defendingUnits(List.of(unit2)) - .battleSite(battleSite) + .battleSite(givenSeaBattleSite()) .build()); assertThat( @@ -1119,13 +1061,7 @@ void attackingDefendingFirstStrikeBasic() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withWW2V2(false) - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withDefendingSubsSneakAttack(false) - .withSubRetreatBeforeBattle(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) @@ -1146,18 +1082,14 @@ void attackingDefendingFirstStrikeBasic() { @Test @DisplayName("Verify attacking/defender firstStrikes with DEFENDING_SUBS_SNEAK_ATTACK true") void attackingDefendingFirstStrikeWithSneakAttackAllowed() { - final Unit unit1 = givenUnitFirstStrikeAndEvade(); - final Unit unit2 = givenUnitFirstStrikeAndEvade(); + final Unit unit1 = givenSeaUnitFirstStrikeAndEvade(); + final Unit unit2 = givenSeaUnitFirstStrikeAndEvade(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withTransportCasualtiesRestricted(false) - .withWW2V2(false) + givenGameDataWithLenientProperties() .withDefendingSubsSneakAttack(true) .withAlliedAirIndependent(true) .build()) @@ -1180,17 +1112,14 @@ void attackingDefendingFirstStrikeWithSneakAttackAllowed() { @Test @DisplayName("Verify attacking/defender firstStrikes with WW2v2 true") void attackingDefendingFirstStrikeWithWW2v2() { - final Unit unit1 = givenUnitFirstStrikeAndEvade(); - final Unit unit2 = givenUnitFirstStrikeAndEvade(); + final Unit unit1 = givenSeaUnitFirstStrikeAndEvade(); + final Unit unit2 = givenSeaUnitFirstStrikeAndEvade(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withTransportCasualtiesRestricted(false) + givenGameDataWithLenientProperties() .withWW2V2(true) .withAlliedAirIndependent(true) .build()) @@ -1214,8 +1143,8 @@ void attackingDefendingFirstStrikeWithWW2v2() { @DisplayName( "Verify attacking/defender firstStrikes with WW2v2 true and attacker/defender destroyers") void attackingDefendingFirstStrikeWithWW2v2AndDestroyers() { - final Unit unit1 = givenUnitFirstStrike(); - final Unit unit2 = givenUnitFirstStrikeAndEvade(); + final Unit unit1 = givenSeaUnitFirstStrike(); + final Unit unit2 = givenSeaUnitFirstStrikeAndEvade(); final Unit unit3 = givenUnitDestroyer(); final Unit unit4 = givenUnitDestroyer(); @@ -1223,10 +1152,7 @@ void attackingDefendingFirstStrikeWithWW2v2AndDestroyers() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withTransportCasualtiesRestricted(false) + givenGameDataWithLenientProperties() .withWW2V2(true) .withAlliedAirIndependent(true) .build()) @@ -1253,19 +1179,15 @@ void attackingDefendingFirstStrikeWithWW2v2AndDestroyers() { "Verify attacking/defender firstStrikes with " + "DEFENDING_SUBS_SNEAK_ATTACK true and defender destroyers") void attackingDefendingFirstStrikeWithSneakAttackAllowedAndDefendingDestroyers() { - final Unit unit1 = givenUnitFirstStrike(); - final Unit unit2 = givenUnitFirstStrikeAndEvade(); + final Unit unit1 = givenSeaUnitFirstStrike(); + final Unit unit2 = givenSeaUnitFirstStrikeAndEvade(); final Unit unit3 = givenUnitDestroyer(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withTransportCasualtiesRestricted(false) - .withWW2V2(false) + givenGameDataWithLenientProperties() .withDefendingSubsSneakAttack(true) .withAlliedAirIndependent(true) .build()) @@ -1290,18 +1212,15 @@ void attackingDefendingFirstStrikeWithSneakAttackAllowedAndDefendingDestroyers() @Test @DisplayName("Verify attacking/defender firstStrikes with WW2v2 true and defender destroyers") void attackingDefendingFirstStrikeWithWW2v2AndDefendingDestroyers() { - final Unit unit1 = givenUnitFirstStrike(); - final Unit unit2 = givenUnitFirstStrikeAndEvade(); + final Unit unit1 = givenSeaUnitFirstStrike(); + final Unit unit2 = givenSeaUnitFirstStrikeAndEvade(); final Unit unit3 = givenUnitDestroyer(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withTransportCasualtiesRestricted(false) + givenGameDataWithLenientProperties() .withWW2V2(true) .withAlliedAirIndependent(true) .build()) @@ -1326,18 +1245,15 @@ void attackingDefendingFirstStrikeWithWW2v2AndDefendingDestroyers() { @Test @DisplayName("Verify attacking/defender firstStrikes with WW2v2 true and attacking destroyers") void attackingDefendingFirstStrikeWithWW2v2AndAttackingDestroyers() { - final Unit unit1 = givenUnitFirstStrikeAndEvade(); - final Unit unit2 = givenUnitFirstStrikeAndEvade(); + final Unit unit1 = givenSeaUnitFirstStrikeAndEvade(); + final Unit unit2 = givenSeaUnitFirstStrikeAndEvade(); final Unit unit3 = givenUnitDestroyer(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withTransportCasualtiesRestricted(false) + givenGameDataWithLenientProperties() .withWW2V2(true) .withAlliedAirIndependent(true) .build()) @@ -1363,16 +1279,13 @@ void attackingDefendingFirstStrikeWithWW2v2AndAttackingDestroyers() { @DisplayName("Verify attacking firstStrikes against air") void attackingFirstStrikeVsAir() { final Unit unit2 = givenUnitIsAir(); - final Unit unit1 = givenUnitFirstStrikeAndEvadeAndCanNotBeTargetedBy(unit2.getType()); + final Unit unit1 = givenSeaUnitFirstStrikeAndEvadeAndCanNotBeTargetedBy(unit2.getType()); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withTransportCasualtiesRestricted(false) + givenGameDataWithLenientProperties() .withWW2V2(true) .withAlliedAirIndependent(true) .build()) @@ -1397,18 +1310,15 @@ void attackingFirstStrikeVsAir() { @DisplayName("Verify attacking firstStrikes against air with other units on both sides") void attackingFirstStrikeVsAirWithOtherUnits() { final Unit unit2 = givenUnitIsAir(); - final Unit unit1 = givenUnitFirstStrikeAndEvadeAndCanNotBeTargetedBy(unit2.getType()); - final Unit unit3 = givenAnyUnit(); - final Unit unit4 = givenAnyUnit(); + final Unit unit1 = givenSeaUnitFirstStrikeAndEvadeAndCanNotBeTargetedBy(unit2.getType()); + final Unit unit3 = givenUnitIsSea(); + final Unit unit4 = givenUnitIsSea(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withTransportCasualtiesRestricted(false) + givenGameDataWithLenientProperties() .withWW2V2(true) .withAlliedAirIndependent(true) .build()) @@ -1435,17 +1345,14 @@ void attackingFirstStrikeVsAirWithOtherUnits() { @DisplayName("Verify attacking firstStrikes against air with destroyer") void attackingFirstStrikeVsAirAndDestroyer() { final Unit unit2 = givenUnitIsAir(); - final Unit unit1 = givenUnitFirstStrike(); + final Unit unit1 = givenSeaUnitFirstStrike(); final Unit unit3 = givenUnitDestroyer(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withTransportCasualtiesRestricted(false) + givenGameDataWithLenientProperties() .withWW2V2(true) .withAlliedAirIndependent(true) .build()) @@ -1469,16 +1376,13 @@ void attackingFirstStrikeVsAirAndDestroyer() { @DisplayName("Verify defending firstStrikes against air") void defendingFirstStrikeVsAir() { final Unit unit1 = givenUnitIsAir(); - final Unit unit2 = givenUnitFirstStrikeAndEvadeAndCanNotBeTargetedBy(unit1.getType()); + final Unit unit2 = givenSeaUnitFirstStrikeAndEvadeAndCanNotBeTargetedBy(unit1.getType()); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withTransportCasualtiesRestricted(false) + givenGameDataWithLenientProperties() .withWW2V2(true) .withAlliedAirIndependent(true) .build()) @@ -1503,17 +1407,14 @@ void defendingFirstStrikeVsAir() { @DisplayName("Verify defending firstStrikes against air with destroyer") void defendingFirstStrikeVsAirAndDestroyer() { final Unit unit1 = givenUnitIsAir(); - final Unit unit2 = givenUnitFirstStrikeAndEvade(); + final Unit unit2 = givenSeaUnitFirstStrikeAndEvade(); final Unit unit3 = givenUnitDestroyer(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withTransportCasualtiesRestricted(false) + givenGameDataWithLenientProperties() .withWW2V2(true) .withAlliedAirIndependent(true) .build()) @@ -1537,18 +1438,15 @@ void defendingFirstStrikeVsAirAndDestroyer() { @DisplayName("Verify defending firstStrikes against air with other units on both sides") void defendingFirstStrikeVsAirWithOtherUnits() { final Unit unit2 = givenUnitIsAir(); - final Unit unit1 = givenUnitFirstStrikeAndEvadeAndCanNotBeTargetedBy(unit2.getType()); - final Unit unit3 = givenAnyUnit(); - final Unit unit4 = givenAnyUnit(); + final Unit unit1 = givenSeaUnitFirstStrikeAndEvadeAndCanNotBeTargetedBy(unit2.getType()); + final Unit unit3 = givenUnitIsSea(); + final Unit unit4 = givenUnitIsSea(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withTransportCasualtiesRestricted(false) + givenGameDataWithLenientProperties() .withWW2V2(true) .withAlliedAirIndependent(true) .build()) @@ -1574,26 +1472,22 @@ void defendingFirstStrikeVsAirWithOtherUnits() { @Test @DisplayName("Verify attacking firstStrike can submerge if SUBMERSIBLE_SUBS is true") void attackingFirstStrikeCanSubmergeIfSubmersibleSubs() { - final Unit unit1 = givenUnitFirstStrikeAndEvade(); - final Unit unit2 = givenAnyUnit(); + final Unit unit1 = givenSeaUnitFirstStrikeAndEvade(); + final Unit unit2 = givenUnitIsSea(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withSubRetreatBeforeBattle(false) - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) + givenGameDataWithLenientProperties() .withAlliedAirIndependent(true) - .withTransportCasualtiesRestricted(false) - .withWW2V2(false) .withSubmersibleSubs(true) .build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) .defendingUnits(List.of(unit2)) - .battleSite(battleSite) + .battleSite(givenSeaBattleSite()) .build()); assertThat( @@ -1609,19 +1503,15 @@ void attackingFirstStrikeCanSubmergeIfSubmersibleSubs() { @Test @DisplayName("Verify defending firstStrike can submerge if SUBMERSIBLE_SUBS is true") void defendingFirstStrikeCanSubmergeIfSubmersibleSubs() { - final Unit unit1 = givenAnyUnit(); - final Unit unit2 = givenUnitFirstStrikeAndEvade(); + final Unit unit1 = givenUnitIsSea(); + final Unit unit2 = givenSeaUnitFirstStrikeAndEvade(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withSubRetreatBeforeBattle(false) - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) + givenGameDataWithLenientProperties() .withAlliedAirIndependent(true) - .withTransportCasualtiesRestricted(false) - .withWW2V2(false) .withDefendingSubsSneakAttack(true) .withSubmersibleSubs(true) .build()) @@ -1629,7 +1519,7 @@ void defendingFirstStrikeCanSubmergeIfSubmersibleSubs() { .defender(defender) .attackingUnits(List.of(unit1)) .defendingUnits(List.of(unit2)) - .battleSite(battleSite) + .battleSite(givenSeaBattleSite()) .build()); assertThat( @@ -1647,25 +1537,21 @@ void defendingFirstStrikeCanSubmergeIfSubmersibleSubs() { "Verify defending firstStrike can submerge if SUBMERSIBLE_SUBS is true even with destroyers") void defendingFirstStrikeCanSubmergeIfSubmersibleSubsAndDestroyers() { final Unit unit1 = givenUnitDestroyer(); - final Unit unit2 = givenUnitFirstStrikeAndEvade(); + final Unit unit2 = givenSeaUnitFirstStrikeAndEvade(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withSubRetreatBeforeBattle(false) - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) + givenGameDataWithLenientProperties() .withAlliedAirIndependent(true) - .withTransportCasualtiesRestricted(false) - .withWW2V2(false) .withSubmersibleSubs(true) .build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) .defendingUnits(List.of(unit2)) - .battleSite(battleSite) + .battleSite(givenSeaBattleSite()) .build()); assertThat( @@ -1680,19 +1566,14 @@ void defendingFirstStrikeCanSubmergeIfSubmersibleSubsAndDestroyers() { @Test @DisplayName("Verify attacking firstStrike can withdraw when SUBMERSIBLE_SUBS is false") void attackingFirstStrikeWithdrawIfAble() { - final Unit unit1 = givenUnitFirstStrikeAndEvade(); + final Unit unit1 = givenSeaUnitFirstStrikeAndEvade(); final Unit unit2 = givenAnyUnit(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withWW2V2(false) - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) @@ -1719,19 +1600,14 @@ void attackingFirstStrikeWithdrawIfAble() { "Verify attacking firstStrike can't withdraw when " + "SUBMERSIBLE_SUBS is false and no retreat territories") void attackingFirstStrikeNoWithdrawIfEmptyTerritories() { - final Unit unit1 = givenUnitFirstStrikeAndEvade(); + final Unit unit1 = givenSeaUnitFirstStrikeAndEvade(); final Unit unit2 = givenAnyUnit(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withWW2V2(false) - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) @@ -1754,25 +1630,20 @@ void attackingFirstStrikeNoWithdrawIfEmptyTerritories() { "Verify attacking firstStrike can't withdraw when " + "SUBMERSIBLE_SUBS is false and destroyers present") void attackingFirstStrikeNoWithdrawIfDestroyers() { - final Unit unit1 = givenUnitFirstStrike(); + final Unit unit1 = givenSeaUnitFirstStrike(); final Unit unit2 = givenUnitDestroyer(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withWW2V2(false) - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) .defendingUnits(List.of(unit2)) .attackerRetreatTerritories(List.of(battleSite)) - .battleSite(battleSite) + .battleSite(givenSeaBattleSite()) .build()); assertThat( @@ -1789,21 +1660,15 @@ void attackingFirstStrikeNoWithdrawIfDestroyers() { "Verify attacking firstStrike can withdraw when " + "SUBMERSIBLE_SUBS is false and defenseless transports with non restricted casualties") void attackingFirstStrikeWithdrawIfNonRestrictedDefenselessTransports() { - final Unit unit1 = givenUnitFirstStrikeAndEvade(); - final UnitAndAttachment unitAndAttachment = newUnitAndAttachment(); + final Unit unit1 = givenSeaUnitFirstStrikeAndEvade(); + final UnitAndAttachment unitAndAttachment = newSeaUnitAndAttachment(); final Unit unit2 = unitAndAttachment.unit; final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withWW2V2(false) - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withTransportCasualtiesRestricted(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) @@ -1829,9 +1694,10 @@ void attackingFirstStrikeWithdrawIfNonRestrictedDefenselessTransports() { @Test @DisplayName("Verify defending firstStrike can withdraw when SUBMERSIBLE_SUBS is false") void defendingFirstStrikeWithdrawIfAble() { - final Unit unit1 = givenAnyUnit(); - final Unit unit2 = givenUnitFirstStrikeAndEvade(); + final Unit unit1 = givenUnitIsSea(); + final Unit unit2 = givenSeaUnitFirstStrikeAndEvade(); + final Territory battleTerritory = givenSeaBattleSite(); final Territory retreatTerritory = mock(Territory.class); when(retreatTerritory.isWater()).thenReturn(true); when(retreatTerritory.getUnitCollection()).thenReturn(mock(UnitCollection.class)); @@ -1840,19 +1706,15 @@ void defendingFirstStrikeWithdrawIfAble() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withTerritoryHasNeighbors(battleSite, Set.of(retreatTerritory)) - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withWW2V2(false) - .withDefendingSubsSneakAttack(false) - .withSubRetreatBeforeBattle(false) + givenGameDataWithLenientProperties() .withAlliedAirIndependent(true) + .withTerritoryHasNeighbors(battleTerritory, Set.of(retreatTerritory)) .build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) .defendingUnits(List.of(unit2)) - .battleSite(battleSite) + .battleSite(battleTerritory) .build()); assertThat( @@ -1876,13 +1738,7 @@ void defendingFirstStrikeNoWithdrawIfEmptyTerritories() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withWW2V2(false) - .withDefendingSubsSneakAttack(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) @@ -1905,23 +1761,18 @@ void defendingFirstStrikeNoWithdrawIfEmptyTerritories() { + "SUBMERSIBLE_SUBS is false and destroyers present") void defendingFirstStrikeNoWithdrawIfDestroyers() { final Unit unit1 = givenUnitDestroyer(); - final Unit unit2 = givenUnitFirstStrikeAndEvade(); + final Unit unit2 = givenSeaUnitFirstStrikeAndEvade(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withWW2V2(false) - .withSubRetreatBeforeBattle(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) .defendingUnits(List.of(unit2)) - .battleSite(battleSite) + .battleSite(givenSeaBattleSite()) .build()); assertThat( @@ -1937,18 +1788,13 @@ void defendingFirstStrikeNoWithdrawIfDestroyers() { @DisplayName("Verify attacking air units at sea can withdraw") void attackingAirUnitsAtSeaCanWithdraw() { final Unit unit1 = givenUnitIsAir(); - final Unit unit2 = givenAnyUnit(); + final Unit unit2 = givenUnitIsSea(); final List steps = givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withTransportCasualtiesRestricted(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1)) @@ -1974,9 +1820,7 @@ void partialAmphibiousAttackCanWithdrawIfHasNonAmphibious() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withSubRetreatBeforeBattle(false) - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) + givenGameDataWithLenientProperties() .withAlliedAirIndependent(true) .withPartialAmphibiousRetreat(true) .build()) @@ -2006,12 +1850,8 @@ void partialAmphibiousAttackCanNotWithdrawIfHasAllAmphibious() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withSubRetreatBeforeBattle(false) - .withWW2V2(false) + givenGameDataWithLenientProperties() .withAlliedAirIndependent(true) - .withAttackerRetreatPlanes(false) - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) .withPartialAmphibiousRetreat(true) .build()) .attacker(attacker) @@ -2037,11 +1877,7 @@ void partialAmphibiousAttackCanNotWithdrawIfNotAllowed() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) - .withAlliedAirIndependent(true) - .build()) + givenGameDataWithLenientProperties().withAlliedAirIndependent(true).build()) .attacker(attacker) .defender(defender) .attackingUnits(List.of(unit1, unit3)) @@ -2065,11 +1901,8 @@ void attackingPlanesCanWithdrawWW2v2AndAmphibious() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) + givenGameDataWithLenientProperties() .withAlliedAirIndependent(true) - .withPartialAmphibiousRetreat(false) .withWW2V2(true) .build()) .attacker(attacker) @@ -2099,11 +1932,7 @@ void attackingPlanesCanWithdrawPartialAmphibiousAndAmphibious() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withSubRetreatBeforeBattle(false) - .withWW2V2(false) - .withAttackerRetreatPlanes(false) - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) + givenGameDataWithLenientProperties() .withAlliedAirIndependent(true) .withPartialAmphibiousRetreat(true) .build()) @@ -2130,12 +1959,8 @@ void attackingPlanesCanWithdrawPlanesRetreatAndAmphibious() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withSubRetreatBeforeBattle(false) - .withWW2V2(false) - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) + givenGameDataWithLenientProperties() .withAlliedAirIndependent(true) - .withPartialAmphibiousRetreat(false) .withAttackerRetreatPlanes(true) .build()) .attacker(attacker) @@ -2161,9 +1986,7 @@ void attackingPlanesCanNotWithdrawWW2v2AndNotAmphibious() { givenBattleSteps( givenBattleStateBuilder() .gameData( - givenGameData() - .withDefendingSuicideAndMunitionUnitsDoNotFire(false) - .withSubRetreatBeforeBattle(false) + givenGameDataWithLenientProperties() .withAlliedAirIndependent(true) .withTransportCasualtiesRestricted(true) .build()) @@ -2177,4 +2000,42 @@ void attackingPlanesCanNotWithdrawWW2v2AndNotAmphibious() { assertThat(steps, is(basicFightStepStrings())); } + + @Test + @DisplayName("Verify that extra steps won't be created due to canNotTarget and non-participants") + void nonParticipantsDontCreateExtraStepsWithCannotTarget() { + // Two attacking units of different types. + final Unit unit1 = givenAnyUnit(); + lenient().when(unit1.getOwner()).thenReturn(attacker); + final Unit unit2 = givenAnyUnit(); + lenient().when(unit2.getOwner()).thenReturn(attacker); + final UnitType unit2Type = unit2.getType(); + + // One defending unit that can only target one of the attackers. + final Unit unit3 = givenAnyUnit(); + lenient().when(unit3.getOwner()).thenReturn(defender); + final UnitAttachment unit3Attachment = unit3.getUnitAttachment(); + when(unit3Attachment.getCanNotTarget()).thenReturn(Set.of(unit2Type)); + // And an infra unit on the defense that should not participate in combat. + final Unit unit4 = givenUnitIsInfrastructure(); + lenient().when(unit4.getOwner()).thenReturn(defender); + + final var unitTypeList = + List.of(unit1.getType(), unit2.getType(), unit3.getType(), unit4.getType()); + + final List steps = + givenBattleSteps( + givenBattleStateBuilder() + .gameData( + givenGameDataWithLenientProperties().withUnitTypeList(unitTypeList).build()) + .attacker(attacker) + .defender(defender) + .attackingUnits(List.of(unit1, unit2)) + .defendingUnits(List.of(unit3, unit4)) + .battleSite(battleSite) + .amphibious(false) + .build()); + + assertThat(steps, is(basicFightStepStrings())); + } } diff --git a/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/MockGameData.java b/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/MockGameData.java index 3ea573dcfb7..49408f75854 100644 --- a/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/MockGameData.java +++ b/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/MockGameData.java @@ -16,6 +16,8 @@ import static games.strategy.triplea.Constants.SUB_RETREAT_BEFORE_BATTLE; import static games.strategy.triplea.Constants.TRANSPORT_CASUALTIES_RESTRICTED; import static games.strategy.triplea.Constants.WW2V2; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -30,9 +32,11 @@ import games.strategy.engine.data.ResourceList; import games.strategy.engine.data.TechnologyFrontier; import games.strategy.engine.data.Territory; +import games.strategy.engine.data.UnitType; import games.strategy.engine.data.UnitTypeList; import games.strategy.engine.data.properties.GameProperties; import games.strategy.triplea.delegate.TechTracker; +import java.util.List; import java.util.Set; public class MockGameData { @@ -102,6 +106,11 @@ public MockGameData withTechnologyFrontier() { return this; } + public MockGameData withLenientProperties() { + lenient().when(gameProperties.get(anyString(), anyBoolean())).thenReturn(false); + return this; + } + public MockGameData withTransportCasualtiesRestricted(final boolean value) { when(gameProperties.get(TRANSPORT_CASUALTIES_RESTRICTED, false)).thenReturn(value); return this; @@ -189,4 +198,14 @@ public MockGameData withLowLuck(final boolean value) { when(gameProperties.get(LOW_LUCK, false)).thenReturn(value); return this; } + + public MockGameData withUnitTypeList(final List types) { + UnitTypeList unitTypeList = new UnitTypeList(gameData); + for (var unitType : types) { + lenient().when(unitType.getData()).thenReturn(gameData); + unitTypeList.addUnitType(unitType); + } + when(gameData.getUnitTypeList()).thenReturn(unitTypeList); + return this; + } } diff --git a/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/change/suicide/RemoveFirstStrikeSuicideTest.java b/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/change/suicide/RemoveFirstStrikeSuicideTest.java index ebd1babad82..6838342c67d 100644 --- a/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/change/suicide/RemoveFirstStrikeSuicideTest.java +++ b/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/change/suicide/RemoveFirstStrikeSuicideTest.java @@ -4,8 +4,8 @@ import static games.strategy.triplea.delegate.battle.BattleState.Side.OFFENSE; import static games.strategy.triplea.delegate.battle.FakeBattleState.givenBattleStateBuilder; import static games.strategy.triplea.delegate.battle.steps.BattleStepsTest.givenAnyUnit; -import static games.strategy.triplea.delegate.battle.steps.BattleStepsTest.givenUnitFirstStrikeSuicideOnAttack; -import static games.strategy.triplea.delegate.battle.steps.BattleStepsTest.givenUnitFirstStrikeSuicideOnDefense; +import static games.strategy.triplea.delegate.battle.steps.BattleStepsTest.givenSeaUnitFirstStrikeSuicideOnAttack; +import static games.strategy.triplea.delegate.battle.steps.BattleStepsTest.givenSeaUnitFirstStrikeSuicideOnDefense; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -34,8 +34,8 @@ public class RemoveFirstStrikeSuicideTest { void suicideUnitsRemoved() { when(delegateBridge.getDisplayChannelBroadcaster()).thenReturn(mock(IDisplay.class)); - final List attackers = List.of(givenAnyUnit(), givenUnitFirstStrikeSuicideOnAttack()); - final List defenders = List.of(givenAnyUnit(), givenUnitFirstStrikeSuicideOnDefense()); + final List attackers = List.of(givenAnyUnit(), givenSeaUnitFirstStrikeSuicideOnAttack()); + final List defenders = List.of(givenAnyUnit(), givenSeaUnitFirstStrikeSuicideOnDefense()); final MockGameData gameData = MockGameData.givenGameData(); final BattleState battleState = givenBattleStateBuilder() diff --git a/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/fire/air/AirAttackVsNonSubsStepTest.java b/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/fire/air/AirAttackVsNonSubsStepTest.java index d26ed24fa61..f355be3ca2b 100644 --- a/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/fire/air/AirAttackVsNonSubsStepTest.java +++ b/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/fire/air/AirAttackVsNonSubsStepTest.java @@ -2,7 +2,7 @@ import static games.strategy.triplea.delegate.battle.FakeBattleState.givenBattleStateBuilder; import static games.strategy.triplea.delegate.battle.steps.BattleStepsTest.givenAnyUnit; -import static games.strategy.triplea.delegate.battle.steps.BattleStepsTest.givenUnitCanNotBeTargetedBy; +import static games.strategy.triplea.delegate.battle.steps.BattleStepsTest.givenSeaUnitCanNotBeTargetedBy; import static games.strategy.triplea.delegate.battle.steps.BattleStepsTest.givenUnitDestroyer; import static games.strategy.triplea.delegate.battle.steps.BattleStepsTest.givenUnitIsAir; import static org.hamcrest.MatcherAssert.assertThat; @@ -35,7 +35,7 @@ static List stepName() { "Attacker has air units and no destroyers vs Defender subs", givenBattleStateBuilder() .attackingUnits(List.of(givenAnyUnit(), givenUnitIsAir())) - .defendingUnits(List.of(givenUnitCanNotBeTargetedBy(mock(UnitType.class)))) + .defendingUnits(List.of(givenSeaUnitCanNotBeTargetedBy(mock(UnitType.class)))) .build(), true), Arguments.of( diff --git a/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/fire/air/AirDefendVsNonSubsStepTest.java b/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/fire/air/AirDefendVsNonSubsStepTest.java index 5ec772a0b65..1b814672b6a 100644 --- a/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/fire/air/AirDefendVsNonSubsStepTest.java +++ b/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/fire/air/AirDefendVsNonSubsStepTest.java @@ -2,7 +2,7 @@ import static games.strategy.triplea.delegate.battle.FakeBattleState.givenBattleStateBuilder; import static games.strategy.triplea.delegate.battle.steps.BattleStepsTest.givenAnyUnit; -import static games.strategy.triplea.delegate.battle.steps.BattleStepsTest.givenUnitCanNotBeTargetedBy; +import static games.strategy.triplea.delegate.battle.steps.BattleStepsTest.givenSeaUnitCanNotBeTargetedBy; import static games.strategy.triplea.delegate.battle.steps.BattleStepsTest.givenUnitDestroyer; import static games.strategy.triplea.delegate.battle.steps.BattleStepsTest.givenUnitIsAir; import static org.hamcrest.MatcherAssert.assertThat; @@ -34,7 +34,7 @@ static List stepName() { Arguments.of( "Defender has air units and no destroyers vs Attacker subs", givenBattleStateBuilder() - .attackingUnits(List.of(givenUnitCanNotBeTargetedBy(mock(UnitType.class)))) + .attackingUnits(List.of(givenSeaUnitCanNotBeTargetedBy(mock(UnitType.class)))) .defendingUnits(List.of(givenAnyUnit(), givenUnitIsAir())) .build(), true), diff --git a/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/fire/general/FiringGroupSplitterGeneralTest.java b/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/fire/general/FiringGroupSplitterGeneralTest.java index 29b0d4721f6..f7b62d6c419 100644 --- a/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/fire/general/FiringGroupSplitterGeneralTest.java +++ b/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/fire/general/FiringGroupSplitterGeneralTest.java @@ -18,10 +18,9 @@ import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import games.strategy.engine.data.GameData; import games.strategy.engine.data.GamePlayer; import games.strategy.engine.data.Unit; import games.strategy.engine.data.UnitType; @@ -29,6 +28,7 @@ import games.strategy.triplea.delegate.battle.steps.fire.FiringGroup; import java.util.List; import java.util.Set; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -40,6 +40,16 @@ class FiringGroupSplitterGeneralTest { @Mock GamePlayer attacker; @Mock GamePlayer defender; + @BeforeEach + void setUp() { + final GameData gameData = new GameData(); + + lenient().when(attacker.getName()).thenReturn("attacker"); + lenient().when(attacker.getData()).thenReturn(gameData); + lenient().when(defender.getName()).thenReturn("defender"); + lenient().when(defender.getData()).thenReturn(gameData); + } + @Test void oneFiringUnitVsOneTargetableUnitMakesOneFiringGroup() { final Unit targetUnit = givenAnyUnit(); @@ -216,14 +226,6 @@ void doNotExcludeUnitsOfAlliesIfAlliedAirIndependentIsFalseButItIsDefense() { assertThat(firingGroups.get(0).getFiringUnits(), contains(fireUnit, fireUnit2)); assertThat(firingGroups.get(0).getTargetUnits(), contains(targetUnit)); assertThat(firingGroups.get(0).isSuicideOnHit(), is(false)); - - verify( - fireUnit, - never() - .description( - "Units on defense with AlliedAirIndependent == false" - + "should never call getOwner")) - .getOwner(); } @Test diff --git a/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/retreat/sub/SubmergeSubsVsOnlyAirStepTest.java b/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/retreat/sub/SubmergeSubsVsOnlyAirStepTest.java index 8f07107bfb1..2f86fe868dd 100644 --- a/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/retreat/sub/SubmergeSubsVsOnlyAirStepTest.java +++ b/game-app/game-core/src/test/java/games/strategy/triplea/delegate/battle/steps/retreat/sub/SubmergeSubsVsOnlyAirStepTest.java @@ -5,7 +5,7 @@ import static games.strategy.triplea.delegate.battle.BattleState.Side.OFFENSE; import static games.strategy.triplea.delegate.battle.FakeBattleState.givenBattleStateBuilder; import static games.strategy.triplea.delegate.battle.steps.BattleStepsTest.givenAnyUnit; -import static games.strategy.triplea.delegate.battle.steps.BattleStepsTest.givenUnitCanEvadeAndCanNotBeTargetedByRandomUnit; +import static games.strategy.triplea.delegate.battle.steps.BattleStepsTest.givenSeaUnitCanEvadeAndCanNotBeTargetedByRandomUnit; import static games.strategy.triplea.delegate.battle.steps.BattleStepsTest.givenUnitIsAir; import static games.strategy.triplea.delegate.battle.steps.MockGameData.givenGameData; import static org.hamcrest.MatcherAssert.assertThat; @@ -90,7 +90,7 @@ static List stepName() { Arguments.of( "Attacking evaders vs ALL air", givenBattleStateBuilder() - .attackingUnits(List.of(givenUnitCanEvadeAndCanNotBeTargetedByRandomUnit())) + .attackingUnits(List.of(givenSeaUnitCanEvadeAndCanNotBeTargetedByRandomUnit())) .defendingUnits(List.of(givenUnitIsAir(), givenUnitIsAir())) .build(), true), @@ -98,7 +98,7 @@ static List stepName() { "Defending evaders vs ALL air", givenBattleStateBuilder() .attackingUnits(List.of(givenUnitIsAir(), givenUnitIsAir())) - .defendingUnits(List.of(givenUnitCanEvadeAndCanNotBeTargetedByRandomUnit())) + .defendingUnits(List.of(givenSeaUnitCanEvadeAndCanNotBeTargetedByRandomUnit())) .build(), true)); }