more optimizations

This commit is contained in:
BetaSteward 2011-03-24 00:09:07 -04:00
parent 5097b0812b
commit 8287364f77
12 changed files with 146 additions and 30 deletions

View file

@ -40,11 +40,9 @@ import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask; import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import mage.Constants.Outcome; import mage.Constants.Outcome;
import mage.Constants.PhaseStep; import mage.Constants.PhaseStep;
import mage.Constants.RangeOfInfluence; import mage.Constants.RangeOfInfluence;
import mage.Mana;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.ActivatedAbility; import mage.abilities.ActivatedAbility;
import mage.abilities.common.PassAbility; import mage.abilities.common.PassAbility;
@ -55,7 +53,6 @@ import mage.abilities.costs.mana.VariableManaCost;
import mage.abilities.effects.Effect; import mage.abilities.effects.Effect;
import mage.abilities.effects.SearchEffect; import mage.abilities.effects.SearchEffect;
import mage.cards.Cards; import mage.cards.Cards;
import mage.cards.decks.Deck;
import mage.choices.Choice; import mage.choices.Choice;
import mage.filter.FilterAbility; import mage.filter.FilterAbility;
import mage.game.Game; import mage.game.Game;
@ -98,6 +95,8 @@ public class ComputerPlayer2 extends ComputerPlayer<ComputerPlayer2> implements
protected int maxDepth; protected int maxDepth;
protected int maxNodes; protected int maxNodes;
protected int nodeCount = 0;
protected long thinkTime = 0;
protected LinkedList<Ability> actions = new LinkedList<Ability>(); protected LinkedList<Ability> actions = new LinkedList<Ability>();
protected List<UUID> targets = new ArrayList<UUID>(); protected List<UUID> targets = new ArrayList<UUID>();
protected List<String> choices = new ArrayList<String>(); protected List<String> choices = new ArrayList<String>();
@ -190,6 +189,7 @@ public class ComputerPlayer2 extends ComputerPlayer<ComputerPlayer2> implements
addActionsTimed(new FilterAbility()); addActionsTimed(new FilterAbility());
else else
addActions(root, new FilterAbility(), Integer.MIN_VALUE, Integer.MAX_VALUE); addActions(root, new FilterAbility(), Integer.MIN_VALUE, Integer.MAX_VALUE);
logger.info(name + " simulated " + nodeCount + " nodes in " + thinkTime/1000000000.0 + "s - average " + nodeCount/(thinkTime/1000000000.0) + " nodes/s");
if (root.children.size() > 0) { if (root.children.size() > 0) {
root = root.children.get(0); root = root.children.get(0);
actions = new LinkedList<Ability>(root.abilities); actions = new LinkedList<Ability>(root.abilities);
@ -319,9 +319,15 @@ public class ComputerPlayer2 extends ComputerPlayer<ComputerPlayer2> implements
return addActions(root, filter, Integer.MIN_VALUE, Integer.MAX_VALUE); return addActions(root, filter, Integer.MIN_VALUE, Integer.MAX_VALUE);
} }
}); });
long startTime = System.nanoTime();
pool.execute(task); pool.execute(task);
try { try {
task.get(Config.maxThinkSeconds, TimeUnit.SECONDS); task.get(Config.maxThinkSeconds, TimeUnit.SECONDS);
long endTime = System.nanoTime();
long duration = endTime - startTime;
logger.info("Calculated " + root.nodeCount + " nodes in " + duration/1000000000.0 + "s");
nodeCount += root.nodeCount;
thinkTime += duration;
} catch (TimeoutException e) { } catch (TimeoutException e) {
logger.debug("simulating - timed out"); logger.debug("simulating - timed out");
task.cancel(true); task.cancel(true);
@ -331,6 +337,7 @@ public class ComputerPlayer2 extends ComputerPlayer<ComputerPlayer2> implements
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
logger.fatal("can't sleep"); logger.fatal("can't sleep");
} }
logger.info("Calculated " + root.nodeCount + " nodes in 30s");
} catch (ExecutionException e) { } catch (ExecutionException e) {
logger.fatal("Simulation error", e); logger.fatal("Simulation error", e);
task.cancel(true); task.cancel(true);
@ -712,6 +719,7 @@ public class ComputerPlayer2 extends ComputerPlayer<ComputerPlayer2> implements
newPlayer.restore(origPlayer); newPlayer.restore(origPlayer);
sim.getState().getPlayers().put(copyPlayer.getId(), newPlayer); sim.getState().getPlayers().put(copyPlayer.getId(), newPlayer);
} }
sim.setSimulation(true);
return sim; return sim;
} }

View file

@ -164,6 +164,7 @@ public class ComputerPlayer3 extends ComputerPlayer2 implements Player {
addActionsTimed(new FilterAbility()); addActionsTimed(new FilterAbility());
else else
addActions(root, new FilterAbility(), Integer.MIN_VALUE, Integer.MAX_VALUE); addActions(root, new FilterAbility(), Integer.MIN_VALUE, Integer.MAX_VALUE);
logger.info(name + " simulated " + nodeCount + " nodes in " + thinkTime/1000000000.0 + "s - average " + nodeCount/(thinkTime/1000000000.0) + " nodes/s");
if (root.children.size() > 0) { if (root.children.size() > 0) {
root = root.children.get(0); root = root.children.get(0);
actions = new LinkedList<Ability>(root.abilities); actions = new LinkedList<Ability>(root.abilities);
@ -187,6 +188,7 @@ public class ComputerPlayer3 extends ComputerPlayer2 implements Player {
addActionsTimed(new FilterAbility()); addActionsTimed(new FilterAbility());
else else
addActions(root, new FilterAbility(), Integer.MIN_VALUE, Integer.MAX_VALUE); addActions(root, new FilterAbility(), Integer.MIN_VALUE, Integer.MAX_VALUE);
logger.info(name + " simulated " + nodeCount + " nodes in " + thinkTime/1000000000.0 + "s - average " + nodeCount/(thinkTime/1000000000.0) + " nodes/s");
if (root.children.size() > 0) { if (root.children.size() > 0) {
root = root.children.get(0); root = root.children.get(0);
actions = new LinkedList<Ability>(root.abilities); actions = new LinkedList<Ability>(root.abilities);

View file

@ -38,6 +38,7 @@ import mage.abilities.keyword.TrampleAbility;
import mage.abilities.mana.ManaAbility; import mage.abilities.mana.ManaAbility;
import mage.counters.BoostCounter; import mage.counters.BoostCounter;
import mage.counters.Counter; import mage.counters.Counter;
import mage.counters.CounterType;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.players.Player; import mage.players.Player;
@ -76,6 +77,7 @@ public class GameStateEvaluator {
return WIN_SCORE; return WIN_SCORE;
} }
int lifeScore = (player.getLife() - opponent.getLife()) * LIFE_FACTOR; int lifeScore = (player.getLife() - opponent.getLife()) * LIFE_FACTOR;
int poisonScore = (opponent.getCounters().getCount(CounterType.POISON) - player.getCounters().getCount(CounterType.POISON)) * LIFE_FACTOR * 2;
int permanentScore = 0; int permanentScore = 0;
for (Permanent permanent: game.getBattlefield().getAllActivePermanents(playerId)) { for (Permanent permanent: game.getBattlefield().getAllActivePermanents(playerId)) {
permanentScore += evaluatePermanent(permanent, game, ignoreTapped); permanentScore += evaluatePermanent(permanent, game, ignoreTapped);
@ -89,7 +91,7 @@ public class GameStateEvaluator {
handScore = player.getHand().size() - opponent.getHand().size(); handScore = player.getHand().size() - opponent.getHand().size();
handScore *= HAND_FACTOR; handScore *= HAND_FACTOR;
int score = lifeScore + permanentScore + handScore; int score = lifeScore + poisonScore + permanentScore + handScore;
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
logger.debug("game state for player " + player.getName() + " evaluated to- lifeScore:" + lifeScore + " permanentScore:" + permanentScore + " handScore:" + handScore + " total:" + score); logger.debug("game state for player " + player.getName() + " evaluated to- lifeScore:" + lifeScore + " permanentScore:" + permanentScore + " handScore:" + handScore + " total:" + score);
return score; return score;

View file

@ -27,6 +27,6 @@ public class Metalcraft implements Condition {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
return game.getBattlefield().countAll(filter, source.getControllerId()) >= 3; return game.getBattlefield().contains(filter, source.getControllerId(), 3);
} }
} }

View file

@ -61,7 +61,7 @@ public class MetalcraftCost extends CostImpl<MetalcraftCost> {
@Override @Override
public boolean canPay(UUID sourceId, UUID controllerId, Game game) { public boolean canPay(UUID sourceId, UUID controllerId, Game game) {
return game.getBattlefield().countAll(filter, controllerId) >= 3; return game.getBattlefield().contains(filter, controllerId, 3);
} }
@Override @Override

View file

@ -73,7 +73,7 @@ class LandwalkEffect extends RestrictionEffect<LandwalkEffect> {
@Override @Override
public boolean canBlock(Permanent attacker, Permanent blocker, Game game) { public boolean canBlock(Permanent attacker, Permanent blocker, Game game) {
return game.getBattlefield().countAll(filter, blocker.getControllerId()) == 0; return !game.getBattlefield().contains(filter, blocker.getControllerId(), 1);
} }
@Override @Override

View file

@ -107,6 +107,8 @@ public interface Game extends MageItem, Serializable {
public GameStates getGameStates(); public GameStates getGameStates();
public void loadGameStates(GameStates states); public void loadGameStates(GameStates states);
public Game copy(); public Game copy();
public boolean isSimulation();
public void setSimulation(boolean simulation);
public Card getLastKnownInformation(UUID objectId, Zone zone); public Card getLastKnownInformation(UUID objectId, Zone zone);
public void rememberLKI(UUID objectId, Zone zone, Card card); public void rememberLKI(UUID objectId, Zone zone, Card card);
public void resetLKI(); public void resetLKI();
@ -119,7 +121,6 @@ public interface Game extends MageItem, Serializable {
public void fireSelectTargetEvent(UUID playerId, String message, Set<UUID> targets, boolean required, Map<String, Serializable> options); public void fireSelectTargetEvent(UUID playerId, String message, Set<UUID> targets, boolean required, Map<String, Serializable> options);
public void fireSelectTargetEvent(UUID playerId, String message, Cards cards, boolean required); public void fireSelectTargetEvent(UUID playerId, String message, Cards cards, boolean required);
public void fireSelectTargetEvent(UUID playerId, String message, TriggeredAbilities abilities, boolean required); public void fireSelectTargetEvent(UUID playerId, String message, TriggeredAbilities abilities, boolean required);
// public void fireRevealCardsEvent(String message, Cards cards);
public void fireSelectEvent(UUID playerId, String message); public void fireSelectEvent(UUID playerId, String message);
public void fireLookAtCardsEvent(UUID playerId, String message, Cards cards); public void fireLookAtCardsEvent(UUID playerId, String message, Cards cards);
public void firePriorityEvent(UUID playerId); public void firePriorityEvent(UUID playerId);
@ -136,7 +137,6 @@ public interface Game extends MageItem, Serializable {
public boolean replaceEvent(GameEvent event); public boolean replaceEvent(GameEvent event);
//game play methods //game play methods
//public void init(UUID choosingPlayerId);
public void start(UUID choosingPlayerId); public void start(UUID choosingPlayerId);
public void start(UUID choosingPlayerId, GameOptions options); public void start(UUID choosingPlayerId, GameOptions options);
public void end(); public void end();

View file

@ -79,6 +79,7 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
private transient Stack<Integer> savedStates = new Stack<Integer>(); private transient Stack<Integer> savedStates = new Stack<Integer>();
private transient Object customData; private transient Object customData;
protected boolean simulation = false;
protected final UUID id; protected final UUID id;
protected boolean ready; protected boolean ready;
@ -113,10 +114,18 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
this.range = game.range; this.range = game.range;
this.attackOption = game.attackOption; this.attackOption = game.attackOption;
this.state = game.state.copy(); this.state = game.state.copy();
// for (Map.Entry<UUID, Card> entry: game.gameCards.entrySet()) {
// this.gameCards.put(entry.getKey(), entry.getValue().copy());
// }
this.gameCards = game.gameCards; this.gameCards = game.gameCards;
this.simulation = game.simulation;
}
@Override
public boolean isSimulation() {
return simulation;
}
@Override
public void setSimulation(boolean simulation) {
this.simulation = simulation;
} }
@Override @Override
@ -273,22 +282,28 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
@Override @Override
public void bookmarkState() { public void bookmarkState() {
if (!simulation) {
saveState(); saveState();
logger.fine("Bookmarking state: " + gameStates.getSize()); logger.fine("Bookmarking state: " + gameStates.getSize());
savedStates.push(gameStates.getSize() - 1); savedStates.push(gameStates.getSize() - 1);
} }
}
@Override @Override
public void restoreState() { public void restoreState() {
if (!simulation) {
GameState restore = gameStates.rollback(savedStates.pop()); GameState restore = gameStates.rollback(savedStates.pop());
if (restore != null) if (restore != null)
state.restore(restore); state.restore(restore);
} }
}
@Override @Override
public void removeLastBookmark() { public void removeLastBookmark() {
if (!simulation) {
savedStates.pop(); savedStates.pop();
} }
}
@Override @Override
public void start(UUID choosingPlayerId) { public void start(UUID choosingPlayerId) {
@ -622,13 +637,13 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
} }
} }
//20091005 - 704.5j, 801.14 //20091005 - 704.5j, 801.14
if (getBattlefield().countAll(filterPlaneswalker) > 1) { //don't bother checking if less than 2 planeswalkers in play if (getBattlefield().contains(filterPlaneswalker, 2)) { //don't bother checking if less than 2 planeswalkers in play
for (Permanent planeswalker: getBattlefield().getAllActivePermanents(CardType.PLANESWALKER)) { for (Permanent planeswalker: getBattlefield().getAllActivePermanents(CardType.PLANESWALKER)) {
for (String planeswalkertype: planeswalker.getSubtype()) { for (String planeswalkertype: planeswalker.getSubtype()) {
filterPlaneswalker.getSubtype().clear(); filterPlaneswalker.getSubtype().clear();
filterPlaneswalker.getSubtype().add(planeswalkertype); filterPlaneswalker.getSubtype().add(planeswalkertype);
filterPlaneswalker.setScopeSubtype(ComparisonScope.Any); filterPlaneswalker.setScopeSubtype(ComparisonScope.Any);
if (getBattlefield().count(filterPlaneswalker, planeswalker.getControllerId(), this) > 1) { if (getBattlefield().contains(filterPlaneswalker, planeswalker.getControllerId(), this, 2)) {
for (Permanent perm: getBattlefield().getActivePermanents(filterPlaneswalker, planeswalker.getControllerId(), this)) { for (Permanent perm: getBattlefield().getActivePermanents(filterPlaneswalker, planeswalker.getControllerId(), this)) {
perm.moveToZone(Zone.GRAVEYARD, null, this, false); perm.moveToZone(Zone.GRAVEYARD, null, this, false);
} }
@ -660,11 +675,11 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
} }
} }
//20091005 - 704.5k, 801.12 //20091005 - 704.5k, 801.12
if (getBattlefield().countAll(filterLegendary) > 1) { //don't bother checking if less than 2 legends in play if (getBattlefield().contains(filterLegendary, 2)) { //don't bother checking if less than 2 legends in play
for (Permanent legend: getBattlefield().getAllActivePermanents(filterLegendary)) { for (Permanent legend: getBattlefield().getAllActivePermanents(filterLegendary)) {
filterLegendName.getName().clear(); filterLegendName.getName().clear();
filterLegendName.getName().add(legend.getName()); filterLegendName.getName().add(legend.getName());
if (getBattlefield().count(filterLegendName, legend.getControllerId(), this) > 1) { if (getBattlefield().contains(filterLegendName, legend.getControllerId(), this, 2)) {
for (Permanent dupLegend: getBattlefield().getActivePermanents(filterLegendName, legend.getControllerId(), this)) { for (Permanent dupLegend: getBattlefield().getActivePermanents(filterLegendName, legend.getControllerId(), this)) {
dupLegend.moveToZone(Zone.GRAVEYARD, null, this, false); dupLegend.moveToZone(Zone.GRAVEYARD, null, this, false);
} }

View file

@ -138,6 +138,81 @@ public class Battlefield implements Serializable {
return count; return count;
} }
/**
* Returns true if the battlefield contains at least 1 {@link Permanent}
* that matches the filter.
* This method ignores the range of influence.
*
* @param filter
* @return boolean
*/
public boolean contains(FilterPermanent filter, int num) {
int count = 0;
for (Permanent permanent: field.values()) {
if (filter.match(permanent)) {
count++;
if (num == count)
return true;
}
}
return false;
}
/**
* Returns true if the battlefield contains at least 1 {@link Permanent}
* that matches the filter and is controlled by controllerId.
* This method ignores the range of influence.
*
* @param filter
* @param controllerId
* @return boolean
*/
public boolean contains(FilterPermanent filter, UUID controllerId, int num) {
int count = 0;
for (Permanent permanent: field.values()) {
if (permanent.getControllerId().equals(controllerId) && filter.match(permanent)) {
count++;
if (num == count)
return true;
}
}
return false;
}
/**
* Returns true if the battlefield contains at least 1 {@link Permanent}
* that is within the range of influence of the specified player id
* and that matches the supplied filter.
*
* @param filter
* @param sourcePlayerId
* @param game
* @return boolean
*/
public boolean contains(FilterPermanent filter, UUID sourcePlayerId, Game game, int num) {
int count = 0;
if (game.getRangeOfInfluence() == RangeOfInfluence.ALL) {
for (Permanent permanent: field.values()) {
if (filter.match(permanent, sourcePlayerId, game)) {
count++;
if (num == count)
return true;
}
}
}
else {
Set<UUID> range = game.getPlayer(sourcePlayerId).getInRange();
for (Permanent permanent: field.values()) {
if (range.contains(permanent.getControllerId()) && filter.match(permanent, sourcePlayerId, game)) {
count++;
if (num == count)
return true;
}
}
}
return false;
}
public void addPermanent(Permanent permanent) { public void addPermanent(Permanent permanent) {
field.put(permanent.getId(), permanent); field.put(permanent.getId(), permanent);
} }

View file

@ -105,12 +105,15 @@ public class TargetPermanent<T extends TargetPermanent<T>> extends TargetObject<
*/ */
@Override @Override
public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) {
int remainingTargets = this.minNumberOfTargets - targets.size();
if (remainingTargets == 0)
return true;
int count = 0; int count = 0;
MageObject targetSource = game.getObject(sourceId); MageObject targetSource = game.getObject(sourceId);
for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) { for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) {
if (permanent.canBeTargetedBy(targetSource)) { if (!targets.containsKey(permanent.getId()) && permanent.canBeTargetedBy(targetSource)) {
count++; count++;
if (count >= this.minNumberOfTargets) if (count >= remainingTargets)
return true; return true;
} }
} }
@ -127,9 +130,18 @@ public class TargetPermanent<T extends TargetPermanent<T>> extends TargetObject<
*/ */
@Override @Override
public boolean canChoose(UUID sourceControllerId, Game game) { public boolean canChoose(UUID sourceControllerId, Game game) {
int possibleTargets = game.getBattlefield().count(filter, sourceControllerId, game); int remainingTargets = this.minNumberOfTargets - targets.size();
return possibleTargets >= this.minNumberOfTargets && if (remainingTargets == 0)
this.getTargets().size() < possibleTargets; return true;
int count = 0;
for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) {
if (!targets.containsKey(permanent.getId())) {
count++;
if (count >= remainingTargets)
return true;
}
}
return false;
} }
@Override @Override
@ -137,7 +149,7 @@ public class TargetPermanent<T extends TargetPermanent<T>> extends TargetObject<
Set<UUID> possibleTargets = new HashSet<UUID>(); Set<UUID> possibleTargets = new HashSet<UUID>();
MageObject targetSource = game.getObject(sourceId); MageObject targetSource = game.getObject(sourceId);
for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) { for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) {
if (permanent.canBeTargetedBy(targetSource)) { if (!targets.containsKey(permanent.getId()) && permanent.canBeTargetedBy(targetSource)) {
possibleTargets.add(permanent.getId()); possibleTargets.add(permanent.getId());
} }
} }
@ -148,8 +160,10 @@ public class TargetPermanent<T extends TargetPermanent<T>> extends TargetObject<
public Set<UUID> possibleTargets(UUID sourceControllerId, Game game) { public Set<UUID> possibleTargets(UUID sourceControllerId, Game game) {
Set<UUID> possibleTargets = new HashSet<UUID>(); Set<UUID> possibleTargets = new HashSet<UUID>();
for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) { for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, sourceControllerId, game)) {
if (!targets.containsKey(permanent.getId())) {
possibleTargets.add(permanent.getId()); possibleTargets.add(permanent.getId());
} }
}
return possibleTargets; return possibleTargets;
} }