Skip to content

Commit

Permalink
hasResource to query resources
Browse files Browse the repository at this point in the history
hasResource will give mapmaker the ability to query resources
  • Loading branch information
WCSumpton committed Aug 25, 2024
1 parent 764ff62 commit 8c04c19
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,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;
Expand Down Expand Up @@ -63,6 +64,8 @@ public class RulesAttachment extends AbstractPlayerRulesAttachment {
// condition for being at war
private @Nullable Set<GamePlayer> atWarPlayers = null;
private int atWarCount = -1;
// condition for checking resources
private @Nullable String[] hasResource = 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]
Expand Down Expand Up @@ -507,6 +510,40 @@ private void resetIsAI() {
isAI = null;
}

@VisibleForTesting
public void setHasResource(final String value) throws GameParseException {
final String[] s = splitOnColon(value);
final int n = getInt(s[0]);
if (n < 1) {
throw new GameParseException("hasResource must be a positive number" + thisErrorMsg());
}
if (s.length == 1) {
hasResource = splitOnColon(value + ":PUs");
}
else {
for (int i = 1; i < s.length; 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());
}
}
hasResource = s;
}
}

private void setHasResource(final String[] value) {
hasResource = value;
}

public String[] getHasResource() {
return hasResource;
}

private void resetHasResource() {
hasResource = null;
}

