From d7fe28bba841cdef19d4f6fb7ce33f84623661c0 Mon Sep 17 00:00:00 2001 From: WCSumpton Date: Sun, 20 Aug 2023 21:08:53 -0400 Subject: [PATCH 01/10] Allow tuv to be set to 0. This change allows tuv to be set to 0 and be used for calculations. Also a check was add for the proper values. --- .../games/strategy/triplea/attachments/UnitAttachment.java | 7 ++++++- .../games/strategy/triplea/util/TuvCostsCalculator.java | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/attachments/UnitAttachment.java b/game-app/game-core/src/main/java/games/strategy/triplea/attachments/UnitAttachment.java index d091731cf2..e4e4d42187 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/attachments/UnitAttachment.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/attachments/UnitAttachment.java @@ -2516,7 +2516,12 @@ private Tuple parseStackingLimit(final String type, final Strin return Tuple.of(max, s[1].intern()); } - private void setTuv(final String s) { + private void setTuv(final String s) throws GameParseException { + final int value = getInt(s); + if (value < -1) { + throw new GameParseException( + "tuv must be 0 positive (or -1, default, to calculate) " + thisErrorMsg()); + } tuv = getInt(s); } diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/util/TuvCostsCalculator.java b/game-app/game-core/src/main/java/games/strategy/triplea/util/TuvCostsCalculator.java index cbaab96685..dd5c4468d0 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/util/TuvCostsCalculator.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/util/TuvCostsCalculator.java @@ -120,7 +120,7 @@ private static IntegerMap getCostsForTuvForAllPlayersMergedAndAveraged // Add any units that have XML TUV even if they aren't purchasable for (final UnitType unitType : data.getUnitTypeList()) { final UnitAttachment ua = unitType.getUnitAttachment(); - if (ua.getTuv() > 0) { + if (ua.getTuv() > -1) { costs.put(unitType, ua.getTuv()); } } @@ -131,7 +131,7 @@ private static IntegerMap getCostsForTuvForAllPlayersMergedAndAveraged private static int getTotalTuv( final UnitType unitType, final IntegerMap costs, final Set alreadyAdded) { final UnitAttachment ua = unitType.getUnitAttachment(); - if (ua.getTuv() > 0) { + if (ua.getTuv() > -1) { return ua.getTuv(); } int tuv = costs.getInt(unitType); From 7447d576eea509fe4acec194c2136a66feb11bef Mon Sep 17 00:00:00 2001 From: WCSumpton Date: Mon, 21 Aug 2023 02:09:40 +0000 Subject: [PATCH 02/10] Made indention change --- .../java/games/strategy/triplea/attachments/UnitAttachment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/attachments/UnitAttachment.java b/game-app/game-core/src/main/java/games/strategy/triplea/attachments/UnitAttachment.java index e4e4d42187..1d95388c04 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/attachments/UnitAttachment.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/attachments/UnitAttachment.java @@ -2520,7 +2520,7 @@ private void setTuv(final String s) throws GameParseException { final int value = getInt(s); if (value < -1) { throw new GameParseException( - "tuv must be 0 positive (or -1, default, to calculate) " + thisErrorMsg()); + "tuv must be 0 positive (or -1, default, to calculate) " + thisErrorMsg()); } tuv = getInt(s); } From 763b7a8dafcbd7fd89012ceeaf0e6108ee60b531 Mon Sep 17 00:00:00 2001 From: WCSumpton Date: Mon, 18 Sep 2023 09:28:07 -0400 Subject: [PATCH 03/10] haveResources w/testing and checkAIPlayer Adding haveResources and checkAIPlayer to RulesAttachment. Included is RulesAttachmentTest which uses "VICTORY_TEST" to test haveResources. checkAIPlayer is not tested because of internal testing for boolean values. Cheers... --- .../triplea/attachments/RulesAttachment.java | 111 ++++++++++++ .../attachments/RulesAttachmentTest.java | 163 ++++++++++++++++++ 2 files changed, 274 insertions(+) create mode 100644 game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/attachments/RulesAttachment.java b/game-app/game-core/src/main/java/games/strategy/triplea/attachments/RulesAttachment.java index 1908d9db9c..88acaf29b6 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/attachments/RulesAttachment.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/attachments/RulesAttachment.java @@ -3,6 +3,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import com.google.common.annotations.VisibleForTesting; import games.strategy.engine.data.Attachable; import games.strategy.engine.data.BattleRecordsList; import games.strategy.engine.data.GameData; @@ -13,6 +14,7 @@ import games.strategy.engine.data.MutableProperty; import games.strategy.engine.data.RelationshipTracker.Relationship; import games.strategy.engine.data.RelationshipType; +import games.strategy.engine.data.Resource; import games.strategy.engine.data.TechnologyFrontier; import games.strategy.engine.data.Territory; import games.strategy.engine.data.Unit; @@ -55,9 +57,13 @@ public class RulesAttachment extends AbstractPlayerRulesAttachment { private int techCount = -1; // condition for having specific relationships private @Nullable List relationship = null; + // condition for checking ai player + private boolean checkAIPlayer = false; // condition for being at war private @Nullable Set atWarPlayers = null; private int atWarCount = -1; + // condition for checking resources + private @Nullable String[] haveResources = null; // condition for having destroyed at least X enemy non-neutral TUV (total unit value) [according // to // the prices the defender pays for the units] @@ -145,6 +151,50 @@ public static Set getNationalObjectives(final GamePlayer player return natObjs; } + @VisibleForTesting + public void setHaveResources(final String value) throws GameParseException { + final String[] s = splitOnColon(value); + if (s.length <= 1) { + throw new GameParseException( + "haveResources must have at least 2 fields. Format value=resource1 count=number, or " + + "value=resource1:resource2:resource3 count=number" + + thisErrorMsg()); + } + if ((s.length <= 2) && (s[1].equalsIgnoreCase("sum") || s[1].equalsIgnoreCase("add"))) { + throw new GameParseException( + "haveResources must have at least 3 fields when used with 'Sum' or 'Add'. Format value=Sum:resource1 " + + "count=number, or value=Sum:resource1:resource2:resource3 count=number" + + thisErrorMsg()); + } + final int n = getInt(s[0]); + if (n < 1) { + throw new GameParseException("haveResources must be a positive integer" + thisErrorMsg()); + } + for (int i = 1;i < s.length; i++) { + if (s[i].equalsIgnoreCase("sum") || s[i].equalsIgnoreCase("add")) { + i++; + } + // validate that this resource exists in the xml + final Resource r = getData().getResourceList().getResource(s[i]); + if (r == null) { + throw new GameParseException("No resource called: " + s[i] + thisErrorMsg()); + } + } + haveResources = s; + } + + private void setHaveResources(final String[] value) { + haveResources = value; + } + + public String[] getHaveResources() { + return haveResources; + } + + private void resetHaveResources() { + haveResources = null; + } + private void setDestroyedTuv(final String value) throws GameParseException { final String[] s = splitOnColon(value); if (s.length != 2) { @@ -489,6 +539,22 @@ private void resetUnitPresence() { unitPresence = null; } + private void setCheckAIPlayer(final String s) { + checkAIPlayer = getBool(s); + } + + private void setCheckAIPlayer(final Boolean s) { + checkAIPlayer = s; + } + + public boolean getCheckAIPlayer() { + return checkAIPlayer; + } + + private void resetCheckAIPlayer() { + checkAIPlayer = false; + } + private int getAtWarCount() { return atWarCount; } @@ -735,11 +801,21 @@ public boolean isSatisfied( } objectiveMet = checkDirectOwnership(listedTerritories, players); } + // check for ai controlled player + if (objectiveMet && getCheckAIPlayer()) { + objectiveMet = checkCheckAIPlayer(players); + } + // check for resources + if (objectiveMet && haveResources != null) { + objectiveMet = checkHaveResources(players); + } // get attached to player final GamePlayer playerAttachedTo = (GamePlayer) getAttachedTo(); + // check for players at war if (objectiveMet && !getAtWarPlayers().isEmpty()) { objectiveMet = checkAtWar(playerAttachedTo, getAtWarPlayers(), getAtWarCount()); } + // check for techs if (objectiveMet && !getTechs().isEmpty()) { objectiveMet = checkTechs(playerAttachedTo, data.getTechnologyFrontier()); } @@ -1001,6 +1077,14 @@ private boolean matchTerritories( return numberMet >= getTerritoryCount(); } + private boolean checkCheckAIPlayer(final List players) { + boolean bcheck = true; + for (GamePlayer player : players) { + bcheck = (bcheck && player.isAi()); + } + return bcheck; + } + private boolean checkAtWar( final GamePlayer player, final Set enemies, final int count) { int found = CollectionUtils.countMatches(enemies, player::isAtWar); @@ -1031,6 +1115,21 @@ private boolean checkTechs(final GamePlayer player, final TechnologyFrontier tec return found >= techCount; } + @VisibleForTesting + public boolean checkHaveResources(final List players) { + final boolean toSum = + haveResources[1].equalsIgnoreCase("sum") || haveResources[1].equalsIgnoreCase("add"); + int itotal = 0; + for (GamePlayer player : players) { + for (int i = toSum ? 2 : 1; i < haveResources.length; i++) { + final Resource resource = getData().getResourceList().getResource(haveResources[i]); + int iamount = player.getResources().getQuantity(resource); + itotal = toSum ? itotal + iamount : Math.max(itotal, iamount); + } + } + return itotal >= getInt(haveResources[0]); + } + @Override public void validate(final GameState data) { validateNames(alliedOwnershipTerritories); @@ -1057,6 +1156,12 @@ public MutableProperty getPropertyOrNull(String propertyName) { this::setRelationship, this::getRelationship, this::resetRelationship); + case "checkAIPlayer": + return MutableProperty.of( + this::setCheckAIPlayer, + this::setCheckAIPlayer, + this::getCheckAIPlayer, + this::resetCheckAIPlayer); case "atWarPlayers": return MutableProperty.of( this::setAtWarPlayers, @@ -1065,6 +1170,12 @@ public MutableProperty getPropertyOrNull(String propertyName) { this::resetAtWarPlayers); case "atWarCount": return MutableProperty.ofReadOnly(this::getAtWarCount); + case "haveResources": + return MutableProperty.of( + this::setHaveResources, + this::setHaveResources, + this::getHaveResources, + this::resetHaveResources); case "destroyedTUV": return MutableProperty.ofString( this::setDestroyedTuv, this::getDestroyedTuv, this::resetDestroyedTuv); diff --git a/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java b/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java new file mode 100644 index 0000000000..63d16d7235 --- /dev/null +++ b/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java @@ -0,0 +1,163 @@ +package games.strategy.triplea.attachments; + +import static games.strategy.triplea.Constants.PUS; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import games.strategy.engine.data.GameData; +import games.strategy.engine.data.GamePlayer; +import games.strategy.engine.data.gameparser.GameParseException; +import java.security.SecureRandom; +import java.util.List; + +import games.strategy.triplea.delegate.GameDataTestUtil; +import games.strategy.triplea.xml.TestMapGameData; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class RulesAttachmentTest { + + /** + * "Victory" map is just a branch/mod of Pact of Steel 2. POS2 is an actual game with good gameplay + * that we don't want to mess with, so "Victory" is more of an xml purely for testing purposes, and + * probably should never be played. + */ + private final GameData gameData = TestMapGameData.VICTORY_TEST.getGameData(); + private final RulesAttachment attachment = new RulesAttachment("Test attachment", null, gameData); + + @Nested + class HaveResources { + + private final GamePlayer italians = GameDataTestUtil.italians(gameData); + private final GamePlayer germans = GameDataTestUtil.germans(gameData); + private final String FUEL = "Fuel"; + private final String ORE = "Ore"; + private final String addString = "add"; + private final String sumString ="SUM"; + + /* Length test for haveResources */ + @Test + void setHaveResourcesInvalidLength() { + assertThrows(GameParseException.class, () -> attachment.setHaveResources("")); + assertThrows(GameParseException.class, () -> attachment.setHaveResources(":add")); + + assertThrows(GameParseException.class, () -> attachment.setHaveResources("a")); + assertThrows(GameParseException.class, () -> attachment.setHaveResources("a:add")); + } + + /* Invalid arguments for haveResources */ + @Test + void setHaveResourcesInvalidArgs() { + /* Not a number (NAN) test */ + assertThrows( + IllegalArgumentException.class, + () -> attachment.setHaveResources("NAN:PUs")); + assertThrows( + IllegalArgumentException.class, + () -> attachment.setHaveResources("NAN:add:PUs")); + /* -1 value test */ + assertThrows( + GameParseException.class, + () -> attachment.setHaveResources("0:PUs")); + assertThrows( + GameParseException.class, + () -> attachment.setHaveResources("0:add:PUs")); + /* Not a resource test */ + assertThrows( + GameParseException.class, + () -> attachment.setHaveResources("1:NOT A RESOURCE")); + assertThrows( + GameParseException.class, + () -> attachment.setHaveResources("1:Sum:NOT A RESOURCE")); + assertThrows( + GameParseException.class, () -> attachment.setHaveResources("0:w")); + assertThrows( + GameParseException.class, () -> attachment.setHaveResources("0:w:e")); + assertThrows( + GameParseException.class, () -> attachment.setHaveResources("0:add:w")); + assertThrows( + GameParseException.class, () -> attachment.setHaveResources("0:add:w:e")); + } + + /* Testing stored values with getHaveResources */ + @Test + void setHaveResourcesTest() throws Exception { + final SecureRandom rand = new SecureRandom(); + final String random1 = Integer.toString(Math.abs(rand.nextInt())); + final String[] expected1 = new String[] {random1, PUS}; + + attachment.setHaveResources( + concatWithColon(random1, addString, PUS)); + assertEquals( + expected1[0], + attachment.getHaveResources()[0]); + assertEquals( + expected1[1], + attachment.getHaveResources()[2]); + } + + /* Testing checkHaveResources */ + @Test + void testCheckHaveResources() throws Exception { + final int italianFuelAmount = italians.getResources().getQuantity(FUEL); + final int italianPuAmount = italians.getResources().getQuantity(PUS); + final int italianOreAmount = italians.getResources().getQuantity(ORE); + final int germanFuelAmount = germans.getResources().getQuantity(FUEL); + final int germanPuAmount = germans.getResources().getQuantity(PUS); + final int germanOreAmount = germans.getResources().getQuantity(ORE); + + final int testItalianPU = italianPuAmount; + final int testItalianResources = italianOreAmount + italianFuelAmount + italianPuAmount; + final int testPUs = testItalianPU + germanPuAmount; + final int testResources = testItalianResources + germanPuAmount + germanFuelAmount + germanOreAmount; + + /* testing with 1 player */ + final List players = List.of(italians); + attachment.setHaveResources( + concatWithColon(String.valueOf(testItalianPU), PUS)); + assertTrue( + attachment.checkHaveResources(players)); + attachment.setHaveResources( + concatWithColon(String.valueOf(testItalianResources), addString, PUS)); + assertFalse( + attachment.checkHaveResources(players)); + attachment.setHaveResources( + concatWithColon(String.valueOf(testItalianResources), addString, PUS, FUEL)); + assertFalse( + attachment.checkHaveResources(players)); + attachment.setHaveResources( + concatWithColon(String.valueOf(testItalianResources), addString, PUS, FUEL, ORE)); + assertTrue( + attachment.checkHaveResources(players)); + + /* testing with 2 players */ + final List players1 = List.of(italians, germans); + attachment.setHaveResources( + concatWithColon(String.valueOf(testPUs), sumString, PUS)); + assertTrue( + attachment.checkHaveResources(players1)); + attachment.setHaveResources( + concatWithColon(String.valueOf(testResources), sumString, PUS)); + assertFalse( + attachment.checkHaveResources(players1)); + attachment.setHaveResources( + concatWithColon(String.valueOf(testResources), sumString, PUS, FUEL)); + assertFalse( + attachment.checkHaveResources(players1)); + attachment.setHaveResources( + concatWithColon(String.valueOf(testResources), sumString, PUS, FUEL, ORE)); + assertTrue( + attachment.checkHaveResources(players1)); + + } + @Test + private String concatWithColon(final String... args) { + return String.join(":", args); + } + } +} From 20a5799476d7279320a2bdb12adc684ce842c896 Mon Sep 17 00:00:00 2001 From: WCSumpton Date: Mon, 18 Sep 2023 10:25:49 -0400 Subject: [PATCH 04/10] Updating to pass test Indentation on comments fixed. Values "FUEL" and "ORE" changed to "fuel" and "ore" --- .../attachments/RulesAttachmentTest.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java b/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java index 63d16d7235..8966acbb22 100644 --- a/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java +++ b/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java @@ -22,11 +22,11 @@ @ExtendWith(MockitoExtension.class) class RulesAttachmentTest { - /** - * "Victory" map is just a branch/mod of Pact of Steel 2. POS2 is an actual game with good gameplay - * that we don't want to mess with, so "Victory" is more of an xml purely for testing purposes, and - * probably should never be played. - */ + /** + * "Victory" map is just a branch/mod of Pact of Steel 2. POS2 is an actual game with good gameplay + * that we don't want to mess with, so "Victory" is more of an xml purely for testing purposes, and + * probably should never be played. + */ private final GameData gameData = TestMapGameData.VICTORY_TEST.getGameData(); private final RulesAttachment attachment = new RulesAttachment("Test attachment", null, gameData); @@ -35,8 +35,8 @@ class HaveResources { private final GamePlayer italians = GameDataTestUtil.italians(gameData); private final GamePlayer germans = GameDataTestUtil.germans(gameData); - private final String FUEL = "Fuel"; - private final String ORE = "Ore"; + private final String fuel = "Fuel"; + private final String ore = "Ore"; private final String addString = "add"; private final String sumString ="SUM"; @@ -53,7 +53,7 @@ void setHaveResourcesInvalidLength() { /* Invalid arguments for haveResources */ @Test void setHaveResourcesInvalidArgs() { - /* Not a number (NAN) test */ + /* Not a number (NAN) test */ assertThrows( IllegalArgumentException.class, () -> attachment.setHaveResources("NAN:PUs")); @@ -104,12 +104,12 @@ void setHaveResourcesTest() throws Exception { /* Testing checkHaveResources */ @Test void testCheckHaveResources() throws Exception { - final int italianFuelAmount = italians.getResources().getQuantity(FUEL); + final int italianFuelAmount = italians.getResources().getQuantity(fuel); final int italianPuAmount = italians.getResources().getQuantity(PUS); - final int italianOreAmount = italians.getResources().getQuantity(ORE); - final int germanFuelAmount = germans.getResources().getQuantity(FUEL); + final int italianOreAmount = italians.getResources().getQuantity(ore); + final int germanFuelAmount = germans.getResources().getQuantity(fuel); final int germanPuAmount = germans.getResources().getQuantity(PUS); - final int germanOreAmount = germans.getResources().getQuantity(ORE); + final int germanOreAmount = germans.getResources().getQuantity(ore); final int testItalianPU = italianPuAmount; final int testItalianResources = italianOreAmount + italianFuelAmount + italianPuAmount; @@ -127,11 +127,11 @@ void testCheckHaveResources() throws Exception { assertFalse( attachment.checkHaveResources(players)); attachment.setHaveResources( - concatWithColon(String.valueOf(testItalianResources), addString, PUS, FUEL)); + concatWithColon(String.valueOf(testItalianResources), addString, PUS, fuel)); assertFalse( attachment.checkHaveResources(players)); attachment.setHaveResources( - concatWithColon(String.valueOf(testItalianResources), addString, PUS, FUEL, ORE)); + concatWithColon(String.valueOf(testItalianResources), addString, PUS, fuel, ore)); assertTrue( attachment.checkHaveResources(players)); @@ -146,11 +146,11 @@ void testCheckHaveResources() throws Exception { assertFalse( attachment.checkHaveResources(players1)); attachment.setHaveResources( - concatWithColon(String.valueOf(testResources), sumString, PUS, FUEL)); + concatWithColon(String.valueOf(testResources), sumString, PUS, fuel)); assertFalse( attachment.checkHaveResources(players1)); attachment.setHaveResources( - concatWithColon(String.valueOf(testResources), sumString, PUS, FUEL, ORE)); + concatWithColon(String.valueOf(testResources), sumString, PUS, fuel, ore)); assertTrue( attachment.checkHaveResources(players1)); From c0b6b0b0846711f92d04686442ee25d6aff06f2b Mon Sep 17 00:00:00 2001 From: WCSumpton Date: Mon, 18 Sep 2023 10:46:49 -0400 Subject: [PATCH 05/10] Fixed spacing and import order Fixed spacing and import order --- .../games/strategy/triplea/attachments/RulesAttachment.java | 2 +- .../strategy/triplea/attachments/RulesAttachmentTest.java | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/attachments/RulesAttachment.java b/game-app/game-core/src/main/java/games/strategy/triplea/attachments/RulesAttachment.java index 88acaf29b6..320dbb133a 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/attachments/RulesAttachment.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/attachments/RulesAttachment.java @@ -170,7 +170,7 @@ public void setHaveResources(final String value) throws GameParseException { if (n < 1) { throw new GameParseException("haveResources must be a positive integer" + thisErrorMsg()); } - for (int i = 1;i < s.length; i++) { + for (int i = 1; i < s.length; i++) { if (s[i].equalsIgnoreCase("sum") || s[i].equalsIgnoreCase("add")) { i++; } diff --git a/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java b/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java index 8966acbb22..8ff601b791 100644 --- a/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java +++ b/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java @@ -9,11 +9,10 @@ import games.strategy.engine.data.GameData; import games.strategy.engine.data.GamePlayer; import games.strategy.engine.data.gameparser.GameParseException; -import java.security.SecureRandom; -import java.util.List; - import games.strategy.triplea.delegate.GameDataTestUtil; import games.strategy.triplea.xml.TestMapGameData; +import java.security.SecureRandom; +import java.util.List; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; From 4e0dcd254a3551966a0fe16bc98d32b918131cb8 Mon Sep 17 00:00:00 2001 From: WCSumpton Date: Mon, 18 Sep 2023 11:07:24 -0400 Subject: [PATCH 06/10] More formatting fixes Formatting fixes and an additional check added for testCheckHaveResources --- .../attachments/RulesAttachmentTest.java | 99 ++++++------------- 1 file changed, 32 insertions(+), 67 deletions(-) diff --git a/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java b/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java index 8ff601b791..d1f76c85be 100644 --- a/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java +++ b/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java @@ -37,7 +37,7 @@ class HaveResources { private final String fuel = "Fuel"; private final String ore = "Ore"; private final String addString = "add"; - private final String sumString ="SUM"; + private final String sumString = "SUM"; /* Length test for haveResources */ @Test @@ -53,34 +53,18 @@ void setHaveResourcesInvalidLength() { @Test void setHaveResourcesInvalidArgs() { /* Not a number (NAN) test */ - assertThrows( - IllegalArgumentException.class, - () -> attachment.setHaveResources("NAN:PUs")); - assertThrows( - IllegalArgumentException.class, - () -> attachment.setHaveResources("NAN:add:PUs")); + assertThrows(IllegalArgumentException.class, () -> attachment.setHaveResources("NAN:PUs")); + assertThrows(IllegalArgumentException.class, () -> attachment.setHaveResources("NAN:add:PUs")); /* -1 value test */ - assertThrows( - GameParseException.class, - () -> attachment.setHaveResources("0:PUs")); - assertThrows( - GameParseException.class, - () -> attachment.setHaveResources("0:add:PUs")); + assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:PUs")); + assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:add:PUs")); /* Not a resource test */ - assertThrows( - GameParseException.class, - () -> attachment.setHaveResources("1:NOT A RESOURCE")); - assertThrows( - GameParseException.class, - () -> attachment.setHaveResources("1:Sum:NOT A RESOURCE")); - assertThrows( - GameParseException.class, () -> attachment.setHaveResources("0:w")); - assertThrows( - GameParseException.class, () -> attachment.setHaveResources("0:w:e")); - assertThrows( - GameParseException.class, () -> attachment.setHaveResources("0:add:w")); - assertThrows( - GameParseException.class, () -> attachment.setHaveResources("0:add:w:e")); + assertThrows(GameParseException.class, () -> attachment.setHaveResources("1:NOT A RESOURCE")); + assertThrows(GameParseException.class, () -> attachment.setHaveResources("1:Sum:NOT A RESOURCE")); + assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:w")); + assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:w:e")); + assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:add:w")); + assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:add:w:e")); } /* Testing stored values with getHaveResources */ @@ -90,14 +74,9 @@ void setHaveResourcesTest() throws Exception { final String random1 = Integer.toString(Math.abs(rand.nextInt())); final String[] expected1 = new String[] {random1, PUS}; - attachment.setHaveResources( - concatWithColon(random1, addString, PUS)); - assertEquals( - expected1[0], - attachment.getHaveResources()[0]); - assertEquals( - expected1[1], - attachment.getHaveResources()[2]); + attachment.setHaveResources(concatWithColon(random1, addString, PUS)); + assertEquals(expected1[0], attachment.getHaveResources()[0]); + assertEquals(expected1[1], attachment.getHaveResources()[2]); } /* Testing checkHaveResources */ @@ -117,41 +96,27 @@ void testCheckHaveResources() throws Exception { /* testing with 1 player */ final List players = List.of(italians); - attachment.setHaveResources( - concatWithColon(String.valueOf(testItalianPU), PUS)); - assertTrue( - attachment.checkHaveResources(players)); - attachment.setHaveResources( - concatWithColon(String.valueOf(testItalianResources), addString, PUS)); - assertFalse( - attachment.checkHaveResources(players)); - attachment.setHaveResources( - concatWithColon(String.valueOf(testItalianResources), addString, PUS, fuel)); - assertFalse( - attachment.checkHaveResources(players)); - attachment.setHaveResources( - concatWithColon(String.valueOf(testItalianResources), addString, PUS, fuel, ore)); - assertTrue( - attachment.checkHaveResources(players)); + attachment.setHaveResources(concatWithColon(String.valueOf(testItalianPU), PUS)); + assertTrue(attachment.checkHaveResources(players)); + attachment.setHaveResources(concatWithColon(String.valueOf(testItalianResources), addString, PUS)); + assertFalse(attachment.checkHaveResources(players)); + attachment.setHaveResources(concatWithColon(String.valueOf(testItalianResources), addString, PUS, fuel)); + assertFalse(attachment.checkHaveResources(players)); + attachment.setHaveResources(concatWithColon(String.valueOf(testItalianResources), addString, PUS, fuel, ore)); + assertTrue(attachment.checkHaveResources(players)); /* testing with 2 players */ final List players1 = List.of(italians, germans); - attachment.setHaveResources( - concatWithColon(String.valueOf(testPUs), sumString, PUS)); - assertTrue( - attachment.checkHaveResources(players1)); - attachment.setHaveResources( - concatWithColon(String.valueOf(testResources), sumString, PUS)); - assertFalse( - attachment.checkHaveResources(players1)); - attachment.setHaveResources( - concatWithColon(String.valueOf(testResources), sumString, PUS, fuel)); - assertFalse( - attachment.checkHaveResources(players1)); - attachment.setHaveResources( - concatWithColon(String.valueOf(testResources), sumString, PUS, fuel, ore)); - assertTrue( - attachment.checkHaveResources(players1)); + attachment.setHaveResources(concatWithColon(String.valueOf(testPUs), PUS)); + assertFalse(attachment.checkHaveResources(players1)); + attachment.setHaveResources(concatWithColon(String.valueOf(testPUs), sumString, PUS)); + assertTrue(attachment.checkHaveResources(players1)); + attachment.setHaveResources(concatWithColon(String.valueOf(testResources), sumString, PUS)); + assertFalse(attachment.checkHaveResources(players1)); + attachment.setHaveResources(concatWithColon(String.valueOf(testResources), sumString, PUS, fuel)); + assertFalse(attachment.checkHaveResources(players1)); + attachment.setHaveResources(concatWithColon(String.valueOf(testResources), sumString, PUS, fuel, ore)); + assertTrue(attachment.checkHaveResources(players1)); } @Test From 49e99768305a9cb9234bc1d748df60340d5cd49e Mon Sep 17 00:00:00 2001 From: WCSumpton Date: Mon, 18 Sep 2023 11:21:39 -0400 Subject: [PATCH 07/10] Still more formatting fixes Still more formatting fixes --- .../attachments/RulesAttachmentTest.java | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java b/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java index d1f76c85be..cae48071d0 100644 --- a/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java +++ b/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java @@ -22,11 +22,12 @@ class RulesAttachmentTest { /** - * "Victory" map is just a branch/mod of Pact of Steel 2. POS2 is an actual game with good gameplay - * that we don't want to mess with, so "Victory" is more of an xml purely for testing purposes, and - * probably should never be played. + * "Victory" map is just a branch/mod of Pact of Steel 2. POS2 is an actual game with good + * gameplay that we don't want to mess with, so "Victory" is more of an xml purely for testing + * purposes, and probably should never be played. */ private final GameData gameData = TestMapGameData.VICTORY_TEST.getGameData(); + private final RulesAttachment attachment = new RulesAttachment("Test attachment", null, gameData); @Nested @@ -54,13 +55,15 @@ void setHaveResourcesInvalidLength() { void setHaveResourcesInvalidArgs() { /* Not a number (NAN) test */ assertThrows(IllegalArgumentException.class, () -> attachment.setHaveResources("NAN:PUs")); - assertThrows(IllegalArgumentException.class, () -> attachment.setHaveResources("NAN:add:PUs")); + assertThrows( + IllegalArgumentException.class, () -> attachment.setHaveResources("NAN:add:PUs")); /* -1 value test */ assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:PUs")); assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:add:PUs")); /* Not a resource test */ assertThrows(GameParseException.class, () -> attachment.setHaveResources("1:NOT A RESOURCE")); - assertThrows(GameParseException.class, () -> attachment.setHaveResources("1:Sum:NOT A RESOURCE")); + assertThrows( + GameParseException.class, () -> attachment.setHaveResources("1:Sum:NOT A RESOURCE")); assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:w")); assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:w:e")); assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:add:w")); @@ -88,7 +91,6 @@ void testCheckHaveResources() throws Exception { final int germanFuelAmount = germans.getResources().getQuantity(fuel); final int germanPuAmount = germans.getResources().getQuantity(PUS); final int germanOreAmount = germans.getResources().getQuantity(ore); - final int testItalianPU = italianPuAmount; final int testItalianResources = italianOreAmount + italianFuelAmount + italianPuAmount; final int testPUs = testItalianPU + germanPuAmount; @@ -98,11 +100,14 @@ void testCheckHaveResources() throws Exception { final List players = List.of(italians); attachment.setHaveResources(concatWithColon(String.valueOf(testItalianPU), PUS)); assertTrue(attachment.checkHaveResources(players)); - attachment.setHaveResources(concatWithColon(String.valueOf(testItalianResources), addString, PUS)); + attachment.setHaveResources( + concatWithColon(String.valueOf(testItalianResources), addString, PUS)); assertFalse(attachment.checkHaveResources(players)); - attachment.setHaveResources(concatWithColon(String.valueOf(testItalianResources), addString, PUS, fuel)); + attachment.setHaveResources( + concatWithColon(String.valueOf(testItalianResources), addString, PUS, fuel)); assertFalse(attachment.checkHaveResources(players)); - attachment.setHaveResources(concatWithColon(String.valueOf(testItalianResources), addString, PUS, fuel, ore)); + attachment.setHaveResources( + concatWithColon(String.valueOf(testItalianResources), addString, PUS, fuel, ore)); assertTrue(attachment.checkHaveResources(players)); /* testing with 2 players */ @@ -111,11 +116,14 @@ void testCheckHaveResources() throws Exception { assertFalse(attachment.checkHaveResources(players1)); attachment.setHaveResources(concatWithColon(String.valueOf(testPUs), sumString, PUS)); assertTrue(attachment.checkHaveResources(players1)); - attachment.setHaveResources(concatWithColon(String.valueOf(testResources), sumString, PUS)); + attachment.setHaveResources( + concatWithColon(String.valueOf(testResources), sumString, PUS)); assertFalse(attachment.checkHaveResources(players1)); - attachment.setHaveResources(concatWithColon(String.valueOf(testResources), sumString, PUS, fuel)); + attachment.setHaveResources( + concatWithColon(String.valueOf(testResources), sumString, PUS, fuel)); assertFalse(attachment.checkHaveResources(players1)); - attachment.setHaveResources(concatWithColon(String.valueOf(testResources), sumString, PUS, fuel, ore)); + attachment.setHaveResources( + concatWithColon(String.valueOf(testResources), sumString, PUS, fuel, ore)); assertTrue(attachment.checkHaveResources(players1)); } From 08acd06ba124eb583f4c51ff06435a5b85a26705 Mon Sep 17 00:00:00 2001 From: WCSumpton Date: Mon, 18 Sep 2023 11:33:04 -0400 Subject: [PATCH 08/10] Still working on formatting issues Still working on formatting issues --- .../attachments/RulesAttachmentTest.java | 90 +++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java b/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java index cae48071d0..3df158f031 100644 --- a/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java +++ b/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java @@ -21,14 +21,14 @@ @ExtendWith(MockitoExtension.class) class RulesAttachmentTest { - /** + /** * "Victory" map is just a branch/mod of Pact of Steel 2. POS2 is an actual game with good * gameplay that we don't want to mess with, so "Victory" is more of an xml purely for testing * purposes, and probably should never be played. */ - private final GameData gameData = TestMapGameData.VICTORY_TEST.getGameData(); + private final GameData gameData = TestMapGameData.VICTORY_TEST.getGameData(); - private final RulesAttachment attachment = new RulesAttachment("Test attachment", null, gameData); + private final RulesAttachment attachment = new RulesAttachment("Test attachment", null, gameData); @Nested class HaveResources { @@ -56,14 +56,14 @@ void setHaveResourcesInvalidArgs() { /* Not a number (NAN) test */ assertThrows(IllegalArgumentException.class, () -> attachment.setHaveResources("NAN:PUs")); assertThrows( - IllegalArgumentException.class, () -> attachment.setHaveResources("NAN:add:PUs")); + IllegalArgumentException.class, () -> attachment.setHaveResources("NAN:add:PUs")); /* -1 value test */ assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:PUs")); assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:add:PUs")); /* Not a resource test */ assertThrows(GameParseException.class, () -> attachment.setHaveResources("1:NOT A RESOURCE")); assertThrows( - GameParseException.class, () -> attachment.setHaveResources("1:Sum:NOT A RESOURCE")); + GameParseException.class, () -> attachment.setHaveResources("1:Sum:NOT A RESOURCE")); assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:w")); assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:w:e")); assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:add:w")); @@ -85,46 +85,46 @@ void setHaveResourcesTest() throws Exception { /* Testing checkHaveResources */ @Test void testCheckHaveResources() throws Exception { - final int italianFuelAmount = italians.getResources().getQuantity(fuel); - final int italianPuAmount = italians.getResources().getQuantity(PUS); - final int italianOreAmount = italians.getResources().getQuantity(ore); - final int germanFuelAmount = germans.getResources().getQuantity(fuel); - final int germanPuAmount = germans.getResources().getQuantity(PUS); - final int germanOreAmount = germans.getResources().getQuantity(ore); - final int testItalianPU = italianPuAmount; - final int testItalianResources = italianOreAmount + italianFuelAmount + italianPuAmount; - final int testPUs = testItalianPU + germanPuAmount; - final int testResources = testItalianResources + germanPuAmount + germanFuelAmount + germanOreAmount; - - /* testing with 1 player */ - final List players = List.of(italians); - attachment.setHaveResources(concatWithColon(String.valueOf(testItalianPU), PUS)); - assertTrue(attachment.checkHaveResources(players)); - attachment.setHaveResources( - concatWithColon(String.valueOf(testItalianResources), addString, PUS)); - assertFalse(attachment.checkHaveResources(players)); - attachment.setHaveResources( - concatWithColon(String.valueOf(testItalianResources), addString, PUS, fuel)); - assertFalse(attachment.checkHaveResources(players)); - attachment.setHaveResources( - concatWithColon(String.valueOf(testItalianResources), addString, PUS, fuel, ore)); - assertTrue(attachment.checkHaveResources(players)); - - /* testing with 2 players */ - final List players1 = List.of(italians, germans); - attachment.setHaveResources(concatWithColon(String.valueOf(testPUs), PUS)); - assertFalse(attachment.checkHaveResources(players1)); - attachment.setHaveResources(concatWithColon(String.valueOf(testPUs), sumString, PUS)); - assertTrue(attachment.checkHaveResources(players1)); - attachment.setHaveResources( - concatWithColon(String.valueOf(testResources), sumString, PUS)); - assertFalse(attachment.checkHaveResources(players1)); - attachment.setHaveResources( - concatWithColon(String.valueOf(testResources), sumString, PUS, fuel)); - assertFalse(attachment.checkHaveResources(players1)); - attachment.setHaveResources( - concatWithColon(String.valueOf(testResources), sumString, PUS, fuel, ore)); - assertTrue(attachment.checkHaveResources(players1)); + final int italianFuelAmount = italians.getResources().getQuantity(fuel); + final int italianPuAmount = italians.getResources().getQuantity(PUS); + final int italianOreAmount = italians.getResources().getQuantity(ore); + final int germanFuelAmount = germans.getResources().getQuantity(fuel); + final int germanPuAmount = germans.getResources().getQuantity(PUS); + final int germanOreAmount = germans.getResources().getQuantity(ore); + final int testItalianPU = italianPuAmount; + final int testItalianResources = italianOreAmount + italianFuelAmount + italianPuAmount; + final int testPUs = testItalianPU + germanPuAmount; + final int testResources = testItalianResources + germanPuAmount + germanFuelAmount + germanOreAmount; + + /* testing with 1 player */ + final List players = List.of(italians); + attachment.setHaveResources(concatWithColon(String.valueOf(testItalianPU), PUS)); + assertTrue(attachment.checkHaveResources(players)); + attachment.setHaveResources( + concatWithColon(String.valueOf(testItalianResources), addString, PUS)); + assertFalse(attachment.checkHaveResources(players)); + attachment.setHaveResources( + concatWithColon(String.valueOf(testItalianResources), addString, PUS, fuel)); + assertFalse(attachment.checkHaveResources(players)); + attachment.setHaveResources( + concatWithColon(String.valueOf(testItalianResources), addString, PUS, fuel, ore)); + assertTrue(attachment.checkHaveResources(players)); + + /* testing with 2 players */ + final List players1 = List.of(italians, germans); + attachment.setHaveResources(concatWithColon(String.valueOf(testPUs), PUS)); + assertFalse(attachment.checkHaveResources(players1)); + attachment.setHaveResources(concatWithColon(String.valueOf(testPUs), sumString, PUS)); + assertTrue(attachment.checkHaveResources(players1)); + attachment.setHaveResources( + concatWithColon(String.valueOf(testResources), sumString, PUS)); + assertFalse(attachment.checkHaveResources(players1)); + attachment.setHaveResources( + concatWithColon(String.valueOf(testResources), sumString, PUS, fuel)); + assertFalse(attachment.checkHaveResources(players1)); + attachment.setHaveResources( + concatWithColon(String.valueOf(testResources), sumString, PUS, fuel, ore)); + assertTrue(attachment.checkHaveResources(players1)); } @Test From 71b222b71ab0d45015e6f0bfc17fd6af7bc3f9d4 Mon Sep 17 00:00:00 2001 From: WCSumpton Date: Mon, 18 Sep 2023 11:48:40 -0400 Subject: [PATCH 09/10] More formatting issues Formatting, and removal of @Test for concatWithColon --- .../triplea/attachments/RulesAttachmentTest.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java b/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java index 3df158f031..7be3c0d3e0 100644 --- a/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java +++ b/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java @@ -94,7 +94,8 @@ void testCheckHaveResources() throws Exception { final int testItalianPU = italianPuAmount; final int testItalianResources = italianOreAmount + italianFuelAmount + italianPuAmount; final int testPUs = testItalianPU + germanPuAmount; - final int testResources = testItalianResources + germanPuAmount + germanFuelAmount + germanOreAmount; + final int testResources = + testItalianResources + germanPuAmount + germanFuelAmount + germanOreAmount; /* testing with 1 player */ final List players = List.of(italians); @@ -116,8 +117,7 @@ void testCheckHaveResources() throws Exception { assertFalse(attachment.checkHaveResources(players1)); attachment.setHaveResources(concatWithColon(String.valueOf(testPUs), sumString, PUS)); assertTrue(attachment.checkHaveResources(players1)); - attachment.setHaveResources( - concatWithColon(String.valueOf(testResources), sumString, PUS)); + attachment.setHaveResources(concatWithColon(String.valueOf(testResources), sumString, PUS)); assertFalse(attachment.checkHaveResources(players1)); attachment.setHaveResources( concatWithColon(String.valueOf(testResources), sumString, PUS, fuel)); @@ -125,9 +125,8 @@ void testCheckHaveResources() throws Exception { attachment.setHaveResources( concatWithColon(String.valueOf(testResources), sumString, PUS, fuel, ore)); assertTrue(attachment.checkHaveResources(players1)); - } - @Test + private String concatWithColon(final String... args) { return String.join(":", args); } From ac9a366334bc7215bfa049d04029fed36ff53843 Mon Sep 17 00:00:00 2001 From: WCSumpton Date: Tue, 19 Sep 2023 07:17:55 -0400 Subject: [PATCH 10/10] Removal of and/sum This removes add/sum keywords and allows haveResources to function more like unitPresence. The list of resources/players are summed automatically. --- .../strategy/engine/data/UnitTypeList.java | 16 +++ .../triplea/attachments/RulesAttachment.java | 16 +-- .../attachments/TechAbilityAttachment.java | 25 ++++ .../triplea/attachments/UnitAttachment.java | 10 +- .../attachments/UnitSupportAttachment.java | 22 ++- .../triplea/delegate/TechTracker.java | 8 ++ .../triplea/delegate/battle/AirBattle.java | 26 ++-- .../AirBattleDefenseCombatValue.java | 54 ++++++-- .../AirBattleOffenseCombatValue.java | 45 ++++-- .../power/calculator/CombatValueBuilder.java | 46 ++++++- .../strategy/triplea/ui/BattleModel.java | 4 +- .../triplea/util/TuvCostsCalculator.java | 15 +- .../attachments/RulesAttachmentTest.java | 34 ++--- .../AirBattleDefenseCombatValueTest.java | 124 ++++++++++++++++- .../AirBattleOffenseCombatValueTest.java | 128 ++++++++++++++++-- 15 files changed, 463 insertions(+), 110 deletions(-) diff --git a/game-app/game-core/src/main/java/games/strategy/engine/data/UnitTypeList.java b/game-app/game-core/src/main/java/games/strategy/engine/data/UnitTypeList.java index b74c8a8b0e..b6d7bb1645 100644 --- a/game-app/game-core/src/main/java/games/strategy/engine/data/UnitTypeList.java +++ b/game-app/game-core/src/main/java/games/strategy/engine/data/UnitTypeList.java @@ -24,6 +24,7 @@ public class UnitTypeList extends GameDataComponent implements Iterable supportRules; private transient @Nullable Set supportAaRules; + private transient @Nullable Set supportAirRules; public UnitTypeList(final GameData data) { super(data); @@ -81,6 +82,21 @@ public Set getSupportAaRules() { return supportAaRules; } + /** + * Returns the unit support rules for the unit types. Computed once and cached afterwards. + * + * @return The unit support rules. + */ + public Set getSupportAirRules() { + if (supportAirRules == null) { + supportAirRules = + UnitSupportAttachment.get(this).stream() + .filter(usa -> (usa.getAirRoll() || usa.getAirStrength())) + .collect(Collectors.toSet()); + } + return supportAirRules; + } + public int size() { return unitTypes.size(); } diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/attachments/RulesAttachment.java b/game-app/game-core/src/main/java/games/strategy/triplea/attachments/RulesAttachment.java index 320dbb133a..526e797302 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/attachments/RulesAttachment.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/attachments/RulesAttachment.java @@ -160,20 +160,11 @@ public void setHaveResources(final String value) throws GameParseException { + "value=resource1:resource2:resource3 count=number" + thisErrorMsg()); } - if ((s.length <= 2) && (s[1].equalsIgnoreCase("sum") || s[1].equalsIgnoreCase("add"))) { - throw new GameParseException( - "haveResources must have at least 3 fields when used with 'Sum' or 'Add'. Format value=Sum:resource1 " - + "count=number, or value=Sum:resource1:resource2:resource3 count=number" - + thisErrorMsg()); - } final int n = getInt(s[0]); if (n < 1) { throw new GameParseException("haveResources must be a positive integer" + thisErrorMsg()); } for (int i = 1; i < s.length; i++) { - if (s[i].equalsIgnoreCase("sum") || s[i].equalsIgnoreCase("add")) { - i++; - } // validate that this resource exists in the xml final Resource r = getData().getResourceList().getResource(s[i]); if (r == null) { @@ -1117,14 +1108,11 @@ private boolean checkTechs(final GamePlayer player, final TechnologyFrontier tec @VisibleForTesting public boolean checkHaveResources(final List players) { - final boolean toSum = - haveResources[1].equalsIgnoreCase("sum") || haveResources[1].equalsIgnoreCase("add"); int itotal = 0; for (GamePlayer player : players) { - for (int i = toSum ? 2 : 1; i < haveResources.length; i++) { + for (int i = 1; i < haveResources.length; i++) { final Resource resource = getData().getResourceList().getResource(haveResources[i]); - int iamount = player.getResources().getQuantity(resource); - itotal = toSum ? itotal + iamount : Math.max(itotal, iamount); + itotal += player.getResources().getQuantity(resource); } } return itotal >= getInt(haveResources[0]); diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/attachments/TechAbilityAttachment.java b/game-app/game-core/src/main/java/games/strategy/triplea/attachments/TechAbilityAttachment.java index f683c44876..01bfbdc9aa 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/attachments/TechAbilityAttachment.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/attachments/TechAbilityAttachment.java @@ -68,6 +68,7 @@ public class TechAbilityAttachment extends DefaultAttachment { private @Nullable IntegerMap attackRollsBonus = null; private @Nullable IntegerMap defenseRollsBonus = null; private @Nullable IntegerMap bombingBonus = null; + private @Nullable IntegerMap tuvBonus = null; public TechAbilityAttachment( final String name, final Attachable attachable, final GameData gameData) { @@ -692,6 +693,24 @@ private void resetBombingBonus() { bombingBonus = null; } + private void setTUVBonus(final String value) throws GameParseException { + if (tuvBonus == null) { + tuvBonus = new IntegerMap<>(); + } + applyCheckedValue("tuvBonus", value, tuvBonus::put); + } + + private void setTUVBonus(final IntegerMap value) { + tuvBonus = value; + } + + public IntegerMap getTUVBonus() { + return getIntegerMapProperty(tuvBonus); + } + + private void resetTUVBonus() { + tuvBonus = null; + } public static boolean getAllowAirborneForces(final Collection techAdvances) { return techAdvances.stream() .map(TechAbilityAttachment::get) @@ -1029,6 +1048,12 @@ public MutableProperty getPropertyOrNull(String propertyName) { this::setBombingBonus, this::getBombingBonus, this::resetBombingBonus); + case "tuvBonus": + return MutableProperty.of( + this::setTUVBonus, + this::setTUVBonus, + this::getTUVBonus, + this::resetTUVBonus); default: return null; } diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/attachments/UnitAttachment.java b/game-app/game-core/src/main/java/games/strategy/triplea/attachments/UnitAttachment.java index 1d95388c04..84cc8a58e7 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/attachments/UnitAttachment.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/attachments/UnitAttachment.java @@ -2525,13 +2525,19 @@ private void setTuv(final String s) throws GameParseException { tuv = getInt(s); } - private void setTuv(final Integer s) { + @VisibleForTesting + public void setTuv(final Integer s) { tuv = s; } - public int getTuv() { + private int getTuv() { return tuv; } + public int getTuv(final GamePlayer player) { + // must account for -1 value, so that TUV can be calculated + final int bonus = getTechTracker().tuv(player, getUnitType()); + return Math.max(-1, bonus + tuv); + } private void resetTuv() { tuv = -1; diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/attachments/UnitSupportAttachment.java b/game-app/game-core/src/main/java/games/strategy/triplea/attachments/UnitSupportAttachment.java index 24b6c68471..1e5321248c 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/attachments/UnitSupportAttachment.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/attachments/UnitSupportAttachment.java @@ -45,6 +45,8 @@ public class UnitSupportAttachment extends DefaultAttachment { private boolean strength = false; private boolean aaRoll = false; private boolean aaStrength = false; + private boolean airRoll = false; + private boolean airStrength = false; private int bonus = 0; private int number = 0; private boolean allied = false; @@ -184,9 +186,13 @@ public UnitSupportAttachment setDice(final String dice) throws GameParseExceptio aaRoll = true; } else if (element.equalsIgnoreCase("AAstrength")) { aaStrength = true; + } else if (element.equalsIgnoreCase("airRoll")) { + airRoll = true; + } else if (element.equalsIgnoreCase("airStrength")) { + airStrength = true; } else { throw new GameParseException( - dice + " dice must be roll, strength, AAroll, or AAstrength: " + thisErrorMsg()); + dice + " dice must be roll, strength, AAroll, AAstrength, airRoll, or airStrength: " + thisErrorMsg()); } } return this; @@ -203,6 +209,8 @@ private void resetDice() { strength = false; aaRoll = false; aaStrength = false; + airRoll = false; + airStrength = false; } private void setBonus(final String bonus) { @@ -327,6 +335,14 @@ public boolean getAaStrength() { return aaStrength; } + public boolean getAirRoll() { + return airRoll; + } + + public boolean getAirStrength() { + return airStrength; + } + public boolean getDefence() { return defence; } @@ -437,6 +453,10 @@ public MutableProperty getPropertyOrNull(String propertyName) { return MutableProperty.ofReadOnly(this::getAaRoll); case "aaStrength": return MutableProperty.ofReadOnly(this::getAaStrength); + case "airRoll": + return MutableProperty.ofReadOnly(this::getAirRoll); + case "airStrength": + return MutableProperty.ofReadOnly(this::getAirStrength); case BONUS: return MutableProperty.of(this::setBonus, this::setBonus, this::getBonus, this::resetBonus); case "number": diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/delegate/TechTracker.java b/game-app/game-core/src/main/java/games/strategy/triplea/delegate/TechTracker.java index b041a2828e..c75cb2f83d 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/delegate/TechTracker.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/delegate/TechTracker.java @@ -120,6 +120,14 @@ public boolean canBombard(GamePlayer player, UnitType type) { return getCached(player, type, "canBombard", getter); } + public int tuv(GamePlayer player, UnitType type) { + //sum will be 0 if there are no TUV bonus + //the return value must be checked against -1 calculate + final Supplier getter = + () -> getSumOfBonuses(TechAbilityAttachment::getTUVBonus, type, player); + return getCached(player, type, "getTUVBonus", getter); + } + public int getMinimumTerritoryValueForProductionBonus(final GamePlayer player) { final Supplier getter = () -> diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/delegate/battle/AirBattle.java b/game-app/game-core/src/main/java/games/strategy/triplea/delegate/battle/AirBattle.java index c645b53975..a9b35bcca9 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/delegate/battle/AirBattle.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/delegate/battle/AirBattle.java @@ -696,9 +696,10 @@ public void execute(final ExecutionStack stack, final IDelegateBridge bridge) { bridge, "Attackers Fire, ", CombatValueBuilder.airBattleCombatValue() + .enemyUnits(defendingUnits) + .friendlyUnits(attackingUnits) .side(BattleState.Side.OFFENSE) - .lhtrHeavyBombers( - Properties.getLhtrHeavyBombers(bridge.getData().getProperties())) + .supportAttachments(bridge.getData().getUnitTypeList().getSupportAirRules()) .gameDiceSides(bridge.getData().getDiceSides()) .build()); } @@ -713,16 +714,12 @@ public void execute(final ExecutionStack stack, final IDelegateBridge bridge) { CasualtySelector.selectCasualties( defender, defendingUnits, - CombatValueBuilder.mainCombatValue() + CombatValueBuilder.airBattleCombatValue() .enemyUnits(attackingUnits) .friendlyUnits(defendingUnits) .side(BattleState.Side.DEFENSE) - .gameSequence(bridge.getData().getSequence()) - .supportAttachments(bridge.getData().getUnitTypeList().getSupportRules()) - .lhtrHeavyBombers( - Properties.getLhtrHeavyBombers(bridge.getData().getProperties())) + .supportAttachments(bridge.getData().getUnitTypeList().getSupportAirRules()) .gameDiceSides(bridge.getData().getDiceSides()) - .territoryEffects(List.of()) .build(), battleSite, bridge, @@ -776,9 +773,10 @@ public void execute(final ExecutionStack stack, final IDelegateBridge bridge) { bridge, "Defenders Fire, ", CombatValueBuilder.airBattleCombatValue() + .enemyUnits(attackingUnits) + .friendlyUnits(defendingUnits) .side(BattleState.Side.DEFENSE) - .lhtrHeavyBombers( - Properties.getLhtrHeavyBombers(bridge.getData().getProperties())) + .supportAttachments(bridge.getData().getUnitTypeList().getSupportAirRules()) .gameDiceSides(bridge.getData().getDiceSides()) .build()); } @@ -793,16 +791,12 @@ public void execute(final ExecutionStack stack, final IDelegateBridge bridge) { CasualtySelector.selectCasualties( attacker, attackingUnits, - CombatValueBuilder.mainCombatValue() + CombatValueBuilder.airBattleCombatValue() .enemyUnits(defendingUnits) .friendlyUnits(attackingUnits) .side(BattleState.Side.OFFENSE) - .gameSequence(bridge.getData().getSequence()) - .supportAttachments(bridge.getData().getUnitTypeList().getSupportRules()) - .lhtrHeavyBombers( - Properties.getLhtrHeavyBombers(bridge.getData().getProperties())) + .supportAttachments(bridge.getData().getUnitTypeList().getSupportAirRules()) .gameDiceSides(bridge.getData().getDiceSides()) - .territoryEffects(List.of()) .build(), battleSite, bridge, diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/delegate/power/calculator/AirBattleDefenseCombatValue.java b/game-app/game-core/src/main/java/games/strategy/triplea/delegate/power/calculator/AirBattleDefenseCombatValue.java index 2a943fdb8c..4b3b9df07e 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/delegate/power/calculator/AirBattleDefenseCombatValue.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/delegate/power/calculator/AirBattleDefenseCombatValue.java @@ -23,7 +23,10 @@ class AirBattleDefenseCombatValue implements CombatValue { @Nonnull Integer gameDiceSides; - @Nonnull Boolean lhtrHeavyBombers; + @Nonnull AvailableSupports strengthSupportFromFriends; + @Nonnull AvailableSupports strengthSupportFromEnemies; + @Nonnull AvailableSupports rollSupportFromFriends; + @Nonnull AvailableSupports rollSupportFromEnemies; @Getter(onMethod_ = @Override) @Nonnull @@ -37,34 +40,42 @@ class AirBattleDefenseCombatValue implements CombatValue { @Override public RollCalculator getRoll() { - return new AirBattleDefenseRoll(); + return new AirBattleDefenseRoll(rollSupportFromFriends.copy(), rollSupportFromEnemies.copy()); } @Override public StrengthCalculator getStrength() { - return new AirBattleDefenseStrength(gameDiceSides); + return new AirBattleDefenseStrength( + gameDiceSides, + strengthSupportFromFriends.copy(), + strengthSupportFromEnemies.copy()); } @Override - public BattleState.Side getBattleSide() { - return BattleState.Side.DEFENSE; + public int getDiceSides(final Unit unit) { + return gameDiceSides; } @Override - public int getDiceSides(final Unit unit) { - return gameDiceSides; + public BattleState.Side getBattleSide() { + return BattleState.Side.DEFENSE; } @Override public boolean chooseBestRoll(final Unit unit) { - return lhtrHeavyBombers || unit.getUnitAttachment().getChooseBestRoll(); + return false; } @Override public CombatValue buildWithNoUnitSupports() { return AirBattleDefenseCombatValue.builder() .gameDiceSides(gameDiceSides) - .lhtrHeavyBombers(lhtrHeavyBombers) + .rollSupportFromFriends(AvailableSupports.EMPTY_RESULT) + .rollSupportFromEnemies(AvailableSupports.EMPTY_RESULT) + .strengthSupportFromFriends(AvailableSupports.EMPTY_RESULT) + .strengthSupportFromEnemies(AvailableSupports.EMPTY_RESULT) + .friendUnits(List.of()) + .enemyUnits(List.of()) .build(); } @@ -72,21 +83,31 @@ public CombatValue buildWithNoUnitSupports() { public CombatValue buildOppositeCombatValue() { return AirBattleOffenseCombatValue.builder() .gameDiceSides(gameDiceSides) - .lhtrHeavyBombers(lhtrHeavyBombers) + .rollSupportFromFriends(rollSupportFromEnemies) + .rollSupportFromEnemies(rollSupportFromFriends) + .strengthSupportFromFriends(strengthSupportFromEnemies) + .strengthSupportFromEnemies(strengthSupportFromFriends) + .friendUnits(enemyUnits) + .enemyUnits(friendUnits) .build(); } @Value static class AirBattleDefenseRoll implements RollCalculator { + AvailableSupports supportFromFriends; + AvailableSupports supportFromEnemies; + @Override public RollValue getRoll(final Unit unit) { - return RollValue.of(unit.getUnitAttachment().getDefenseRolls(unit.getOwner())); + return RollValue.of(1) + .add(supportFromFriends.giveSupportToUnit(unit)) + .add(supportFromEnemies.giveSupportToUnit(unit)); } @Override public Map> getSupportGiven() { - return Map.of(); + return SupportCalculator.getCombinedSupportGiven(supportFromFriends, supportFromEnemies); } } @@ -94,15 +115,20 @@ public Map> getSupportGiven() { static class AirBattleDefenseStrength implements StrengthCalculator { int diceSides; + AvailableSupports supportFromFriends; + AvailableSupports supportFromEnemies; @Override public StrengthValue getStrength(final Unit unit) { - return StrengthValue.of(diceSides, unit.getUnitAttachment().getAirDefense(unit.getOwner())); + return StrengthValue.of(diceSides, + unit.getUnitAttachment().getAirDefense(unit.getOwner())) + .add(supportFromFriends.giveSupportToUnit(unit)) + .add(supportFromEnemies.giveSupportToUnit(unit)); } @Override public Map> getSupportGiven() { - return Map.of(); + return SupportCalculator.getCombinedSupportGiven(supportFromFriends, supportFromEnemies); } } } diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/delegate/power/calculator/AirBattleOffenseCombatValue.java b/game-app/game-core/src/main/java/games/strategy/triplea/delegate/power/calculator/AirBattleOffenseCombatValue.java index 67bded7a37..d2cf8661f5 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/delegate/power/calculator/AirBattleOffenseCombatValue.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/delegate/power/calculator/AirBattleOffenseCombatValue.java @@ -23,7 +23,10 @@ class AirBattleOffenseCombatValue implements CombatValue { @Nonnull Integer gameDiceSides; - @Nonnull Boolean lhtrHeavyBombers; + @Nonnull AvailableSupports strengthSupportFromFriends; + @Nonnull AvailableSupports strengthSupportFromEnemies; + @Nonnull AvailableSupports rollSupportFromFriends; + @Nonnull AvailableSupports rollSupportFromEnemies; @Getter(onMethod_ = @Override) @Nonnull @@ -37,12 +40,15 @@ class AirBattleOffenseCombatValue implements CombatValue { @Override public RollCalculator getRoll() { - return new AirBattleOffenseRoll(); + return new AirBattleOffenseRoll(rollSupportFromFriends.copy(), rollSupportFromEnemies.copy()); } @Override public StrengthCalculator getStrength() { - return new AirBattleOffenseStrength(gameDiceSides); + return new AirBattleOffenseStrength( + gameDiceSides, + strengthSupportFromFriends.copy(), + strengthSupportFromEnemies.copy()); } @Override @@ -57,14 +63,19 @@ public int getDiceSides(final Unit unit) { @Override public boolean chooseBestRoll(final Unit unit) { - return lhtrHeavyBombers || unit.getUnitAttachment().getChooseBestRoll(); + return false; } @Override public CombatValue buildWithNoUnitSupports() { return AirBattleOffenseCombatValue.builder() .gameDiceSides(gameDiceSides) - .lhtrHeavyBombers(lhtrHeavyBombers) + .rollSupportFromFriends(AvailableSupports.EMPTY_RESULT) + .rollSupportFromEnemies(AvailableSupports.EMPTY_RESULT) + .strengthSupportFromFriends(AvailableSupports.EMPTY_RESULT) + .strengthSupportFromEnemies(AvailableSupports.EMPTY_RESULT) + .friendUnits(List.of()) + .enemyUnits(List.of()) .build(); } @@ -72,21 +83,30 @@ public CombatValue buildWithNoUnitSupports() { public CombatValue buildOppositeCombatValue() { return AirBattleDefenseCombatValue.builder() .gameDiceSides(gameDiceSides) - .lhtrHeavyBombers(lhtrHeavyBombers) + .rollSupportFromFriends(rollSupportFromEnemies) + .rollSupportFromEnemies(rollSupportFromFriends) + .strengthSupportFromFriends(strengthSupportFromEnemies) + .strengthSupportFromEnemies(strengthSupportFromFriends) + .friendUnits(enemyUnits) + .enemyUnits(friendUnits) .build(); } @Value static class AirBattleOffenseRoll implements RollCalculator { + AvailableSupports supportFromFriends; + AvailableSupports supportFromEnemies; @Override public RollValue getRoll(final Unit unit) { - return RollValue.of(unit.getUnitAttachment().getAttackRolls(unit.getOwner())); + return RollValue.of(1) + .add(supportFromFriends.giveSupportToUnit(unit)) + .add(supportFromEnemies.giveSupportToUnit(unit)); } @Override public Map> getSupportGiven() { - return Map.of(); + return SupportCalculator.getCombinedSupportGiven(supportFromFriends, supportFromEnemies); } } @@ -94,15 +114,20 @@ public Map> getSupportGiven() { static class AirBattleOffenseStrength implements StrengthCalculator { int diceSides; + AvailableSupports supportFromFriends; + AvailableSupports supportFromEnemies; @Override public StrengthValue getStrength(final Unit unit) { - return StrengthValue.of(diceSides, unit.getUnitAttachment().getAirAttack(unit.getOwner())); + return StrengthValue.of(diceSides, + unit.getUnitAttachment().getAirAttack(unit.getOwner())) + .add(supportFromFriends.giveSupportToUnit(unit)) + .add(supportFromEnemies.giveSupportToUnit(unit)); } @Override public Map> getSupportGiven() { - return Map.of(); + return SupportCalculator.getCombinedSupportGiven(supportFromFriends, supportFromEnemies); } } } diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/delegate/power/calculator/CombatValueBuilder.java b/game-app/game-core/src/main/java/games/strategy/triplea/delegate/power/calculator/CombatValueBuilder.java index b9a3db6c0a..039556d535 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/delegate/power/calculator/CombatValueBuilder.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/delegate/power/calculator/CombatValueBuilder.java @@ -149,16 +149,52 @@ static CombatValue buildBombardmentCombatValue( @Builder(builderMethodName = "airBattleCombatValue", builderClassName = "AirBattleBuilder") static CombatValue buildAirBattleCombatValue( - final BattleState.Side side, final boolean lhtrHeavyBombers, final int gameDiceSides) { + final Collection enemyUnits, + final Collection friendlyUnits, + final BattleState.Side side, + final Collection supportAttachments, + final int gameDiceSides) { + + // Get all friendly supports + final AvailableSupports supportFromFriends = + AvailableSupports.getSortedSupport( + new SupportCalculator( + friendlyUnits, // + supportAttachments, + side, + true)); + + // Get all enemy supports + final AvailableSupports supportFromEnemies = + AvailableSupports.getSortedSupport( + new SupportCalculator( + enemyUnits, // + supportAttachments, + side.getOpposite(), + false)); return side == BattleState.Side.DEFENSE - ? AirBattleDefenseCombatValue.builder() + ? AirBattleDefenseCombatValue.builder() + .strengthSupportFromFriends( + supportFromFriends.filter(UnitSupportAttachment::getAirStrength)) + .strengthSupportFromEnemies( + supportFromEnemies.filter(UnitSupportAttachment::getAirStrength)) + .rollSupportFromFriends(supportFromFriends.filter(UnitSupportAttachment::getAirRoll)) + .rollSupportFromEnemies(supportFromEnemies.filter(UnitSupportAttachment::getAirRoll)) + .friendUnits(friendlyUnits) + .enemyUnits(enemyUnits) .gameDiceSides(gameDiceSides) - .lhtrHeavyBombers(lhtrHeavyBombers) .build() - : AirBattleOffenseCombatValue.builder() + : AirBattleOffenseCombatValue.builder() + .strengthSupportFromFriends( + supportFromFriends.filter(UnitSupportAttachment::getAirStrength)) + .strengthSupportFromEnemies( + supportFromEnemies.filter(UnitSupportAttachment::getAirStrength)) + .rollSupportFromFriends(supportFromFriends.filter(UnitSupportAttachment::getAirRoll)) + .rollSupportFromEnemies(supportFromEnemies.filter(UnitSupportAttachment::getAirRoll)) + .friendUnits(friendlyUnits) + .enemyUnits(enemyUnits) .gameDiceSides(gameDiceSides) - .lhtrHeavyBombers(lhtrHeavyBombers) .build(); } } diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/ui/BattleModel.java b/game-app/game-core/src/main/java/games/strategy/triplea/ui/BattleModel.java index f76f8495ae..63acedf85a 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/ui/BattleModel.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/ui/BattleModel.java @@ -109,8 +109,10 @@ void refresh() { if (isAirPreBattleOrPreRaid) { combatValue = CombatValueBuilder.airBattleCombatValue() + .enemyUnits(new ArrayList<>(enemyBattleModel.getUnits())) + .friendlyUnits(units) .side(BattleState.Side.DEFENSE) - .lhtrHeavyBombers(lhtrHeavyBombers) + .supportAttachments(gameData.getUnitTypeList().getSupportAirRules()) .gameDiceSides(gameData.getDiceSides()) .build(); } else { diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/util/TuvCostsCalculator.java b/game-app/game-core/src/main/java/games/strategy/triplea/util/TuvCostsCalculator.java index dd5c4468d0..5a754b8af0 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/util/TuvCostsCalculator.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/util/TuvCostsCalculator.java @@ -52,7 +52,7 @@ public IntegerMap computeCostsForTuv(final GamePlayer player) { // Override with XML TUV or consumesUnit sum final IntegerMap result = new IntegerMap<>(costs); for (final UnitType unitType : costs.keySet()) { - result.put(unitType, getTotalTuv(unitType, costs, new HashSet<>())); + result.put(unitType, getTotalTuv(player, unitType, costs, new HashSet<>())); } return result; @@ -88,6 +88,7 @@ private IntegerMap computeBaseCostsForPlayer(GamePlayer player) { */ private static IntegerMap getCostsForTuvForAllPlayersMergedAndAveraged( final GameData data) { + final GamePlayer nullPlayer = data.getPlayerList().getNullPlayer(); final Resource pus = data.getResourceList().getResource(Constants.PUS); final IntegerMap costs = new IntegerMap<>(); final Map> differentCosts = new HashMap<>(); @@ -120,19 +121,19 @@ private static IntegerMap getCostsForTuvForAllPlayersMergedAndAveraged // Add any units that have XML TUV even if they aren't purchasable for (final UnitType unitType : data.getUnitTypeList()) { final UnitAttachment ua = unitType.getUnitAttachment(); - if (ua.getTuv() > -1) { - costs.put(unitType, ua.getTuv()); + if (ua.getTuv(nullPlayer) > -1) { + costs.put(unitType, ua.getTuv(nullPlayer)); } } return costs; } - private static int getTotalTuv( + private static int getTotalTuv(final GamePlayer player, final UnitType unitType, final IntegerMap costs, final Set alreadyAdded) { final UnitAttachment ua = unitType.getUnitAttachment(); - if (ua.getTuv() > -1) { - return ua.getTuv(); + if (ua.getTuv(player) > -1) { + return ua.getTuv(player); } int tuv = costs.getInt(unitType); if (ua.getConsumesUnits().isEmpty() || alreadyAdded.contains(unitType)) { @@ -140,7 +141,7 @@ private static int getTotalTuv( } alreadyAdded.add(unitType); for (final UnitType ut : ua.getConsumesUnits().keySet()) { - tuv += ua.getConsumesUnits().getInt(ut) * getTotalTuv(ut, costs, alreadyAdded); + tuv += ua.getConsumesUnits().getInt(ut) * getTotalTuv(player, ut, costs, alreadyAdded); } alreadyAdded.remove(unitType); return tuv; diff --git a/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java b/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java index 7be3c0d3e0..4e8e7bb48d 100644 --- a/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java +++ b/game-app/game-core/src/test/java/games/strategy/triplea/attachments/RulesAttachmentTest.java @@ -37,17 +37,11 @@ class HaveResources { private final GamePlayer germans = GameDataTestUtil.germans(gameData); private final String fuel = "Fuel"; private final String ore = "Ore"; - private final String addString = "add"; - private final String sumString = "SUM"; - /* Length test for haveResources */ @Test void setHaveResourcesInvalidLength() { assertThrows(GameParseException.class, () -> attachment.setHaveResources("")); - assertThrows(GameParseException.class, () -> attachment.setHaveResources(":add")); - assertThrows(GameParseException.class, () -> attachment.setHaveResources("a")); - assertThrows(GameParseException.class, () -> attachment.setHaveResources("a:add")); } /* Invalid arguments for haveResources */ @@ -55,15 +49,10 @@ void setHaveResourcesInvalidLength() { void setHaveResourcesInvalidArgs() { /* Not a number (NAN) test */ assertThrows(IllegalArgumentException.class, () -> attachment.setHaveResources("NAN:PUs")); - assertThrows( - IllegalArgumentException.class, () -> attachment.setHaveResources("NAN:add:PUs")); - /* -1 value test */ + /* 0 value test */ assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:PUs")); - assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:add:PUs")); /* Not a resource test */ assertThrows(GameParseException.class, () -> attachment.setHaveResources("1:NOT A RESOURCE")); - assertThrows( - GameParseException.class, () -> attachment.setHaveResources("1:Sum:NOT A RESOURCE")); assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:w")); assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:w:e")); assertThrows(GameParseException.class, () -> attachment.setHaveResources("0:add:w")); @@ -77,9 +66,9 @@ void setHaveResourcesTest() throws Exception { final String random1 = Integer.toString(Math.abs(rand.nextInt())); final String[] expected1 = new String[] {random1, PUS}; - attachment.setHaveResources(concatWithColon(random1, addString, PUS)); + attachment.setHaveResources(concatWithColon(random1, PUS)); assertEquals(expected1[0], attachment.getHaveResources()[0]); - assertEquals(expected1[1], attachment.getHaveResources()[2]); + assertEquals(expected1[1], attachment.getHaveResources()[1]); } /* Testing checkHaveResources */ @@ -101,29 +90,24 @@ void testCheckHaveResources() throws Exception { final List players = List.of(italians); attachment.setHaveResources(concatWithColon(String.valueOf(testItalianPU), PUS)); assertTrue(attachment.checkHaveResources(players)); - attachment.setHaveResources( - concatWithColon(String.valueOf(testItalianResources), addString, PUS)); + attachment.setHaveResources(concatWithColon(String.valueOf(testItalianResources), PUS)); assertFalse(attachment.checkHaveResources(players)); attachment.setHaveResources( - concatWithColon(String.valueOf(testItalianResources), addString, PUS, fuel)); + concatWithColon(String.valueOf(testItalianResources), PUS, fuel)); assertFalse(attachment.checkHaveResources(players)); attachment.setHaveResources( - concatWithColon(String.valueOf(testItalianResources), addString, PUS, fuel, ore)); + concatWithColon(String.valueOf(testItalianResources), PUS, fuel, ore)); assertTrue(attachment.checkHaveResources(players)); /* testing with 2 players */ final List players1 = List.of(italians, germans); attachment.setHaveResources(concatWithColon(String.valueOf(testPUs), PUS)); - assertFalse(attachment.checkHaveResources(players1)); - attachment.setHaveResources(concatWithColon(String.valueOf(testPUs), sumString, PUS)); assertTrue(attachment.checkHaveResources(players1)); - attachment.setHaveResources(concatWithColon(String.valueOf(testResources), sumString, PUS)); + attachment.setHaveResources(concatWithColon(String.valueOf(testResources), PUS)); assertFalse(attachment.checkHaveResources(players1)); - attachment.setHaveResources( - concatWithColon(String.valueOf(testResources), sumString, PUS, fuel)); + attachment.setHaveResources(concatWithColon(String.valueOf(testResources), PUS, fuel)); assertFalse(attachment.checkHaveResources(players1)); - attachment.setHaveResources( - concatWithColon(String.valueOf(testResources), sumString, PUS, fuel, ore)); + attachment.setHaveResources(concatWithColon(String.valueOf(testResources), PUS, fuel, ore)); assertTrue(attachment.checkHaveResources(players1)); } diff --git a/game-app/game-core/src/test/java/games/strategy/triplea/delegate/power/calculator/AirBattleDefenseCombatValueTest.java b/game-app/game-core/src/test/java/games/strategy/triplea/delegate/power/calculator/AirBattleDefenseCombatValueTest.java index 3d9f7363e9..8c9d65adf9 100644 --- a/game-app/game-core/src/test/java/games/strategy/triplea/delegate/power/calculator/AirBattleDefenseCombatValueTest.java +++ b/game-app/game-core/src/test/java/games/strategy/triplea/delegate/power/calculator/AirBattleDefenseCombatValueTest.java @@ -10,9 +10,16 @@ import games.strategy.engine.data.GamePlayer; import games.strategy.engine.data.Unit; import games.strategy.engine.data.UnitType; +import games.strategy.engine.data.gameparser.GameParseException; import games.strategy.triplea.attachments.UnitAttachment; +import games.strategy.triplea.attachments.UnitSupportAttachment; +import games.strategy.triplea.delegate.battle.BattleState; +import java.util.List; +import java.util.Map; +import java.util.Set; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.triplea.java.collections.IntegerMap; class AirBattleDefenseCombatValueTest { @@ -20,7 +27,7 @@ class AirBattleDefenseCombatValueTest { class AirBattleDefenseStrengthTest { @Test - void calculatesValue() { + void calculatesValue() throws GameParseException { final GameData gameData = givenGameData().withDiceSides(6).build(); final GamePlayer player = mock(GamePlayer.class); @@ -31,9 +38,55 @@ void calculatesValue() { final Unit unit = unitType.createTemp(1, player).get(0); unit.getUnitAttachment().setAirDefense(3); + final Unit supportUnit = unitType.createTemp(1, player).get(0); + final UnitSupportAttachment unitSupportAttachment = + givenUnitSupportAttachment(gameData, unitType, "test") + .setBonus(3) + .setPlayers(List.of(player)) + .setUnitType(Set.of(unitType)); + + final AvailableSupports friendlySupport = + AvailableSupports.getSupport( + new SupportCalculator( + List.of(supportUnit), + Set.of(unitSupportAttachment), + BattleState.Side.OFFENSE, + true)); + + final Unit enemySupportUnit = unitType.createTemp(1, player).get(0); + final UnitSupportAttachment enemyUnitSupportAttachment = + givenUnitSupportAttachment(gameData, unitType, "test2") + .setBonus(-2) + .setPlayers(List.of(player)) + .setUnitType(Set.of(unitType)); + + final AvailableSupports enemySupport = + AvailableSupports.getSupport( + new SupportCalculator( + List.of(enemySupportUnit), + Set.of(enemyUnitSupportAttachment), + BattleState.Side.OFFENSE, + true)); + final AirBattleDefenseCombatValue.AirBattleDefenseStrength strength = - new AirBattleDefenseCombatValue.AirBattleDefenseStrength(6); - assertThat("Air defense is 3", strength.getStrength(unit).getValue(), is(3)); + new AirBattleDefenseCombatValue.AirBattleDefenseStrength( + 6, friendlySupport, enemySupport); + assertThat( + "Strength starts at 3, friendly adds 3, enemy removes 2: total 4", + strength.getStrength(unit).getValue(), + is(4)); + } + + UnitSupportAttachment givenUnitSupportAttachment( + final GameData gameData, final UnitType unitType, final String name) + throws GameParseException { + return new UnitSupportAttachment("rule" + name, unitType, gameData) + .setBonus(1) + .setBonusType("bonus" + name) + .setDice("airStrength") + .setNumber(1) + .setSide("offence") + .setFaction("allied"); } @Test @@ -49,11 +102,68 @@ void limitsToDiceSides() { unit.getUnitAttachment().setAirDefense(8); final AirBattleDefenseCombatValue.AirBattleDefenseStrength strength = - new AirBattleDefenseCombatValue.AirBattleDefenseStrength(6); + new AirBattleDefenseCombatValue.AirBattleDefenseStrength(6, AvailableSupports.EMPTY_RESULT, AvailableSupports.EMPTY_RESULT); assertThat( - "Air defense is 8 but dice sides is 6 so it is limited to 6", - strength.getStrength(unit).getValue(), - is(6)); + "Air defense is 8 but dice sides is 6 so it is limited to 6", + strength.getStrength(unit).getValue(), + is(6)); + } + + @Test + void calculatesSupportUsed() throws GameParseException { + final GameData gameData = givenGameData().withDiceSides(6).build(); + + final GamePlayer player = mock(GamePlayer.class); + + final UnitType unitType = new UnitType("test", gameData); + final UnitAttachment unitAttachment = new UnitAttachment("attachment", unitType, gameData); + unitType.addAttachment(UNIT_ATTACHMENT_NAME, unitAttachment); + final Unit unit = unitType.createTemp(1, player).get(0); + unit.getUnitAttachment().setAirDefense(3); + + final Unit supportUnit = unitType.createTemp(1, player).get(0); + final UnitSupportAttachment unitSupportAttachment = + givenUnitSupportAttachment(gameData, unitType, "test") + .setBonus(2) + .setPlayers(List.of(player)) + .setUnitType(Set.of(unitType)); + + final AvailableSupports friendlySupport = + AvailableSupports.getSupport( + new SupportCalculator( + List.of(supportUnit), + Set.of(unitSupportAttachment), + BattleState.Side.OFFENSE, + true)); + + final Unit enemySupportUnit = unitType.createTemp(1, player).get(0); + final UnitSupportAttachment enemyUnitSupportAttachment = + givenUnitSupportAttachment(gameData, unitType, "test2") + .setBonus(-1) + .setPlayers(List.of(player)) + .setUnitType(Set.of(unitType)); + + final AvailableSupports enemySupport = + AvailableSupports.getSupport( + new SupportCalculator( + List.of(enemySupportUnit), + Set.of(enemyUnitSupportAttachment), + BattleState.Side.OFFENSE, + true)); + + final AirBattleDefenseCombatValue.AirBattleDefenseStrength strength = + new AirBattleDefenseCombatValue.AirBattleDefenseStrength( + 6, friendlySupport, enemySupport); + strength.getStrength(unit); + assertThat( + "Friendly gave 2 and enemy gave -1", + strength.getSupportGiven(), + is( + Map.of( + supportUnit, + IntegerMap.of(Map.of(unit, 2)), + enemySupportUnit, + IntegerMap.of(Map.of(unit, -1))))); } } } diff --git a/game-app/game-core/src/test/java/games/strategy/triplea/delegate/power/calculator/AirBattleOffenseCombatValueTest.java b/game-app/game-core/src/test/java/games/strategy/triplea/delegate/power/calculator/AirBattleOffenseCombatValueTest.java index a49d8984c6..3be8bf3b25 100644 --- a/game-app/game-core/src/test/java/games/strategy/triplea/delegate/power/calculator/AirBattleOffenseCombatValueTest.java +++ b/game-app/game-core/src/test/java/games/strategy/triplea/delegate/power/calculator/AirBattleOffenseCombatValueTest.java @@ -10,9 +10,17 @@ import games.strategy.engine.data.GamePlayer; import games.strategy.engine.data.Unit; import games.strategy.engine.data.UnitType; +import games.strategy.engine.data.gameparser.GameParseException; import games.strategy.triplea.attachments.UnitAttachment; +import games.strategy.triplea.attachments.UnitSupportAttachment; +import games.strategy.triplea.delegate.battle.BattleState; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.triplea.java.collections.IntegerMap; + +import java.util.List; +import java.util.Map; +import java.util.Set; class AirBattleOffenseCombatValueTest { @@ -20,7 +28,7 @@ class AirBattleOffenseCombatValueTest { class AirBattleOffenseStrengthTest { @Test - void calculatesValue() { + void calculatesValue() throws GameParseException { final GameData gameData = givenGameData().withDiceSides(6).build(); final GamePlayer player = mock(GamePlayer.class); @@ -31,13 +39,59 @@ void calculatesValue() { final Unit unit = unitType.createTemp(1, player).get(0); unit.getUnitAttachment().setAirAttack(3); + final Unit supportUnit = unitType.createTemp(1, player).get(0); + final UnitSupportAttachment unitSupportAttachment = + givenUnitSupportAttachment(gameData, unitType, "test") + .setBonus(3) + .setPlayers(List.of(player)) + .setUnitType(Set.of(unitType)); + + final AvailableSupports friendlySupport = + AvailableSupports.getSupport( + new SupportCalculator( + List.of(supportUnit), + Set.of(unitSupportAttachment), + BattleState.Side.OFFENSE, + true)); + + final Unit enemySupportUnit = unitType.createTemp(1, player).get(0); + final UnitSupportAttachment enemyUnitSupportAttachment = + givenUnitSupportAttachment(gameData, unitType, "test2") + .setBonus(-2) + .setPlayers(List.of(player)) + .setUnitType(Set.of(unitType)); + + final AvailableSupports enemySupport = + AvailableSupports.getSupport( + new SupportCalculator( + List.of(enemySupportUnit), + Set.of(enemyUnitSupportAttachment), + BattleState.Side.OFFENSE, + true)); + final AirBattleOffenseCombatValue.AirBattleOffenseStrength strength = - new AirBattleOffenseCombatValue.AirBattleOffenseStrength(6); - assertThat("Air attack is 3", strength.getStrength(unit).getValue(), is(3)); + new AirBattleOffenseCombatValue.AirBattleOffenseStrength( + 6, friendlySupport, enemySupport); + assertThat( + "Strength starts at 3, friendly adds 3, enemy removes 2: total 4", + strength.getStrength(unit).getValue(), + is(4)); + } + + UnitSupportAttachment givenUnitSupportAttachment( + final GameData gameData, final UnitType unitType, final String name) + throws GameParseException { + return new UnitSupportAttachment("rule" + name, unitType, gameData) + .setBonus(1) + .setBonusType("bonus" + name) + .setDice("airStrength") + .setNumber(1) + .setSide("offence") + .setFaction("allied"); } @Test - void limitsToDiceSides() { + void limitsToDiceSides() { final GameData gameData = givenGameData().withDiceSides(6).build(); final GamePlayer player = mock(GamePlayer.class); @@ -48,12 +102,70 @@ void limitsToDiceSides() { final Unit unit = unitType.createTemp(1, player).get(0); unit.getUnitAttachment().setAirAttack(8); + final AirBattleOffenseCombatValue.AirBattleOffenseStrength strength = - new AirBattleOffenseCombatValue.AirBattleOffenseStrength(6); + new AirBattleOffenseCombatValue.AirBattleOffenseStrength(6, AvailableSupports.EMPTY_RESULT, AvailableSupports.EMPTY_RESULT); + assertThat( + "Air defense is 8 but dice sides is 6 so it is limited to 6", + strength.getStrength(unit).getValue(), + is(6)); + } + + @Test + void calculatesSupportUsed() throws GameParseException { + final GameData gameData = givenGameData().withDiceSides(6).build(); + + final GamePlayer player = mock(GamePlayer.class); + + final UnitType unitType = new UnitType("test", gameData); + final UnitAttachment unitAttachment = new UnitAttachment("attachment", unitType, gameData); + unitType.addAttachment(UNIT_ATTACHMENT_NAME, unitAttachment); + final Unit unit = unitType.createTemp(1, player).get(0); + unit.getUnitAttachment().setAirAttack(3); + + final Unit supportUnit = unitType.createTemp(1, player).get(0); + final UnitSupportAttachment unitSupportAttachment = + givenUnitSupportAttachment(gameData, unitType, "test") + .setBonus(2) + .setPlayers(List.of(player)) + .setUnitType(Set.of(unitType)); + + final AvailableSupports friendlySupport = + AvailableSupports.getSupport( + new SupportCalculator( + List.of(supportUnit), + Set.of(unitSupportAttachment), + BattleState.Side.OFFENSE, + true)); + + final Unit enemySupportUnit = unitType.createTemp(1, player).get(0); + final UnitSupportAttachment enemyUnitSupportAttachment = + givenUnitSupportAttachment(gameData, unitType, "test2") + .setBonus(-1) + .setPlayers(List.of(player)) + .setUnitType(Set.of(unitType)); + + final AvailableSupports enemySupport = + AvailableSupports.getSupport( + new SupportCalculator( + List.of(enemySupportUnit), + Set.of(enemyUnitSupportAttachment), + BattleState.Side.OFFENSE, + true)); + + final AirBattleDefenseCombatValue.AirBattleDefenseStrength strength = + new AirBattleDefenseCombatValue.AirBattleDefenseStrength( + 6, friendlySupport, enemySupport); + strength.getStrength(unit); assertThat( - "Air attack is 8 but dice sides is 6 so it is limited to 6", - strength.getStrength(unit).getValue(), - is(6)); + "Friendly gave 2 and enemy gave -1", + strength.getSupportGiven(), + is( + Map.of( + supportUnit, + IntegerMap.of(Map.of(unit, 2)), + enemySupportUnit, + IntegerMap.of(Map.of(unit, -1))))); } } }