From 6fa0c2bf0227eb5004c22932bdf56aa50c36a65c Mon Sep 17 00:00:00 2001 From: asvitkine Date: Fri, 14 Jul 2023 21:13:45 -0400 Subject: [PATCH] Fix issue a cause of "Could not find step name" (2.6 bug). (#11773) This was happening because the code to generate step names was not excluding units that would not participate in combat, resulting in infrastructure units getting their own steps (which later did not match what the engine generated once the filtering took place). Uses the same logic as what's done for the battle to exclude units. This change required adjusting a bunch of tests that were previously not careful about which had mistakes in setting up mock battles where the units didn't match the territory (in terms of sea vs. land). Also makes the tests to mock game data properties leniently, so that only the ones being set to true need to be specified (removing lots of LOC). Includes a unit test. Fixes: https://github.com/triplea-game/triplea/issues/10647 Note: Doesn't fix https://github.com/triplea-game/triplea/issues/11617, as the root cause of that error is different, despite the actual error being the same. I will fix that in a separate PR. --- .../delegate/battle/MustFightBattle.java | 1 + .../general/FiringGroupSplitterGeneral.java | 26 +- .../battle/steps/BattleStepsTest.java | 529 +++++++----------- .../delegate/battle/steps/MockGameData.java | 19 + .../suicide/RemoveFirstStrikeSuicideTest.java | 8 +- .../fire/air/AirAttackVsNonSubsStepTest.java | 4 +- .../fire/air/AirDefendVsNonSubsStepTest.java | 4 +- .../FiringGroupSplitterGeneralTest.java | 22 +- .../sub/SubmergeSubsVsOnlyAirStepTest.java | 6 +- 9 files changed, 256 insertions(+), 363 deletions(-) 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)); }