private int getAtWarCount() {
return atWarCount;
}
Expand Down Expand Up @@ -757,6 +794,10 @@ public boolean isSatisfied(
if (objectiveMet && getIsAI() != null) {
objectiveMet = checkIsAI(players);
}
// check for resources
if (objectiveMet && hasResource != null) {
objectiveMet = checkHasResource(players);
}
// get attached to player
final GamePlayer playerAttachedTo = (GamePlayer) getAttachedTo();
// check for players at war
Expand Down Expand Up @@ -1064,6 +1105,18 @@ private boolean checkTechs(final GamePlayer player, final TechnologyFrontier tec
return found >= techCount;
}

@VisibleForTesting
public boolean checkHasResource(final List<GamePlayer> players) {
int itotal = 0;
for (GamePlayer player : players) {
for (int i = 1; i < hasResource.length; i++) {
final Resource resource = getData().getResourceList().getResource(hasResource[i]);
itotal += player.getResources().getQuantity(resource);
}
}
return itotal >= getInt(hasResource[0]);
}

@Override
public void validate(final GameState data) {
validateNames(alliedOwnershipTerritories);
Expand Down Expand Up @@ -1100,6 +1153,12 @@ public void validate(final GameState data) {
this::resetAtWarPlayers);
case "atWarCount":
return MutableProperty.ofReadOnly(this::getAtWarCount);
case "haveResources":
return MutableProperty.of(
this::setHasResource,
this::setHasResource,
this::getHasResource,
this::resetHasResource);
case "destroyedTUV":
return MutableProperty.ofString(
this::setDestroyedTuv, this::getDestroyedTuv, this::resetDestroyedTuv);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
package games.strategy.triplea.attachments;

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 static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
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.PlayerList;
import games.strategy.engine.data.Resource;
import games.strategy.engine.data.ResourceList;
import games.strategy.engine.data.gameparser.GameParseException;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
Expand All @@ -19,12 +26,12 @@
@ExtendWith(MockitoExtension.class)
class RulesAttachmentTest {

@Mock private PlayerList playerList;

private final GameData gameData = mock(GameData.class);

private final RulesAttachment attachment = new RulesAttachment("Test attachment", null, gameData);

@Mock private PlayerList playerList;

@Mock private GamePlayer player1;
@Mock private GamePlayer player2;
@Mock private GamePlayer player3;
Expand All @@ -35,52 +42,98 @@ void setUp() {
when(gameData.getPlayerList()).thenReturn(playerList);
}

/* Testing stored getIsAI values with setIsAI */
@Test
void isAITest() {
attachment.setIsAI("true");
assertTrue(attachment.getIsAI());
attachment.setIsAI("false");
assertFalse(attachment.getIsAI());
@Nested
class hasResource {

@Mock private ResourceList resourceList;

private final String player1String = "Player1";
private final String resource1String = "Resource1";

@Mock private Resource resource1;

@BeforeEach
void setUp() {
when(gameData.getResourceList()).thenReturn(resourceList);
lenient().when(playerList.getPlayerId(player1String)).thenReturn(player1);
lenient().when(resourceList.getResource(resource1String)).thenReturn(resource1);
}

/* Testing setHaveResources with invalid arguments */
@Test
void setHasResourceInvalidArgs() {
assertThrows(
IllegalArgumentException.class,
() -> attachment.setHasResource("NOT A NUMBER:resource1String"));
verify(resourceList, times(0)).getResource(resource1String);
assertThrows(
GameParseException.class,
() -> attachment.setHasResource("0:resource1String"));
verify(resourceList, times(0)).getResource(resource1String);
assertThrows(
GameParseException.class,
() -> attachment.setHasResource("1:NOT A RESOURCE"));
verify(resourceList).getResource("NOT A RESOURCE");
assertThrows(
IllegalArgumentException.class, () -> attachment.setHasResource("q:w"));
}

private String concatWithColon(final String... args) {
return String.join(":", args);
}

}

/* Testing returned values for checkGetIsAI */
@Test
void checkGetIsAITest() {
lenient().when(player1.isAi()).thenReturn(false);
lenient().when(player2.isAi()).thenReturn(true);
lenient().when(player3.isAi()).thenReturn(true);
lenient().when(player4.isAi()).thenReturn(false);

/* Testing with 1 non AI player */
final List<GamePlayer> players1 = List.of(player1);
attachment.setIsAI("true");
assertFalse(attachment.checkIsAI(players1));
attachment.setIsAI("false");
assertTrue(attachment.checkIsAI(players1));
/* Testing with 1 AI player */
final List<GamePlayer> players2 = List.of(player2);
attachment.setIsAI("true");
assertTrue(attachment.checkIsAI(players2));
attachment.setIsAI("false");
assertFalse(attachment.checkIsAI(players2));
/* Testing with 1 non AI player and 1 AI player */
final List<GamePlayer> players12 = List.of(player1, player2);
attachment.setIsAI("true");
assertFalse(attachment.checkIsAI(players12));
attachment.setIsAI("false");
assertFalse(attachment.checkIsAI(players12));
/* Testing with 2 AI players */
final List<GamePlayer> players23 = List.of(player2, player3);
attachment.setIsAI("true");
assertTrue(attachment.checkIsAI(players23));
attachment.setIsAI("false");
assertFalse(attachment.checkIsAI(players23));
/* Testing with 2 non AI players */
final List<GamePlayer> players14 = List.of(player1, player4);
attachment.setIsAI("true");
assertFalse(attachment.checkIsAI(players14));
attachment.setIsAI("false");
assertTrue(attachment.checkIsAI(players14));
@Nested
class isAI {

/* Testing stored getIsAI values with setIsAI */
@Test
void isAITest() {
attachment.setIsAI("true");
assertTrue(attachment.getIsAI());
attachment.setIsAI("false");
assertFalse(attachment.getIsAI());
}

/* Testing returned values for checkGetIsAI */
@Test
void checkGetIsAITest() {
lenient().when(player1.isAi()).thenReturn(false);
lenient().when(player2.isAi()).thenReturn(true);
lenient().when(player3.isAi()).thenReturn(true);
lenient().when(player4.isAi()).thenReturn(false);

/* Testing with 1 non AI player */
final List<GamePlayer> players1 = List.of(player1);
attachment.setIsAI("true");
assertFalse(attachment.checkIsAI(players1));
attachment.setIsAI("false");
assertTrue(attachment.checkIsAI(players1));
/* Testing with 1 AI player */
final List<GamePlayer> players2 = List.of(player2);
attachment.setIsAI("true");
assertTrue(attachment.checkIsAI(players2));
attachment.setIsAI("false");
assertFalse(attachment.checkIsAI(players2));
/* Testing with 1 non AI player and 1 AI player */
final List<GamePlayer> players12 = List.of(player1, player2);
attachment.setIsAI("true");
assertFalse(attachment.checkIsAI(players12));
attachment.setIsAI("false");
assertFalse(attachment.checkIsAI(players12));
/* Testing with 2 AI players */
final List<GamePlayer> players23 = List.of(player2, player3);
attachment.setIsAI("true");
assertTrue(attachment.checkIsAI(players23));
attachment.setIsAI("false");
assertFalse(attachment.checkIsAI(players23));
/* Testing with 2 non AI players */
final List<GamePlayer> players14 = List.of(player1, player4);
attachment.setIsAI("true");
assertFalse(attachment.checkIsAI(players14));
attachment.setIsAI("false");
assertTrue(attachment.checkIsAI(players14));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertFalse;

import games.strategy.engine.data.GameData;
import games.strategy.engine.data.GamePlayer;
Expand All @@ -21,8 +22,10 @@
import games.strategy.engine.data.Unit;
import games.strategy.engine.data.UnitType;
import games.strategy.engine.data.changefactory.ChangeFactory;
import games.strategy.engine.data.gameparser.GameParseException;
import games.strategy.engine.delegate.IDelegateBridge;
import games.strategy.triplea.Constants;
import games.strategy.triplea.attachments.RulesAttachment;
import games.strategy.triplea.delegate.move.validation.MoveValidator;
import games.strategy.triplea.xml.TestMapGameData;
import java.util.Collection;
Expand All @@ -40,6 +43,7 @@
* 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.
*/
/* Added testing for hasResource 8/22/24 */
class VictoryTest {
private final GameData gameData = TestMapGameData.VICTORY_TEST.getGameData();
private final GamePlayer italians = GameDataTestUtil.italians(gameData);
Expand All @@ -64,6 +68,8 @@ class VictoryTest {
private MoveDelegate moveDelegate;
private PurchaseDelegate purchaseDelegate;

private final RulesAttachment attachment = new RulesAttachment("Test attachment", null, gameData);

@BeforeEach
void setUp() {
testBridge = newDelegateBridge(italians);
Expand Down Expand Up @@ -529,4 +535,50 @@ void testNoPuResourcesToPurchase() {
assertNull(error);
assertEquals(italianResources, italians.getResources().getResourcesCopy());
}

@Test
void testHasResourceWithOnePlayer() throws GameParseException {
final List<GamePlayer> players1 = List.of(italians);
/* italian starts with 16 PUs, 50 Fuel and 20 Ore */
/* testing default value "PUs" for hasResource */
attachment.setHasResource("15");
assertTrue(attachment.checkHasResource(players1));
attachment.setHasResource("20");
assertFalse(attachment.checkHasResource(players1));
/* testing for one resource */
attachment.setHasResource("20:Ore");
assertTrue(attachment.checkHasResource(players1));
attachment.setHasResource("25:Ore");
assertFalse(attachment.checkHasResource(players1));
/* testing multiple resources */
attachment.setHasResource("35:PUs:Ore");
assertTrue(attachment.checkHasResource(players1));
attachment.setHasResource("40:PUs:Ore");
assertFalse(attachment.checkHasResource(players1));
}

@Test
void testHasResourceWithTwoPlayers() throws GameParseException {
final List<GamePlayer> players2 = List.of(italians, germans);
/* italian starts with 16 PUs, 50 Fuel and 20 Ore */
/* germany starts with 40 PUs */
/* testing default value "PUs" for hasResource */
/* 16 + 40 */
attachment.setHasResource("55");
assertTrue(attachment.checkHasResource(players2));
attachment.setHasResource("60");
assertFalse(attachment.checkHasResource(players2));
/* testing for one resource */
/* 20 + 0 */
attachment.setHasResource("20:Ore");
assertTrue(attachment.checkHasResource(players2));
attachment.setHasResource("25:Ore");
assertFalse(attachment.checkHasResource(players2));
/* testing multiple resources */
/* 16 + 40 + 20 + 0 */
attachment.setHasResource("75:PUs:Ore");
assertTrue(attachment.checkHasResource(players2));
attachment.setHasResource("80:PUs:Ore");
assertFalse(attachment.checkHasResource(players2));
}
}

1 comment on commit 8c04c19

@WCSumpton
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasResource allows the mapmakers to query resources.

value is the resource. Use ":" to enter more than one resource. If more than one resource is used their values will total together. PUs is the dafault value count is the value resource must be equal to or greater then to be true. PUs is the default value . If players option is used all values are added together.

Cheers...

Please sign in to comment.