Fixed a persistent issue with testing stacked cards via the test rig (finally). Improved ChooseANumber to use value resolvers.

This commit is contained in:
Christian 'ketura' McCarty
2022-07-21 07:35:14 -05:00
parent 6319bd3820
commit d7e7fcfaa4
4 changed files with 120 additions and 50 deletions

View File

@@ -1,13 +1,11 @@
package com.gempukku.lotro.cards.build.field.effect.appender; package com.gempukku.lotro.cards.build.field.effect.appender;
import com.gempukku.lotro.cards.build.ActionContext; import com.gempukku.lotro.cards.build.*;
import com.gempukku.lotro.cards.build.CardGenerationEnvironment;
import com.gempukku.lotro.cards.build.InvalidCardDefinitionException;
import com.gempukku.lotro.cards.build.PlayerSource;
import com.gempukku.lotro.cards.build.field.FieldUtils; import com.gempukku.lotro.cards.build.field.FieldUtils;
import com.gempukku.lotro.cards.build.field.effect.EffectAppender; import com.gempukku.lotro.cards.build.field.effect.EffectAppender;
import com.gempukku.lotro.cards.build.field.effect.EffectAppenderProducer; import com.gempukku.lotro.cards.build.field.effect.EffectAppenderProducer;
import com.gempukku.lotro.cards.build.field.effect.appender.resolver.PlayerResolver; import com.gempukku.lotro.cards.build.field.effect.appender.resolver.PlayerResolver;
import com.gempukku.lotro.cards.build.field.effect.appender.resolver.ValueResolver;
import com.gempukku.lotro.logic.actions.CostToEffectAction; import com.gempukku.lotro.logic.actions.CostToEffectAction;
import com.gempukku.lotro.logic.decisions.DecisionResultInvalidException; import com.gempukku.lotro.logic.decisions.DecisionResultInvalidException;
import com.gempukku.lotro.logic.decisions.IntegerAwaitingDecision; import com.gempukku.lotro.logic.decisions.IntegerAwaitingDecision;
@@ -22,8 +20,9 @@ public class ChooseANumber implements EffectAppenderProducer {
final String player = FieldUtils.getString(effectObject.get("player"), "player", "you"); final String player = FieldUtils.getString(effectObject.get("player"), "player", "you");
final String displayText = FieldUtils.getString(effectObject.get("text"), "text", "Choose a number"); final String displayText = FieldUtils.getString(effectObject.get("text"), "text", "Choose a number");
final int from = FieldUtils.getInteger(effectObject.get("from"), "from", 0); final ValueSource fromSource = ValueResolver.resolveEvaluator(effectObject.get("from"), 0, environment);
final int to = FieldUtils.getInteger(effectObject.get("to"), "to", 1); final ValueSource toSource = ValueResolver.resolveEvaluator(effectObject.get("to"), 1, environment);
final String memorize = FieldUtils.getString(effectObject.get("memorize"), "memorize"); final String memorize = FieldUtils.getString(effectObject.get("memorize"), "memorize");
final PlayerSource playerSource = PlayerResolver.resolvePlayer(player, environment); final PlayerSource playerSource = PlayerResolver.resolvePlayer(player, environment);
@@ -35,7 +34,10 @@ public class ChooseANumber implements EffectAppenderProducer {
@Override @Override
protected Effect createEffect(boolean cost, CostToEffectAction action, ActionContext actionContext) { protected Effect createEffect(boolean cost, CostToEffectAction action, ActionContext actionContext) {
return new PlayoutDecisionEffect(actionContext.getPerformingPlayer(), return new PlayoutDecisionEffect(actionContext.getPerformingPlayer(),
new IntegerAwaitingDecision(1, displayText, from, to) { new IntegerAwaitingDecision(1, displayText,
fromSource.getEvaluator(actionContext).evaluateExpression(actionContext.getGame(), null),
toSource.getEvaluator(actionContext).evaluateExpression(actionContext.getGame(), null))
{
@Override @Override
public void decisionMade(String result) throws DecisionResultInvalidException { public void decisionMade(String result) throws DecisionResultInvalidException {
actionContext.setValueToMemory(memorize, String.valueOf(getValidatedResult(result))); actionContext.setValueToMemory(memorize, String.valueOf(getValidatedResult(result)));

View File

@@ -46,7 +46,8 @@ public class CardResolver {
if (type.startsWith("memory(") && type.endsWith(")")) { if (type.startsWith("memory(") && type.endsWith(")")) {
return resolveMemoryCards(type, choiceFilter, playabilityFilter, countSource, memory, cardSource); return resolveMemoryCards(type, choiceFilter, playabilityFilter, countSource, memory, cardSource);
} else if (type.startsWith("choose(") && type.endsWith(")")) { }
else if (type.startsWith("choose(") && type.endsWith(")")) {
final PlayerSource playerSource = PlayerResolver.resolvePlayer(choicePlayer, environment); final PlayerSource playerSource = PlayerResolver.resolvePlayer(choicePlayer, environment);
ChoiceEffectSource effectSource = (possibleCards, action, actionContext, min, max) -> { ChoiceEffectSource effectSource = (possibleCards, action, actionContext, min, max) -> {
String choicePlayerId = playerSource.getPlayer(actionContext); String choicePlayerId = playerSource.getPlayer(actionContext);

View File

@@ -55,13 +55,15 @@ public class GenericCardTestHelper extends AbstractAtTest {
// Player key, then name/card // Player key, then name/card
public Map<String, Map<String, PhysicalCardImpl>> Cards = new HashMap<>(); public Map<String, Map<String, PhysicalCardImpl>> Cards = new HashMap<>();
public GenericCardTestHelper(Map<String, String> cardIDs) throws CardNotFoundException, DecisionResultInvalidException { public GenericCardTestHelper(HashMap<String, String> cardIDs) throws CardNotFoundException, DecisionResultInvalidException {
this(cardIDs, null, null, null); this(cardIDs, null, null, null);
} }
public GenericCardTestHelper(Map<String, String> cardIDs, Map<String, String> siteIDs, String ringBearerID, String ringID) throws CardNotFoundException, DecisionResultInvalidException { public GenericCardTestHelper(HashMap<String, String> cardIDs, HashMap<String, String> siteIDs, String ringBearerID, String ringID) throws CardNotFoundException, DecisionResultInvalidException {
super(); super();
if(siteIDs == null || ringBearerID == null || ringID == null) { if(siteIDs == null || ringBearerID == null || ringID == null) {
initializeSimplestGame(); initializeSimplestGame();
} }
@@ -91,20 +93,17 @@ public class GenericCardTestHelper extends AbstractAtTest {
if(cardIDs != null) { if(cardIDs != null) {
for(String name : cardIDs.keySet()) { for(String name : cardIDs.keySet()) {
String id = cardIDs.get(name); String id = cardIDs.get(name);
PhysicalCardImpl card = CreateCard(P1, id); PhysicalCardImpl card = createCard(P1, id);
Cards.get(P1).put(name, card); Cards.get(P1).put(name, card);
FreepsMoveCardsToTopOfDeck(card); FreepsMoveCardsToTopOfDeck(card);
card = CreateCard(P2, id); card = createCard(P2, id);
Cards.get(P2).put(name, card); Cards.get(P2).put(name, card);
FreepsMoveCardsToTopOfDeck(card); FreepsMoveCardsToTopOfDeck(card);
} }
} }
} }
public PhysicalCardImpl CreateCard(String playerID, String cardID) throws CardNotFoundException {
return new PhysicalCardImpl(LastCardID++, cardID, playerID, _library.getLotroCardBlueprint(cardID));
}
public void StartGame() throws DecisionResultInvalidException { public void StartGame() throws DecisionResultInvalidException {
skipMulligans(); skipMulligans();
@@ -256,10 +255,9 @@ public class GenericCardTestHelper extends AbstractAtTest {
public int GetFreepsDiscardCount() { return GetPlayerDiscardCount(P1); } public int GetFreepsDiscardCount() { return GetPlayerDiscardCount(P1); }
public int GetShadowDiscardCount() { return GetPlayerDiscardCount(P2); } public int GetShadowDiscardCount() { return GetPlayerDiscardCount(P2); }
public int GetPlayerDiscardCount(String player) public int GetPlayerDiscardCount(String player) { return _game.getGameState().getDiscard(player).size(); }
{
return _game.getGameState().getDiscard(player).size(); public int GetFreepsDeadCount() { return _game.getGameState().getDeadPile(P1).size(); }
}
public Phase GetCurrentPhase() { return _game.getGameState().getCurrentPhase(); } public Phase GetCurrentPhase() { return _game.getGameState().getCurrentPhase(); }
@@ -360,6 +358,8 @@ public class GenericCardTestHelper extends AbstractAtTest {
RemoveCardZone(card.getOwner(), card); RemoveCardZone(card.getOwner(), card);
_game.getGameState().putCardOnTopOfDeck(card); _game.getGameState().putCardOnTopOfDeck(card);
}); });
ShuffleFreepsDeck();
} }
public void ShadowShuffleCardsInDeck(String...cardNames) { public void ShadowShuffleCardsInDeck(String...cardNames) {
Arrays.stream(cardNames).forEach(cardName -> ShadowShuffleCardsInDeck(GetShadowCard(cardName))); Arrays.stream(cardNames).forEach(cardName -> ShadowShuffleCardsInDeck(GetShadowCard(cardName)));
@@ -369,6 +369,14 @@ public class GenericCardTestHelper extends AbstractAtTest {
RemoveCardZone(card.getOwner(), card); RemoveCardZone(card.getOwner(), card);
_game.getGameState().putCardOnTopOfDeck(card); _game.getGameState().putCardOnTopOfDeck(card);
}); });
ShuffleShadowDeck();
}
public void ShuffleFreepsDeck() { ShuffleDeck(P1); }
public void ShuffleShadowDeck() { ShuffleDeck(P2); }
public void ShuffleDeck(String player) {
_game.getGameState().shuffleDeck(player);
} }
public void FreepsMoveCharToTable(String...names) { public void FreepsMoveCharToTable(String...names) {
@@ -385,22 +393,43 @@ public class GenericCardTestHelper extends AbstractAtTest {
} }
public void FreepsMoveCardToSupportArea(String cardName) { FreepsMoveCardToSupportArea(GetFreepsCard(cardName)); } public void FreepsMoveCardToSupportArea(String...names) {
Arrays.stream(names).forEach(name -> FreepsMoveCardToSupportArea(GetFreepsCard(name)));
}
public void FreepsMoveCardToSupportArea(PhysicalCardImpl...cards) { public void FreepsMoveCardToSupportArea(PhysicalCardImpl...cards) {
Arrays.stream(cards).forEach(card -> MoveCardToZone(P1, card, Zone.SUPPORT)); Arrays.stream(cards).forEach(card -> MoveCardToZone(P1, card, Zone.SUPPORT));
} }
public void ShadowMoveCardToSupportArea(String cardName) { ShadowMoveCardToSupportArea(GetShadowCard(cardName)); } public void ShadowMoveCardToSupportArea(String...names) {
public void ShadowMoveCardToSupportArea(PhysicalCardImpl...cards) { Arrays.stream(names).forEach(name -> ShadowMoveCardToSupportArea(GetFreepsCard(name)));
Arrays.stream(cards).forEach(card -> MoveCardToZone(P1, card, Zone.SUPPORT)); }
public void ShadowMoveCardToSupportArea(PhysicalCardImpl...cards) {
Arrays.stream(cards).forEach(card -> MoveCardToZone(P2, card, Zone.SUPPORT));
}
public void FreepsMoveCardToDiscard(String...names) {
Arrays.stream(names).forEach(name -> FreepsMoveCardToDiscard(GetFreepsCard(name)));
} }
public void FreepsMoveCardToDiscard(String cardName) { FreepsMoveCardToDiscard(GetFreepsCard(cardName)); }
public void FreepsMoveCardToDiscard(PhysicalCardImpl...cards) { public void FreepsMoveCardToDiscard(PhysicalCardImpl...cards) {
Arrays.stream(cards).forEach(card -> MoveCardToZone(P1, card, Zone.DISCARD)); Arrays.stream(cards).forEach(card -> MoveCardToZone(P1, card, Zone.DISCARD));
} }
public void ShadowMoveCardToDiscard(String cardName) { ShadowMoveCardToDiscard(GetShadowCard(cardName)); } public void ShadowMoveCardToDiscard(String...names) {
Arrays.stream(names).forEach(name -> ShadowMoveCardToDiscard(GetFreepsCard(name)));
}
public void ShadowMoveCardToDiscard(PhysicalCardImpl...cards) { public void ShadowMoveCardToDiscard(PhysicalCardImpl...cards) {
Arrays.stream(cards).forEach(card -> MoveCardToZone(P1, card, Zone.DISCARD)); Arrays.stream(cards).forEach(card -> MoveCardToZone(P2, card, Zone.DISCARD));
}
public void FreepsMoveCardToDeadPile(String...names) {
Arrays.stream(names).forEach(name -> FreepsMoveCardToDeadPile(GetFreepsCard(name)));
}
public void FreepsMoveCardToDeadPile(PhysicalCardImpl...cards) {
Arrays.stream(cards).forEach(card -> MoveCardToZone(P1, card, Zone.DEAD));
}
public void ShadowMoveCardToDeadPile(String...names) {
Arrays.stream(names).forEach(name -> ShadowMoveCardToDeadPile(GetFreepsCard(name)));
}
public void ShadowMoveCardToDeadPile(PhysicalCardImpl...cards) {
Arrays.stream(cards).forEach(card -> MoveCardToZone(P2, card, Zone.DEAD));
} }
@@ -557,25 +586,27 @@ public class GenericCardTestHelper extends AbstractAtTest {
public int GetFreepsCardChoiceCount() { return FreepsGetCardChoices().size(); } public int GetFreepsCardChoiceCount() { return FreepsGetCardChoices().size(); }
public int GetShadowCardChoiceCount() { return ShadowGetCardChoices().size(); } public int GetShadowCardChoiceCount() { return ShadowGetCardChoices().size(); }
public void FreepsChooseCardBPFromSelection(PhysicalCardImpl card) throws DecisionResultInvalidException { ChooseCardBPFromSelection(P1, card);} public void FreepsChooseCardBPFromSelection(PhysicalCardImpl...cards) throws DecisionResultInvalidException { ChooseCardBPFromSelection(P1, cards);}
public void ShadowChooseCardBPFromSelection(PhysicalCardImpl card) throws DecisionResultInvalidException { ChooseCardBPFromSelection(P2, card);} public void ShadowChooseCardBPFromSelection(PhysicalCardImpl...cards) throws DecisionResultInvalidException { ChooseCardBPFromSelection(P2, cards);}
public void ChooseCardBPFromSelection(String player, PhysicalCardImpl card) throws DecisionResultInvalidException { public void ChooseCardBPFromSelection(String player, PhysicalCardImpl...cards) throws DecisionResultInvalidException {
AwaitingDecision decision = _userFeedback.getAwaitingDecision(player); String[] choices = GetAwaitingDecisionParam(player,"blueprintId");
ArrayList<String> bps = new ArrayList<String>();
String[] ids = GetAwaitingDecisionParam(player,"blueprintId"); for(int i = 0; i < choices.length; i++)
for(int i = 0; i < ids.length; i++)
{ {
String id = ids[i]; for(PhysicalCardImpl card : cards)
if(id == card.getBlueprintId())
{ {
// I have no idea why the spacing is required, but the BP parser skips to the fourth position if(card.getBlueprintId() == choices[i])
playerDecided(player, " " + i); {
return; // I have no idea why the spacing is required, but the BP parser skips to the fourth position
bps.add(" " + i);
break;
}
} }
} }
playerDecided(player, card.getBlueprintId()); playerDecided(player, String.join(",", bps));
} }
public void FreepsChooseCardIDFromSelection(PhysicalCardImpl card) throws DecisionResultInvalidException { ChooseCardIDFromSelection(P1, card);} public void FreepsChooseCardIDFromSelection(PhysicalCardImpl card) throws DecisionResultInvalidException { ChooseCardIDFromSelection(P1, card);}

View File

@@ -112,19 +112,18 @@ public class Card_09_039_Tests
PhysicalCardImpl library = scn.GetShadowCard("library"); PhysicalCardImpl library = scn.GetShadowCard("library");
PhysicalCardImpl troop1 = scn.GetShadowCard("troop1"); PhysicalCardImpl troop1 = scn.GetShadowCard("troop1");
PhysicalCardImpl troop2 = scn.GetShadowCard("troop2");
PhysicalCardImpl axe1 = scn.GetShadowCard("axe1"); PhysicalCardImpl axe1 = scn.GetShadowCard("axe1");
scn.ShadowMoveCardToHand(library, troop1); scn.ShadowMoveCardToSupportArea(library);
scn.ShadowMoveCharToTable(troop1);
scn.StackCardsOn(library, axe1, troop2);
scn.StartGame(); scn.StartGame();
scn.SetTwilight(20); scn.SetTwilight(20);
scn.FreepsSkipCurrentPhaseAction(); scn.FreepsSkipCurrentPhaseAction();
scn.ShadowPlayCard(library);
scn.ShadowUseCardAction(library);
assertEquals(library, axe1.getStackedOn());
scn.SkipToPhase(Phase.ASSIGNMENT); scn.SkipToPhase(Phase.ASSIGNMENT);
scn.SkipCurrentPhaseActions(); scn.SkipCurrentPhaseActions();
scn.FreepsAssignToMinions(scn.GetRingBearer(), troop1); scn.FreepsAssignToMinions(scn.GetRingBearer(), troop1);
@@ -132,17 +131,54 @@ public class Card_09_039_Tests
scn.FreepsSkipCurrentPhaseAction(); scn.FreepsSkipCurrentPhaseAction();
assertTrue(scn.ShadowCardActionAvailable(library)); assertTrue(scn.ShadowCardActionAvailable(library));
// 20 initially, +2 from move, -2 from library, -5 for troop, -2 for roaming // 20 initially, +2 from move, the rest were all cheated in
assertEquals(13, scn.GetTwilight()); assertEquals(22, scn.GetTwilight());
assertEquals(9, scn.GetStrength(troop1)); assertEquals(9, scn.GetStrength(troop1));
assertEquals(1, scn.GetStackedCards(library).size()); assertEquals(2, scn.GetStackedCards(library).size());
scn.ShadowUseCardAction(library); scn.ShadowUseCardAction(library);
assertEquals(13, scn.GetTwilight()); assertFalse(scn.ShadowAnyDecisionsAvailable()); // only 1 valid choice even with 2 stacked, so should have no decision
assertEquals(9, scn.GetStrength(troop1)); assertEquals(21, scn.GetTwilight());
assertEquals(0, scn.GetStackedCards(library).size()); assertEquals(10, scn.GetStrength(troop1));
assertEquals(1, scn.GetStackedCards(library).size());
assertEquals(Zone.DISCARD, axe1.getZone()); assertEquals(Zone.DISCARD, axe1.getZone());
} }
@Test
public void RegroupAbilityRemoves1AndTakesStackedIsengardCardIntoHand() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
GenericCardTestHelper scn = GetScenario();
PhysicalCardImpl library = scn.GetShadowCard("library");
PhysicalCardImpl troop1 = scn.GetShadowCard("troop1");
PhysicalCardImpl runner = scn.GetShadowCard("runner");
PhysicalCardImpl axe1 = scn.GetShadowCard("axe1");
scn.ShadowMoveCardToSupportArea(library);
scn.StackCardsOn(library, axe1, runner, troop1);
scn.StartGame();
scn.SetTwilight(20);
scn.FreepsSkipCurrentPhaseAction();
scn.SkipToPhase(Phase.REGROUP);
scn.FreepsSkipCurrentPhaseAction();
assertTrue(scn.ShadowCardActionAvailable(library));
// 20 initially, +2 from move, the rest were all cheated in
assertEquals(22, scn.GetTwilight());
assertEquals(0, scn.GetShadowHandCount());
assertEquals(3, scn.GetStackedCards(library).size());
scn.ShadowUseCardAction(library);
assertFalse(scn.ShadowAnyDecisionsAvailable()); // only 1 valid choice even with 3 stacked, so should have no decision
assertEquals(21, scn.GetTwilight());
assertEquals(1, scn.GetShadowHandCount());
assertEquals(2, scn.GetStackedCards(library).size());
assertEquals(Zone.HAND, troop1.getZone());
}