mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
test framework improves:
- now game logs will show stack ability on push and on resolve (before any choices); - now game logs will show used choices made by cast/activate, setChoice, setMode and addTarget commands (not work for AI tests, part of #13832); - improved choice logic for modes and yes/not dialogs (now it's use a more strictly checks, use TestPlayer.MODE_SKIP to stop mode selection); - improved error logs and testable dialogs menu in cheat mode;
This commit is contained in:
parent
a7a6ffd6f3
commit
e866707912
17 changed files with 553 additions and 410 deletions
|
|
@ -7,6 +7,7 @@ import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.Target;
|
import mage.target.Target;
|
||||||
import mage.target.TargetPermanent;
|
import mage.target.TargetPermanent;
|
||||||
|
import mage.target.TargetPlayer;
|
||||||
import mage.target.common.TargetPermanentOrPlayer;
|
import mage.target.common.TargetPermanentOrPlayer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -76,6 +77,10 @@ abstract class BaseTestableDialog implements TestableDialog {
|
||||||
return createAnyTarget(min, max, false);
|
return createAnyTarget(min, max, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Target createPlayerTarget(int min, int max, boolean notTarget) {
|
||||||
|
return new TargetPlayer(min, max, notTarget);
|
||||||
|
}
|
||||||
|
|
||||||
private static Target createAnyTarget(int min, int max, boolean notTarget) {
|
private static Target createAnyTarget(int min, int max, boolean notTarget) {
|
||||||
return new TargetPermanentOrPlayer(min, max).withNotTarget(notTarget);
|
return new TargetPermanentOrPlayer(min, max).withNotTarget(notTarget);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,19 @@ class ChooseTargetTestableDialog extends BaseTestableDialog {
|
||||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 1-3", createImpossibleTarget(1, 3)).aiMustChoose(false, 0));
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 1-3", createImpossibleTarget(1, 3)).aiMustChoose(false, 0));
|
||||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 2-3", createImpossibleTarget(2, 3)).aiMustChoose(false, 0));
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 2-3", createImpossibleTarget(2, 3)).aiMustChoose(false, 0));
|
||||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible max", createImpossibleTarget(0, Integer.MAX_VALUE)).aiMustChoose(false, 0));
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible max", createImpossibleTarget(0, Integer.MAX_VALUE)).aiMustChoose(false, 0));
|
||||||
|
//
|
||||||
|
// additional tests for 2 possible options limitation
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 0", createPlayerTarget(0, 0, notTarget)).aiMustChoose(false, 0));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 0-1", createPlayerTarget(0, 1, notTarget)).aiMustChoose(true, 1));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 0-2", createPlayerTarget(0, 2, notTarget)).aiMustChoose(true, 1));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 0-5", createPlayerTarget(0, 5, notTarget)).aiMustChoose(true, 1));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 1", createPlayerTarget(1, 1, notTarget)).aiMustChoose(true, 1));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 1-2", createPlayerTarget(1, 2, notTarget)).aiMustChoose(true, 1));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 1-5", createPlayerTarget(1, 5, notTarget)).aiMustChoose(true, 1));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 2", createPlayerTarget(2, 2, notTarget)).aiMustChoose(true, 2));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 2-5", createPlayerTarget(2, 5, notTarget)).aiMustChoose(true, 2));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 3", createPlayerTarget(3, 3, notTarget)).aiMustChoose(false, 0));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 3-5", createPlayerTarget(3, 5, notTarget)).aiMustChoose(false, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,7 @@ import mage.constants.Outcome;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -120,10 +117,8 @@ public class TestableDialogsRunner {
|
||||||
choice = prepareSelectDialogChoice(needGroup);
|
choice = prepareSelectDialogChoice(needGroup);
|
||||||
player.choose(Outcome.Benefit, choice, game);
|
player.choose(Outcome.Benefit, choice, game);
|
||||||
if (choice.getChoiceKey() != null) {
|
if (choice.getChoiceKey() != null) {
|
||||||
int needIndex = Integer.parseInt(choice.getChoiceKey());
|
int needRegNumber = Integer.parseInt(choice.getChoiceKey());
|
||||||
if (needIndex < this.dialogs.size()) {
|
needDialog = this.dialogs.getOrDefault(needRegNumber, null);
|
||||||
needDialog = this.dialogs.get(needIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (needDialog == null) {
|
if (needDialog == null) {
|
||||||
|
|
@ -144,15 +139,20 @@ public class TestableDialogsRunner {
|
||||||
Choice choice = new ChoiceImpl(false);
|
Choice choice = new ChoiceImpl(false);
|
||||||
choice.setMessage("Choose dialogs group to run");
|
choice.setMessage("Choose dialogs group to run");
|
||||||
|
|
||||||
|
// use min reg number for groups
|
||||||
|
Map<String, Integer> groupNumber = new HashMap<>();
|
||||||
|
this.dialogs.values().forEach(dialog -> {
|
||||||
|
groupNumber.put(dialog.getGroup(), Math.min(groupNumber.getOrDefault(dialog.getGroup(), Integer.MAX_VALUE), dialog.getRegNumber()));
|
||||||
|
});
|
||||||
|
|
||||||
// main groups
|
// main groups
|
||||||
int recNumber = 0;
|
|
||||||
for (int i = 0; i < groups.size(); i++) {
|
for (int i = 0; i < groups.size(); i++) {
|
||||||
recNumber++;
|
|
||||||
String group = groups.get(i);
|
String group = groups.get(i);
|
||||||
|
Integer groupMinNumber = groupNumber.getOrDefault(group, 0);
|
||||||
choice.withItem(
|
choice.withItem(
|
||||||
String.valueOf(i),
|
String.valueOf(i),
|
||||||
String.format("%02d. %s", recNumber, group),
|
String.format("%02d. %s", groupMinNumber, group),
|
||||||
recNumber,
|
groupMinNumber,
|
||||||
ChoiceHintType.TEXT,
|
ChoiceHintType.TEXT,
|
||||||
String.join("<br>", group)
|
String.join("<br>", group)
|
||||||
);
|
);
|
||||||
|
|
@ -186,18 +186,15 @@ public class TestableDialogsRunner {
|
||||||
private Choice prepareSelectDialogChoice(String needGroup) {
|
private Choice prepareSelectDialogChoice(String needGroup) {
|
||||||
Choice choice = new ChoiceImpl(false);
|
Choice choice = new ChoiceImpl(false);
|
||||||
choice.setMessage("Choose game dialog to run from " + needGroup);
|
choice.setMessage("Choose game dialog to run from " + needGroup);
|
||||||
int recNumber = 0;
|
for (TestableDialog dialog : this.dialogs.values()) {
|
||||||
for (int i = 0; i < this.dialogs.size(); i++) {
|
|
||||||
TestableDialog dialog = this.dialogs.get(i);
|
|
||||||
if (!dialog.getGroup().equals(needGroup)) {
|
if (!dialog.getGroup().equals(needGroup)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
recNumber++;
|
|
||||||
String info = String.format("%s - %s - %s", dialog.getGroup(), dialog.getName(), dialog.getDescription());
|
String info = String.format("%s - %s - %s", dialog.getGroup(), dialog.getName(), dialog.getDescription());
|
||||||
choice.withItem(
|
choice.withItem(
|
||||||
String.valueOf(i),
|
String.valueOf(dialog.getRegNumber()),
|
||||||
String.format("%02d. %s", recNumber, info),
|
String.format("%02d. %s", dialog.getRegNumber(), info),
|
||||||
recNumber,
|
dialog.getRegNumber(),
|
||||||
ChoiceHintType.TEXT,
|
ChoiceHintType.TEXT,
|
||||||
String.join("<br>", info)
|
String.join("<br>", info)
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -405,13 +405,13 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
||||||
if (effect != null
|
if (effect != null
|
||||||
&& stackObject.getControllerId().equals(playerId)) {
|
&& stackObject.getControllerId().equals(playerId)) {
|
||||||
Target target = effect.getTarget();
|
Target target = effect.getTarget();
|
||||||
if (!target.isChoiceCompleted(getId(), (StackAbility) stackObject, game)) {
|
if (!target.isChoiceCompleted(getId(), (StackAbility) stackObject, game, null)) {
|
||||||
for (UUID targetId : target.possibleTargets(stackObject.getControllerId(), stackObject.getStackAbility(), game)) {
|
for (UUID targetId : target.possibleTargets(stackObject.getControllerId(), stackObject.getStackAbility(), game)) {
|
||||||
Game sim = game.createSimulationForAI();
|
Game sim = game.createSimulationForAI();
|
||||||
StackAbility newAbility = (StackAbility) stackObject.copy();
|
StackAbility newAbility = (StackAbility) stackObject.copy();
|
||||||
SearchEffect newEffect = getSearchEffect(newAbility);
|
SearchEffect newEffect = getSearchEffect(newAbility);
|
||||||
newEffect.getTarget().addTarget(targetId, newAbility, sim);
|
newEffect.getTarget().addTarget(targetId, newAbility, sim);
|
||||||
sim.getStack().push(newAbility);
|
sim.getStack().push(sim, newAbility);
|
||||||
SimulationNode2 newNode = new SimulationNode2(node, sim, depth, stackObject.getControllerId());
|
SimulationNode2 newNode = new SimulationNode2(node, sim, depth, stackObject.getControllerId());
|
||||||
node.children.add(newNode);
|
node.children.add(newNode);
|
||||||
newNode.getTargets().add(targetId);
|
newNode.getTargets().add(targetId);
|
||||||
|
|
@ -886,10 +886,10 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
||||||
if (!target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (!target.isChoiceCompleted(abilityControllerId, source, game, cards)) {
|
||||||
for (UUID targetId : targets) {
|
for (UUID targetId : targets) {
|
||||||
target.addTarget(targetId, source, game);
|
target.addTarget(targetId, source, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, cards)) {
|
||||||
targets.clear();
|
targets.clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -906,10 +906,10 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
||||||
if (!target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (!target.isChoiceCompleted(abilityControllerId, source, game, cards)) {
|
||||||
for (UUID targetId : targets) {
|
for (UUID targetId : targets) {
|
||||||
target.add(targetId, game);
|
target.add(targetId, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, cards)) {
|
||||||
targets.clear();
|
targets.clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ public final class SimulatedPlayer2 extends ComputerPlayer {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void simulateOptions(Game game) {
|
private void simulateOptions(Game game) {
|
||||||
List<ActivatedAbility> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
|
List<ActivatedAbility> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
|
||||||
for (ActivatedAbility ability : playables) {
|
for (ActivatedAbility ability : playables) {
|
||||||
if (ability.isManaAbility()) {
|
if (ability.isManaAbility()) {
|
||||||
|
|
@ -176,6 +176,10 @@ public final class SimulatedPlayer2 extends ComputerPlayer {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove invalid targets
|
||||||
|
// TODO: is it useless cause it already filtered before?
|
||||||
|
options.removeIf(option -> !option.getTargets().isChosen(game));
|
||||||
|
|
||||||
if (AI_SIMULATE_ALL_BAD_AND_GOOD_TARGETS) {
|
if (AI_SIMULATE_ALL_BAD_AND_GOOD_TARGETS) {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
@ -315,7 +319,7 @@ public final class SimulatedPlayer2 extends ComputerPlayer {
|
||||||
List<Ability> options = getPlayableOptions(ability, game);
|
List<Ability> options = getPlayableOptions(ability, game);
|
||||||
if (options.isEmpty()) {
|
if (options.isEmpty()) {
|
||||||
logger.debug("simulating -- triggered ability:" + ability);
|
logger.debug("simulating -- triggered ability:" + ability);
|
||||||
game.getStack().push(new StackAbility(ability, playerId));
|
game.getStack().push(game, new StackAbility(ability, playerId));
|
||||||
if (ability.activate(game, false) && ability.isUsesStack()) {
|
if (ability.activate(game, false) && ability.isUsesStack()) {
|
||||||
game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability, ability.getControllerId()));
|
game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability, ability.getControllerId()));
|
||||||
}
|
}
|
||||||
|
|
@ -337,9 +341,9 @@ public final class SimulatedPlayer2 extends ComputerPlayer {
|
||||||
|
|
||||||
protected void addAbilityNode(SimulationNode2 parent, Ability ability, int depth, Game game) {
|
protected void addAbilityNode(SimulationNode2 parent, Ability ability, int depth, Game game) {
|
||||||
Game sim = game.createSimulationForAI();
|
Game sim = game.createSimulationForAI();
|
||||||
sim.getStack().push(new StackAbility(ability, playerId));
|
sim.getStack().push(sim, new StackAbility(ability, playerId));
|
||||||
if (ability.activate(sim, false) && ability.isUsesStack()) {
|
if (ability.activate(sim, false) && ability.isUsesStack()) {
|
||||||
game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability, ability.getControllerId()));
|
sim.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability, ability.getControllerId()));
|
||||||
}
|
}
|
||||||
sim.applyEffects();
|
sim.applyEffects();
|
||||||
SimulationNode2 newNode = new SimulationNode2(parent, sim, depth, playerId);
|
SimulationNode2 newNode = new SimulationNode2(parent, sim, depth, playerId);
|
||||||
|
|
|
||||||
|
|
@ -142,17 +142,27 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
||||||
|
|
||||||
// nothing to choose, e.g. X=0
|
// nothing to choose, e.g. X=0
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, fromCards)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// default logic for any targets
|
|
||||||
PossibleTargetsSelector possibleTargetsSelector = new PossibleTargetsSelector(outcome, target, abilityControllerId, source, game);
|
PossibleTargetsSelector possibleTargetsSelector = new PossibleTargetsSelector(outcome, target, abilityControllerId, source, game);
|
||||||
possibleTargetsSelector.findNewTargets(fromCards);
|
possibleTargetsSelector.findNewTargets(fromCards);
|
||||||
|
|
||||||
|
// nothing to choose, e.g. no valid targets
|
||||||
|
if (!possibleTargetsSelector.hasAnyTargets()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// can't choose
|
||||||
|
if (!possibleTargetsSelector.hasMinNumberOfTargets()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// good targets -- choose as much as possible
|
// good targets -- choose as much as possible
|
||||||
for (MageItem item : possibleTargetsSelector.getGoodTargets()) {
|
for (MageItem item : possibleTargetsSelector.getGoodTargets()) {
|
||||||
target.add(item.getId(), game);
|
target.add(item.getId(), game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, fromCards)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -226,7 +236,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
||||||
|
|
||||||
// nothing to choose, e.g. X=0
|
// nothing to choose, e.g. X=0
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -238,6 +248,11 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// can't choose
|
||||||
|
if (!possibleTargetsSelector.hasMinNumberOfTargets()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// KILL PRIORITY
|
// KILL PRIORITY
|
||||||
if (outcome == Outcome.Damage) {
|
if (outcome == Outcome.Damage) {
|
||||||
// opponent first
|
// opponent first
|
||||||
|
|
@ -251,7 +266,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
int leftLife = PossibleTargetsComparator.getLifeForDamage(item, game);
|
int leftLife = PossibleTargetsComparator.getLifeForDamage(item, game);
|
||||||
if (leftLife > 0 && leftLife <= target.getAmountRemaining()) {
|
if (leftLife > 0 && leftLife <= target.getAmountRemaining()) {
|
||||||
target.addTarget(item.getId(), leftLife, source, game);
|
target.addTarget(item.getId(), leftLife, source, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -268,7 +283,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
int leftLife = PossibleTargetsComparator.getLifeForDamage(item, game);
|
int leftLife = PossibleTargetsComparator.getLifeForDamage(item, game);
|
||||||
if (leftLife > 0 && leftLife <= target.getAmountRemaining()) {
|
if (leftLife > 0 && leftLife <= target.getAmountRemaining()) {
|
||||||
target.addTarget(item.getId(), leftLife, source, game);
|
target.addTarget(item.getId(), leftLife, source, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -283,7 +298,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -303,7 +318,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
int leftLife = PossibleTargetsComparator.getLifeForDamage(item, game);
|
int leftLife = PossibleTargetsComparator.getLifeForDamage(item, game);
|
||||||
if (leftLife > 1) {
|
if (leftLife > 1) {
|
||||||
target.addTarget(item.getId(), Math.min(leftLife - 1, target.getAmountRemaining()), source, game);
|
target.addTarget(item.getId(), Math.min(leftLife - 1, target.getAmountRemaining()), source, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -322,7 +337,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
return !target.getTargets().isEmpty();
|
return !target.getTargets().isEmpty();
|
||||||
}
|
}
|
||||||
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -339,7 +354,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -355,7 +370,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
return !target.getTargets().isEmpty();
|
return !target.getTargets().isEmpty();
|
||||||
}
|
}
|
||||||
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,6 @@ public class PossibleTargetsSelector {
|
||||||
// collect new valid targets
|
// collect new valid targets
|
||||||
List<MageItem> found = target.possibleTargets(abilityControllerId, source, game, fromTargetsList).stream()
|
List<MageItem> found = target.possibleTargets(abilityControllerId, source, game, fromTargetsList).stream()
|
||||||
.filter(id -> !target.contains(id))
|
.filter(id -> !target.contains(id))
|
||||||
.filter(id -> target.canTarget(abilityControllerId, id, source, game))
|
|
||||||
.map(id -> {
|
.map(id -> {
|
||||||
Player player = game.getPlayer(id);
|
Player player = game.getPlayer(id);
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
|
|
@ -137,6 +136,10 @@ public class PossibleTargetsSelector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<MageItem> getAny() {
|
||||||
|
return this.any;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isMyItem(UUID abilityControllerId, MageItem item) {
|
public static boolean isMyItem(UUID abilityControllerId, MageItem item) {
|
||||||
if (item instanceof Player) {
|
if (item instanceof Player) {
|
||||||
return item.getId().equals(abilityControllerId);
|
return item.getId().equals(abilityControllerId);
|
||||||
|
|
@ -181,7 +184,12 @@ public class PossibleTargetsSelector {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hasAnyTargets() {
|
public boolean hasAnyTargets() {
|
||||||
return !this.any.isEmpty();
|
return !this.any.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasMinNumberOfTargets() {
|
||||||
|
return this.target.getMinNumberOfTargets() == 0
|
||||||
|
|| this.any.size() >= this.target.getMinNumberOfTargets();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -121,7 +121,7 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ability.isUsesStack()) {
|
if (ability.isUsesStack()) {
|
||||||
game.getStack().push(new StackAbility(ability, playerId));
|
game.getStack().push(game, new StackAbility(ability, playerId));
|
||||||
if (ability.activate(game, false)) {
|
if (ability.activate(game, false)) {
|
||||||
game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability, ability.getControllerId()));
|
game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability, ability.getControllerId()));
|
||||||
actionCount++;
|
actionCount++;
|
||||||
|
|
@ -187,8 +187,8 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer {
|
||||||
abort = true;
|
abort = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean chooseRandom(Target target, Game game) {
|
private boolean chooseRandom(Target target, Ability source, Game game) {
|
||||||
Set<UUID> possibleTargets = target.possibleTargets(playerId, game);
|
Set<UUID> possibleTargets = target.possibleTargets(playerId, source, game);
|
||||||
if (possibleTargets.isEmpty()) {
|
if (possibleTargets.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -233,7 +233,7 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer {
|
||||||
@Override
|
@Override
|
||||||
public boolean choose(Outcome outcome, Target target, Ability source, Game game) {
|
public boolean choose(Outcome outcome, Target target, Ability source, Game game) {
|
||||||
if (this.isHuman()) {
|
if (this.isHuman()) {
|
||||||
return chooseRandom(target, game);
|
return chooseRandom(target, source, game);
|
||||||
}
|
}
|
||||||
return super.choose(outcome, target, source, game);
|
return super.choose(outcome, target, source, game);
|
||||||
}
|
}
|
||||||
|
|
@ -241,7 +241,7 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer {
|
||||||
@Override
|
@Override
|
||||||
public boolean choose(Outcome outcome, Target target, Ability source, Game game, Map<String, Serializable> options) {
|
public boolean choose(Outcome outcome, Target target, Ability source, Game game, Map<String, Serializable> options) {
|
||||||
if (this.isHuman()) {
|
if (this.isHuman()) {
|
||||||
return chooseRandom(target, game);
|
return chooseRandom(target, source, game);
|
||||||
}
|
}
|
||||||
return super.choose(outcome, target, source, game, options);
|
return super.choose(outcome, target, source, game, options);
|
||||||
}
|
}
|
||||||
|
|
@ -252,7 +252,7 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer {
|
||||||
if (cards.isEmpty()) {
|
if (cards.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Set<UUID> possibleTargets = target.possibleTargets(playerId, cards, source, game);
|
Set<UUID> possibleTargets = target.possibleTargets(playerId, source, game, cards);
|
||||||
if (possibleTargets.isEmpty()) {
|
if (possibleTargets.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import mage.abilities.effects.common.InfoEffect;
|
||||||
import mage.abilities.effects.common.continuous.PlayAdditionalLandsAllEffect;
|
import mage.abilities.effects.common.continuous.PlayAdditionalLandsAllEffect;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
|
import mage.util.ConsoleUtil;
|
||||||
import mage.utils.testers.TestableDialog;
|
import mage.utils.testers.TestableDialog;
|
||||||
import mage.utils.testers.TestableDialogsRunner;
|
import mage.utils.testers.TestableDialogsRunner;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
|
@ -107,7 +108,7 @@ public class TestableDialogsTest extends CardTestPlayerBaseWithAIHelps {
|
||||||
@Test
|
@Test
|
||||||
@Ignore // debug only - run single dialog by reg number
|
@Ignore // debug only - run single dialog by reg number
|
||||||
public void test_RunSingle_Debugging() {
|
public void test_RunSingle_Debugging() {
|
||||||
int needRegNumber = 557;
|
int needRegNumber = 5;
|
||||||
|
|
||||||
prepareCards();
|
prepareCards();
|
||||||
|
|
||||||
|
|
@ -233,15 +234,15 @@ public class TestableDialogsTest extends CardTestPlayerBaseWithAIHelps {
|
||||||
if (resAssert == null) {
|
if (resAssert == null) {
|
||||||
totalUnknown++;
|
totalUnknown++;
|
||||||
status = "?";
|
status = "?";
|
||||||
coloredTexts.put("?", asYellow("?"));
|
coloredTexts.put("?", ConsoleUtil.asYellow("?"));
|
||||||
} else if (resAssert.isEmpty()) {
|
} else if (resAssert.isEmpty()) {
|
||||||
totalGood++;
|
totalGood++;
|
||||||
status = "OK";
|
status = "OK";
|
||||||
coloredTexts.put("OK", asGreen("OK"));
|
coloredTexts.put("OK", ConsoleUtil.asGreen("OK"));
|
||||||
} else {
|
} else {
|
||||||
totalBad++;
|
totalBad++;
|
||||||
status = "FAIL";
|
status = "FAIL";
|
||||||
coloredTexts.put("FAIL", asRed("FAIL"));
|
coloredTexts.put("FAIL", ConsoleUtil.asRed("FAIL"));
|
||||||
assertError = resAssert;
|
assertError = resAssert;
|
||||||
}
|
}
|
||||||
if (!assertError.isEmpty()) {
|
if (!assertError.isEmpty()) {
|
||||||
|
|
@ -256,8 +257,8 @@ public class TestableDialogsTest extends CardTestPlayerBaseWithAIHelps {
|
||||||
// print dialog error
|
// print dialog error
|
||||||
if (!assertError.isEmpty()) {
|
if (!assertError.isEmpty()) {
|
||||||
coloredTexts.clear();
|
coloredTexts.clear();
|
||||||
coloredTexts.put(resAssert, asRed(resAssert));
|
coloredTexts.put(resAssert, ConsoleUtil.asRed(resAssert));
|
||||||
coloredTexts.put(resDebugSource, asRed(resDebugSource));
|
coloredTexts.put(resDebugSource, ConsoleUtil.asRed(resDebugSource));
|
||||||
String badAssert = getColoredRow(totalsRightFormat, coloredTexts, resAssert);
|
String badAssert = getColoredRow(totalsRightFormat, coloredTexts, resAssert);
|
||||||
String badDebugSource = getColoredRow(totalsRightFormat, coloredTexts, resDebugSource);
|
String badDebugSource = getColoredRow(totalsRightFormat, coloredTexts, resDebugSource);
|
||||||
if (firstBadDialog == null) {
|
if (firstBadDialog == null) {
|
||||||
|
|
@ -283,15 +284,15 @@ public class TestableDialogsTest extends CardTestPlayerBaseWithAIHelps {
|
||||||
String badStats = String.format("%d bad", totalBad);
|
String badStats = String.format("%d bad", totalBad);
|
||||||
String unknownStats = String.format("%d unknown", totalUnknown);
|
String unknownStats = String.format("%d unknown", totalUnknown);
|
||||||
coloredTexts.clear();
|
coloredTexts.clear();
|
||||||
coloredTexts.put(goodStats, String.format("%s good", asGreen(String.valueOf(totalGood))));
|
coloredTexts.put(goodStats, String.format("%s good", ConsoleUtil.asGreen(String.valueOf(totalGood))));
|
||||||
coloredTexts.put(badStats, String.format("%s bad", asRed(String.valueOf(totalBad))));
|
coloredTexts.put(badStats, String.format("%s bad", ConsoleUtil.asRed(String.valueOf(totalBad))));
|
||||||
coloredTexts.put(unknownStats, String.format("%s unknown", asYellow(String.valueOf(totalUnknown))));
|
coloredTexts.put(unknownStats, String.format("%s unknown", ConsoleUtil.asYellow(String.valueOf(totalUnknown))));
|
||||||
System.out.print(getColoredRow(totalsLeftFormat, coloredTexts, String.format("Total results: %s, %s, %s",
|
System.out.print(getColoredRow(totalsLeftFormat, coloredTexts, String.format("Total results: %s, %s, %s",
|
||||||
goodStats, badStats, unknownStats)));
|
goodStats, badStats, unknownStats)));
|
||||||
// first error for fast access in big list
|
// first error for fast access in big list
|
||||||
if (totalDialogs > 1 && firstBadDialog != null) {
|
if (totalDialogs > 1 && firstBadDialog != null) {
|
||||||
System.out.println(horizontalBorder);
|
System.out.println(horizontalBorder);
|
||||||
System.out.print(getColoredRow(totalsRightFormat, coloredTexts, "First bad dialog: " + firstBadDialog.getRegNumber()));
|
System.out.print(getColoredRow(totalsRightFormat, coloredTexts, "First bad dialog: " + firstBadDialog.getRegNumber() + " (debug it by test_RunSingle_Debugging)"));
|
||||||
System.out.print(getColoredRow(totalsRightFormat, coloredTexts, firstBadDialog.getName() + " - " + firstBadDialog.getDescription()));
|
System.out.print(getColoredRow(totalsRightFormat, coloredTexts, firstBadDialog.getName() + " - " + firstBadDialog.getDescription()));
|
||||||
System.out.print(firstBadAssert);
|
System.out.print(firstBadAssert);
|
||||||
System.out.print(firstBadDebugSource);
|
System.out.print(firstBadDebugSource);
|
||||||
|
|
@ -313,16 +314,4 @@ public class TestableDialogsTest extends CardTestPlayerBaseWithAIHelps {
|
||||||
}
|
}
|
||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String asRed(String text) {
|
|
||||||
return "\u001B[31m" + text + "\u001B[0m";
|
|
||||||
}
|
|
||||||
|
|
||||||
private String asGreen(String text) {
|
|
||||||
return "\u001B[32m" + text + "\u001B[0m";
|
|
||||||
}
|
|
||||||
|
|
||||||
private String asYellow(String text) {
|
|
||||||
return "\u001B[33m" + text + "\u001B[0m";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1706,6 +1706,11 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
||||||
private void assertAllCommandsUsed() throws AssertionError {
|
private void assertAllCommandsUsed() throws AssertionError {
|
||||||
for (Player player : currentGame.getPlayers().values()) {
|
for (Player player : currentGame.getPlayers().values()) {
|
||||||
TestPlayer testPlayer = (TestPlayer) player;
|
TestPlayer testPlayer = (TestPlayer) player;
|
||||||
|
|
||||||
|
if (testPlayer.isSkipAllNextChooseCommands()) {
|
||||||
|
Assert.fail(testPlayer.getName() + " used skip next choose commands, but game do not call any choose dialog after it. Skip must be removed after debug.");
|
||||||
|
}
|
||||||
|
|
||||||
assertActionsMustBeEmpty(testPlayer);
|
assertActionsMustBeEmpty(testPlayer);
|
||||||
assertChoicesCount(testPlayer, 0);
|
assertChoicesCount(testPlayer, 0);
|
||||||
assertTargetsCount(testPlayer, 0);
|
assertTargetsCount(testPlayer, 0);
|
||||||
|
|
@ -2008,7 +2013,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
||||||
* @param step
|
* @param step
|
||||||
* @param player
|
* @param player
|
||||||
* @param cardName
|
* @param cardName
|
||||||
* @param targetName for modes you can add "mode=3" before target name;
|
* @param targetName for non default mode you can add target by "mode=3target_name" style;
|
||||||
* multiple targets can be separated by ^;
|
* multiple targets can be separated by ^;
|
||||||
* no target marks as TestPlayer.NO_TARGET;
|
* no target marks as TestPlayer.NO_TARGET;
|
||||||
* warning, do not support cards with target adjusters - use addTarget instead
|
* warning, do not support cards with target adjusters - use addTarget instead
|
||||||
|
|
@ -2315,6 +2320,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
||||||
* spell mode can be used only once like Demonic Pact, the
|
* spell mode can be used only once like Demonic Pact, the
|
||||||
* value has to be set to the number of the remaining modes
|
* value has to be set to the number of the remaining modes
|
||||||
* (e.g. if only 2 are left the number need to be 1 or 2).
|
* (e.g. if only 2 are left the number need to be 1 or 2).
|
||||||
|
* If you need to partly select then use TestPlayer.MODE_SKIP
|
||||||
*/
|
*/
|
||||||
public void setModeChoice(TestPlayer player, String choice) {
|
public void setModeChoice(TestPlayer player, String choice) {
|
||||||
player.addModeChoice(choice);
|
player.addModeChoice(choice);
|
||||||
|
|
@ -2439,6 +2445,26 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
||||||
gameOptions.skipInitShuffling = true;
|
gameOptions.skipInitShuffling = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug only: skip all choose commands after that command.
|
||||||
|
* <p>
|
||||||
|
* Alternative to comment/uncomment all test commands:
|
||||||
|
* - insert skip before first choice command;
|
||||||
|
* - run test and look at error message about miss choice;
|
||||||
|
* - make sure test use correct choice;
|
||||||
|
* - move skip command to next test's choice and repeat;
|
||||||
|
*/
|
||||||
|
protected void skipAllNextChooseCommands() {
|
||||||
|
playerA.skipAllNextChooseCommands();
|
||||||
|
playerB.skipAllNextChooseCommands();
|
||||||
|
if (playerC != null) {
|
||||||
|
playerC.skipAllNextChooseCommands();
|
||||||
|
}
|
||||||
|
if (playerD != null) {
|
||||||
|
playerD.skipAllNextChooseCommands();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void assertDamageReceived(Player player, String cardName, int expected) {
|
public void assertDamageReceived(Player player, String cardName, int expected) {
|
||||||
Permanent p = getPermanent(cardName, player);
|
Permanent p = getPermanent(cardName, player);
|
||||||
if (p != null) {
|
if (p != null) {
|
||||||
|
|
|
||||||
|
|
@ -130,4 +130,9 @@ public class Mode implements Serializable {
|
||||||
public int getPawPrintValue() {
|
public int getPawPrintValue() {
|
||||||
return pawPrintValue;
|
return pawPrintValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("%s", this.getEffects().getText(this));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package mage.collectors;
|
||||||
|
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.Table;
|
import mage.game.Table;
|
||||||
|
import mage.players.Player;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -11,10 +12,12 @@ import java.util.UUID;
|
||||||
* Supported features:
|
* Supported features:
|
||||||
* - [x] collect and print game logs in server output, including unit tests
|
* - [x] collect and print game logs in server output, including unit tests
|
||||||
* - [x] collect and save full games history and decks
|
* - [x] collect and save full games history and decks
|
||||||
* - [ ] collect and print performance metrics like ApplyEffects calc time or inform players time (pings)
|
* - [ ] TODO: collect and print performance metrics like ApplyEffects calc time or inform players time (pings)
|
||||||
* - [ ] collect and send metrics to third party tools like prometheus + grafana
|
* - [ ] TODO: collect and send metrics to third party tools like prometheus + grafana
|
||||||
* - [ ] prepare "attachable" game data for bug reports
|
* - [x] tests: print used selections (choices, targets, modes, skips) TODO: add yes/no, replacement effect, coins, other choices
|
||||||
* - [ ] record game replays data (GameView history)
|
* - [ ] TODO: tests: print additional info like current resolve ability?
|
||||||
|
* - [ ] TODO: prepare "attachable" game data for bug reports
|
||||||
|
* - [ ] TODO: record game replays data (GameView history)
|
||||||
* <p>
|
* <p>
|
||||||
* How-to enable or disable:
|
* How-to enable or disable:
|
||||||
* - use java params like -Dxmage.dataCollectors.saveGameHistory=true
|
* - use java params like -Dxmage.dataCollectors.saveGameHistory=true
|
||||||
|
|
@ -62,7 +65,27 @@ public interface DataCollector {
|
||||||
void onChatTable(UUID tableId, String userName, String message);
|
void onChatTable(UUID tableId, String userName, String message);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param gameId chat sessings don't have full game access, so use onGameStart event to find game's ID before chat
|
* @param gameId chat session don't have full game access, so use onGameStart event to find game's ID before chat
|
||||||
*/
|
*/
|
||||||
void onChatGame(UUID gameId, String userName, String message);
|
void onChatGame(UUID gameId, String userName, String message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests only: on any non-target choice like yes/no, mode, etc
|
||||||
|
*/
|
||||||
|
void onTestsChoiceUse(Game game, Player player, String usingChoice, String reason);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests only: on any target choice
|
||||||
|
*/
|
||||||
|
void onTestsTargetUse(Game game, Player player, String usingTarget, String reason);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests only: on push object to stack (calls before activate and make any choice/announce)
|
||||||
|
*/
|
||||||
|
void onTestsStackPush(Game game);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests only: on stack object resolve (calls before starting resolve)
|
||||||
|
*/
|
||||||
|
void onTestsStackResolve(Game game);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import mage.collectors.services.PrintGameLogsDataCollector;
|
||||||
import mage.collectors.services.SaveGameHistoryDataCollector;
|
import mage.collectors.services.SaveGameHistoryDataCollector;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.Table;
|
import mage.game.Table;
|
||||||
|
import mage.players.Player;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
|
@ -140,4 +141,28 @@ final public class DataCollectorServices implements DataCollector {
|
||||||
public void onChatGame(UUID gameId, String userName, String message) {
|
public void onChatGame(UUID gameId, String userName, String message) {
|
||||||
activeServices.forEach(c -> c.onChatGame(gameId, userName, message));
|
activeServices.forEach(c -> c.onChatGame(gameId, userName, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTestsChoiceUse(Game game, Player player, String usingChoice, String reason) {
|
||||||
|
if (game.isSimulation()) return;
|
||||||
|
activeServices.forEach(c -> c.onTestsChoiceUse(game, player, usingChoice, reason));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTestsTargetUse(Game game, Player player, String usingTarget, String reason) {
|
||||||
|
if (game.isSimulation()) return;
|
||||||
|
activeServices.forEach(c -> c.onTestsTargetUse(game, player, usingTarget, reason));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTestsStackPush(Game game) {
|
||||||
|
if (game.isSimulation()) return;
|
||||||
|
activeServices.forEach(c -> c.onTestsStackPush(game));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTestsStackResolve(Game game) {
|
||||||
|
if (game.isSimulation()) return;
|
||||||
|
activeServices.forEach(c -> c.onTestsStackResolve(game));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package mage.collectors.services;
|
||||||
import mage.collectors.DataCollector;
|
import mage.collectors.DataCollector;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.Table;
|
import mage.game.Table;
|
||||||
|
import mage.players.Player;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -67,4 +68,24 @@ public abstract class EmptyDataCollector implements DataCollector {
|
||||||
public void onChatGame(UUID gameId, String userName, String message) {
|
public void onChatGame(UUID gameId, String userName, String message) {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTestsChoiceUse(Game game, Player player, String usingChoice, String reason) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTestsTargetUse(Game game, Player player, String usingTarget, String reason) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTestsStackPush(Game game) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTestsStackResolve(Game game) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package mage.collectors.services;
|
package mage.collectors.services;
|
||||||
|
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
|
import mage.players.Player;
|
||||||
import mage.util.CardUtil;
|
import mage.util.CardUtil;
|
||||||
|
import mage.util.ConsoleUtil;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
import org.jsoup.Jsoup;
|
import org.jsoup.Jsoup;
|
||||||
|
|
||||||
|
|
@ -40,9 +42,47 @@ public class PrintGameLogsDataCollector extends EmptyDataCollector {
|
||||||
@Override
|
@Override
|
||||||
public void onGameLog(Game game, String message) {
|
public void onGameLog(Game game, String message) {
|
||||||
String needMessage = Jsoup.parse(message).text();
|
String needMessage = Jsoup.parse(message).text();
|
||||||
writeLog("GAME", "LOG", String.format("%s: %s",
|
writeLog("LOG", "GAME", String.format("%s: %s",
|
||||||
CardUtil.getTurnInfo(game),
|
CardUtil.getTurnInfo(game),
|
||||||
needMessage
|
needMessage
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTestsChoiceUse(Game game, Player player, String choice, String reason) {
|
||||||
|
String needReason = Jsoup.parse(reason).text();
|
||||||
|
writeLog("LOG", "GAME", ConsoleUtil.asYellow(String.format("%s: %s using choice: %s%s",
|
||||||
|
CardUtil.getTurnInfo(game),
|
||||||
|
player.getName(),
|
||||||
|
choice,
|
||||||
|
reason.isEmpty() ? "" : " (" + needReason + ")"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTestsTargetUse(Game game, Player player, String target, String reason) {
|
||||||
|
String needReason = Jsoup.parse(reason).text();
|
||||||
|
writeLog("LOG", "GAME", ConsoleUtil.asYellow(String.format("%s: %s using target: %s%s",
|
||||||
|
CardUtil.getTurnInfo(game),
|
||||||
|
player.getName(),
|
||||||
|
target,
|
||||||
|
reason.isEmpty() ? "" : " (" + needReason + ")"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTestsStackPush(Game game) {
|
||||||
|
writeLog("LOG", "GAME", String.format("%s: Stack push: %s",
|
||||||
|
CardUtil.getTurnInfo(game),
|
||||||
|
game.getStack().toString()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTestsStackResolve(Game game) {
|
||||||
|
writeLog("LOG", "GAME", String.format("%s: Stack resolve: %s",
|
||||||
|
CardUtil.getTurnInfo(game),
|
||||||
|
game.getStack().toString()
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
19
Mage/src/main/java/mage/util/ConsoleUtil.java
Normal file
19
Mage/src/main/java/mage/util/ConsoleUtil.java
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
package mage.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to work with console logs
|
||||||
|
*/
|
||||||
|
public class ConsoleUtil {
|
||||||
|
|
||||||
|
public static String asRed(String text) {
|
||||||
|
return "\u001B[31m" + text + "\u001B[0m";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String asGreen(String text) {
|
||||||
|
return "\u001B[32m" + text + "\u001B[0m";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String asYellow(String text) {
|
||||||
|
return "\u001B[33m" + text + "\u001B[0m";
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue