Replaced some replacement effects with restriction effects. Added new method to restriction effect. Improved canAttack methods (not finished yet).

This commit is contained in:
LevelX2 2014-08-01 15:59:59 +02:00
parent d3dadc41aa
commit cbb6117b8e
17 changed files with 101 additions and 110 deletions

View file

@ -944,10 +944,11 @@ public class ComputerPlayer6 extends ComputerPlayer implements Player {
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, activePlayerId, activePlayerId))) { if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, activePlayerId, activePlayerId))) {
Player attackingPlayer = game.getPlayer(activePlayerId); Player attackingPlayer = game.getPlayer(activePlayerId);
// TODO: this works only in two player game, also no attack of Planeswalker
UUID defenderId = game.getOpponents(playerId).iterator().next(); UUID defenderId = game.getOpponents(playerId).iterator().next();
Player defender = game.getPlayer(defenderId); Player defender = game.getPlayer(defenderId);
List<Permanent> attackersList = super.getAvailableAttackers(game); List<Permanent> attackersList = super.getAvailableAttackers(defenderId, game);
if (attackersList.isEmpty()) { if (attackersList.isEmpty()) {
return; return;
} }

View file

@ -382,9 +382,9 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
Integer val = null; Integer val = null;
SimulationNode2 bestNode = null; SimulationNode2 bestNode = null;
SimulatedPlayer2 attacker = (SimulatedPlayer2) game.getPlayer(attackerId); SimulatedPlayer2 attacker = (SimulatedPlayer2) game.getPlayer(attackerId);
UUID defenderId = game.getOpponents(attackerId).iterator().next();
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug(attacker.getName() + "'s possible attackers: " + attacker.getAvailableAttackers(game)); logger.debug(attacker.getName() + "'s possible attackers: " + attacker.getAvailableAttackers(defenderId, game));
} }
for (Combat engagement: attacker.addAttackers(game)) { for (Combat engagement: attacker.addAttackers(game)) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
@ -395,7 +395,6 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
break; break;
} }
Game sim = game.copy(); Game sim = game.copy();
UUID defenderId = game.getOpponents(attackerId).iterator().next();
for (CombatGroup group: engagement.getGroups()) { for (CombatGroup group: engagement.getGroups()) {
for (UUID attackId: group.getAttackers()) { for (UUID attackId: group.getAttackers()) {
sim.getPlayer(attackerId).declareAttacker(attackId, defenderId, sim, false); sim.getPlayer(attackerId).declareAttacker(attackId, defenderId, sim, false);

View file

@ -334,7 +334,7 @@ public class SimulatedPlayer2 extends ComputerPlayer {
Map<Integer, Combat> engagements = new HashMap<>(); Map<Integer, Combat> engagements = new HashMap<>();
//useful only for two player games - will only attack first opponent //useful only for two player games - will only attack first opponent
UUID defenderId = game.getOpponents(playerId).iterator().next(); UUID defenderId = game.getOpponents(playerId).iterator().next();
List<Permanent> attackersList = super.getAvailableAttackers(game); List<Permanent> attackersList = super.getAvailableAttackers(defenderId, game);
//use binary digits to calculate powerset of attackers //use binary digits to calculate powerset of attackers
int powerElements = (int) Math.pow(2, attackersList.size()); int powerElements = (int) Math.pow(2, attackersList.size());
StringBuilder binary = new StringBuilder(); StringBuilder binary = new StringBuilder();

View file

@ -135,9 +135,10 @@ public class MCTSPlayer extends ComputerPlayer {
} }
List<UUID> engagement = new ArrayList<UUID>(); List<UUID> engagement = new ArrayList<UUID>();
for (int j = 0; j < attackersList.size(); j++) { for (int j = 0; j < attackersList.size(); j++) {
if (binary.charAt(j) == '1') if (binary.charAt(j) == '1') {
engagement.add(attackersList.get(j).getId()); engagement.add(attackersList.get(j).getId());
} }
}
engagements.add(engagement); engagements.add(engagement);
} }
return engagements; return engagements;
@ -146,7 +147,9 @@ public class MCTSPlayer extends ComputerPlayer {
public List<List<List<UUID>>> getBlocks(Game game) { public List<List<List<UUID>>> getBlocks(Game game) {
List<List<List<UUID>>> engagements = new ArrayList<List<List<UUID>>>(); List<List<List<UUID>>> engagements = new ArrayList<List<List<UUID>>>();
int numGroups = game.getCombat().getGroups().size(); int numGroups = game.getCombat().getGroups().size();
if (numGroups == 0) return engagements; if (numGroups == 0) {
return engagements;
}
//add a node with no blockers //add a node with no blockers
List<List<UUID>> engagement = new ArrayList<List<UUID>>(); List<List<UUID>> engagement = new ArrayList<List<UUID>>();

View file

@ -176,7 +176,7 @@ public class SimulatedPlayerMCTS extends MCTSPlayer {
//useful only for two player games - will only attack first opponent //useful only for two player games - will only attack first opponent
// logger.info("select attackers"); // logger.info("select attackers");
UUID defenderId = game.getOpponents(playerId).iterator().next(); UUID defenderId = game.getOpponents(playerId).iterator().next();
List<Permanent> attackersList = super.getAvailableAttackers(game); List<Permanent> attackersList = super.getAvailableAttackers(defenderId, game);
//use binary digits to calculate powerset of attackers //use binary digits to calculate powerset of attackers
int powerElements = (int) Math.pow(2, attackersList.size()); int powerElements = (int) Math.pow(2, attackersList.size());
int value = rnd.nextInt(powerElements); int value = rnd.nextInt(powerElements);
@ -186,9 +186,10 @@ public class SimulatedPlayerMCTS extends MCTSPlayer {
binary.insert(0, "0"); //pad with zeros binary.insert(0, "0"); //pad with zeros
} }
for (int i = 0; i < attackersList.size(); i++) { for (int i = 0; i < attackersList.size(); i++) {
if (binary.charAt(i) == '1') if (binary.charAt(i) == '1') {
game.getCombat().declareAttacker(attackersList.get(i).getId(), defenderId, game); game.getCombat().declareAttacker(attackersList.get(i).getId(), defenderId, game);
} }
}
actionCount++; actionCount++;
} }

View file

@ -307,8 +307,10 @@ public class ComputerPlayer3 extends ComputerPlayer2 implements Player {
SimulationNode bestNode = null; SimulationNode bestNode = null;
SimulatedPlayer attacker = (SimulatedPlayer) game.getPlayer(attackerId); SimulatedPlayer attacker = (SimulatedPlayer) game.getPlayer(attackerId);
if (logger.isDebugEnabled()) UUID defenderId = game.getOpponents(attackerId).iterator().next();
logger.debug(indent(node.depth) + attacker.getName() + "'s possible attackers: " + attacker.getAvailableAttackers(game)); if (logger.isDebugEnabled()) {
logger.debug(indent(node.depth) + attacker.getName() + "'s possible attackers: " + attacker.getAvailableAttackers(defenderId, game));
}
List<Combat> engagements = attacker.addAttackers(game); List<Combat> engagements = attacker.addAttackers(game);
for (Combat engagement: engagements) { for (Combat engagement: engagements) {
if (alpha >= beta) { if (alpha >= beta) {
@ -316,7 +318,7 @@ public class ComputerPlayer3 extends ComputerPlayer2 implements Player {
break; break;
} }
Game sim = game.copy(); Game sim = game.copy();
UUID defenderId = game.getOpponents(attackerId).iterator().next();
for (CombatGroup group: engagement.getGroups()) { for (CombatGroup group: engagement.getGroups()) {
for (UUID attackId: group.getAttackers()) { for (UUID attackId: group.getAttackers()) {
sim.getPlayer(attackerId).declareAttacker(attackId, defenderId, sim, false); sim.getPlayer(attackerId).declareAttacker(attackId, defenderId, sim, false);
@ -324,8 +326,9 @@ public class ComputerPlayer3 extends ComputerPlayer2 implements Player {
} }
sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_ATTACKERS, attackerId, attackerId)); sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_ATTACKERS, attackerId, attackerId));
SimulationNode newNode = new SimulationNode(node, sim, attackerId); SimulationNode newNode = new SimulationNode(node, sim, attackerId);
if (logger.isDebugEnabled()) if (logger.isDebugEnabled()) {
logger.debug(indent(node.depth) + "simulating attack for player:" + game.getPlayer(attackerId).getName()); logger.debug(indent(node.depth) + "simulating attack for player:" + game.getPlayer(attackerId).getName());
}
sim.checkStateAndTriggered(); sim.checkStateAndTriggered();
while (!sim.getStack().isEmpty()) { while (!sim.getStack().isEmpty()) {
sim.getStack().resolve(sim); sim.getStack().resolve(sim);

View file

@ -165,7 +165,7 @@ public class SimulatedPlayer extends ComputerPlayer {
Map<Integer, Combat> engagements = new HashMap<Integer, Combat>(); Map<Integer, Combat> engagements = new HashMap<Integer, Combat>();
//useful only for two player games - will only attack first opponent //useful only for two player games - will only attack first opponent
UUID defenderId = game.getOpponents(playerId).iterator().next(); UUID defenderId = game.getOpponents(playerId).iterator().next();
List<Permanent> attackersList = super.getAvailableAttackers(game); List<Permanent> attackersList = super.getAvailableAttackers(defenderId, game);
//use binary digits to calculate powerset of attackers //use binary digits to calculate powerset of attackers
int powerElements = (int) Math.pow(2, attackersList.size()); int powerElements = (int) Math.pow(2, attackersList.size());
StringBuilder binary = new StringBuilder(); StringBuilder binary = new StringBuilder();

View file

@ -180,7 +180,7 @@ public class RandomPlayer extends ComputerPlayer {
public void selectAttackers(Game game, UUID attackingPlayerId) { public void selectAttackers(Game game, UUID attackingPlayerId) {
//useful only for two player games - will only attack first opponent //useful only for two player games - will only attack first opponent
UUID defenderId = game.getOpponents(playerId).iterator().next(); UUID defenderId = game.getOpponents(playerId).iterator().next();
List<Permanent> attackersList = super.getAvailableAttackers(game); List<Permanent> attackersList = super.getAvailableAttackers(defenderId, game);
//use binary digits to calculate powerset of attackers //use binary digits to calculate powerset of attackers
int powerElements = (int) Math.pow(2, attackersList.size()); int powerElements = (int) Math.pow(2, attackersList.size());
int value = rnd.nextInt(powerElements); int value = rnd.nextInt(powerElements);

View file

@ -178,7 +178,7 @@ public class TestPlayer extends ComputerPlayer {
FilterCreatureForCombat filter = new FilterCreatureForCombat(); FilterCreatureForCombat filter = new FilterCreatureForCombat();
filter.add(new NamePredicate(groups[0])); filter.add(new NamePredicate(groups[0]));
Permanent attacker = findPermanent(filter, playerId, game); Permanent attacker = findPermanent(filter, playerId, game);
if (attacker != null && attacker.canAttack(game)) { if (attacker != null && attacker.canAttack(defenderId, game)) {
this.declareAttacker(attacker.getId(), defenderId, game, false); this.declareAttacker(attacker.getId(), defenderId, game, false);
} }
} }

View file

@ -28,6 +28,7 @@
package mage.abilities.effects; package mage.abilities.effects;
import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.EffectType; import mage.constants.EffectType;
@ -65,6 +66,10 @@ public abstract class RestrictionEffect extends ContinuousEffectImpl {
return true; return true;
} }
public boolean canAttack(UUID defenderId, Ability source, Game game) {
return true;
}
public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game) { public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game) {
return true; return true;
} }

View file

@ -29,25 +29,22 @@
package mage.abilities.effects.common.combat; package mage.abilities.effects.common.combat;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.MageSingleton; import mage.abilities.effects.RestrictionEffect;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.constants.AttachmentType; import mage.constants.AttachmentType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
/** /**
* *
* @author LevelX2 * @author LevelX2
*/ */
public class CanBlockOnlyFlyingAttachedEffect extends ReplacementEffectImpl implements MageSingleton {
public class CanBlockOnlyFlyingAttachedEffect extends RestrictionEffect {
public CanBlockOnlyFlyingAttachedEffect(AttachmentType attachmentType) { public CanBlockOnlyFlyingAttachedEffect(AttachmentType attachmentType) {
super(Duration.WhileOnBattlefield, Outcome.Detriment); super(Duration.WhileOnBattlefield);
if (attachmentType.equals(AttachmentType.AURA)) { if (attachmentType.equals(AttachmentType.AURA)) {
this.staticText = "Enchanted creature can block only creatures with flying"; this.staticText = "Enchanted creature can block only creatures with flying";
} else { } else {
@ -59,34 +56,19 @@ public class CanBlockOnlyFlyingAttachedEffect extends ReplacementEffectImpl impl
super(effect); super(effect);
} }
@Override
public boolean applies(Permanent permanent, Ability source, Game game) {
return permanent.getAttachments().contains(source.getSourceId());
}
@Override
public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game) {
return attacker.getAbilities().contains(FlyingAbility.getInstance());
}
@Override @Override
public CanBlockOnlyFlyingAttachedEffect copy() { public CanBlockOnlyFlyingAttachedEffect copy() {
return new CanBlockOnlyFlyingAttachedEffect(this); return new CanBlockOnlyFlyingAttachedEffect(this);
} }
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
return true;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (event.getType() == EventType.DECLARE_BLOCKER) {
Permanent attachment = game.getPermanent(source.getSourceId());
if (attachment != null && attachment.getAttachedTo() != null
&& event.getSourceId().equals(attachment.getAttachedTo())) {
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent != null && !permanent.getAbilities().containsKey(FlyingAbility.getInstance().getId())) {
return true;
}
}
}
return false;
}
} }

View file

@ -28,33 +28,24 @@
package mage.abilities.effects.common.combat; package mage.abilities.effects.common.combat;
import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.MageSingleton; import mage.abilities.effects.RestrictionEffect;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.constants.AttachmentType; import mage.constants.AttachmentType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.players.Player;
/** /**
* *
* @author LevelX2 * @author LevelX2
*/ */
public class CantAttackControllerAttachedEffect extends ReplacementEffectImpl implements MageSingleton {
/** public class CantAttackControllerAttachedEffect extends RestrictionEffect {
* The creature this permanent is attached to can't attack the controller
* of the attachment nor it's plainswalkers
*
* @param attachmentType
*/
public CantAttackControllerAttachedEffect(AttachmentType attachmentType) { public CantAttackControllerAttachedEffect(AttachmentType attachmentType) {
super(Duration.WhileOnBattlefield, Outcome.Detriment); super(Duration.WhileOnBattlefield);
if (attachmentType.equals(AttachmentType.AURA)) { if (attachmentType.equals(AttachmentType.AURA)) {
this.staticText = "Enchanted creature can't attack you or a planeswalker you control"; this.staticText = "Enchanted creature can't attack you or a planeswalker you control";
} else { } else {
@ -66,44 +57,24 @@ public class CantAttackControllerAttachedEffect extends ReplacementEffectImpl im
super(effect); super(effect);
} }
@Override
public boolean applies(Permanent permanent, Ability source, Game game) {
return permanent.getAttachments().contains(source.getSourceId());
}
@Override
public boolean canAttack(UUID defenderId, Ability source, Game game) {
if (defenderId.equals(source.getControllerId())) {
return false;
}
Permanent plainswalker = game.getPermanent(defenderId);
return plainswalker == null || !plainswalker.getControllerId().equals(source.getSourceId());
}
@Override @Override
public CantAttackControllerAttachedEffect copy() { public CantAttackControllerAttachedEffect copy() {
return new CantAttackControllerAttachedEffect(this); return new CantAttackControllerAttachedEffect(this);
} }
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
Player attackingPlayer = game.getPlayer(event.getPlayerId());
if (attackingPlayer != null && sourcePermanent != null) {
game.informPlayer(attackingPlayer,
new StringBuilder("You can't attack this player or his or her planeswalker, because the attacking creature is enchanted by ")
.append(sourcePermanent.getName()).append(".").toString());
}
return true;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (event.getType() == EventType.DECLARE_ATTACKER) {
Permanent attachment = game.getPermanent(source.getSourceId());
if (attachment != null && attachment.getAttachedTo() != null
&& event.getSourceId().equals(attachment.getAttachedTo())) {
if (event.getTargetId().equals(source.getControllerId())) {
return true;
}
Permanent plainswalker = game.getPermanent(event.getTargetId());
if (plainswalker != null && plainswalker.getControllerId().equals(source.getSourceId())) {
return true;
}
}
}
return false;
}
} }

View file

@ -50,10 +50,7 @@ public class CantAttackTargetEffect extends RestrictionEffect {
@Override @Override
public boolean applies(Permanent permanent, Ability source, Game game) { public boolean applies(Permanent permanent, Ability source, Game game) {
if (permanent.getId().equals(targetPointer.getFirst(game, source))) { return permanent.getId().equals(targetPointer.getFirst(game, source));
return true;
}
return false;
} }
@Override @Override

View file

@ -150,6 +150,14 @@ public interface Permanent extends Card, Controllable {
void setMaxBlockedBy(int maxBlockedBy); void setMaxBlockedBy(int maxBlockedBy);
boolean canAttack(Game game); boolean canAttack(Game game);
/**
*
* @param defenderId id of planeswalker or player to attack
* @param game
* @return
*/
boolean canAttack(UUID defenderId, Game game);
boolean canBlock(UUID attackerId, Game game); boolean canBlock(UUID attackerId, Game game);
boolean canBlockAny(Game game); boolean canBlockAny(Game game);

View file

@ -857,8 +857,15 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
return game.replaceEvent(GameEvent.getEvent(eventType, this.objectId, ownerId)); return game.replaceEvent(GameEvent.getEvent(eventType, this.objectId, ownerId));
} }
@Override @Override
public boolean canAttack(Game game) { public boolean canAttack(Game game) {
return canAttack(null, game);
}
@Override
public boolean canAttack(UUID defenderId, Game game) {
if (tapped) { if (tapped) {
return false; return false;
} }
@ -866,10 +873,17 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
return false; return false;
} }
//20101001 - 508.1c //20101001 - 508.1c
for (RestrictionEffect effect: game.getContinuousEffects().getApplicableRestrictionEffects(this, game).keySet()) { for (Map.Entry<RestrictionEffect, HashSet<Ability>> effectEntry: game.getContinuousEffects().getApplicableRestrictionEffects(this, game).entrySet()) {
if (!effect.canAttack(game)) { if (!effectEntry.getKey().canAttack(game)) {
return false; return false;
} }
if (defenderId != null) {
for (Ability ability :effectEntry.getValue()) {
if (!effectEntry.getKey().canAttack(defenderId, ability, game)) {
return false;
}
}
}
} }
return !abilities.containsKey(DefenderAbility.getInstance().getId()) return !abilities.containsKey(DefenderAbility.getInstance().getId())
|| game.getContinuousEffects().asThough(this.objectId, AsThoughEffectType.ATTACK, this.getControllerId(), game); || game.getContinuousEffects().asThough(this.objectId, AsThoughEffectType.ATTACK, this.getControllerId(), game);

View file

@ -321,6 +321,7 @@ public interface Player extends MageItem, Copyable<Player> {
void declareAttacker(UUID attackerId, UUID defenderId, Game game, boolean allowUndo); void declareAttacker(UUID attackerId, UUID defenderId, Game game, boolean allowUndo);
void declareBlocker(UUID defenderId, UUID blockerId, UUID attackerId, Game game); void declareBlocker(UUID defenderId, UUID blockerId, UUID attackerId, Game game);
List<Permanent> getAvailableAttackers(Game game); List<Permanent> getAvailableAttackers(Game game);
List<Permanent> getAvailableAttackers(UUID defenderId, Game game);
List<Permanent> getAvailableBlockers(Game game); List<Permanent> getAvailableBlockers(Game game);
void beginTurn(Game game); void beginTurn(Game game);

View file

@ -1673,7 +1673,7 @@ public abstract class PlayerImpl implements Player, Serializable {
setStoredBookmark(game.bookmarkState()); // makes it possible to UNDO a declared attacker with costs from e.g. Propaganda setStoredBookmark(game.bookmarkState()); // makes it possible to UNDO a declared attacker with costs from e.g. Propaganda
} }
Permanent attacker = game.getPermanent(attackerId); Permanent attacker = game.getPermanent(attackerId);
if (attacker != null && attacker.canAttack(game) && attacker.getControllerId().equals(playerId)) { if (attacker != null && attacker.canAttack(defenderId, game) && attacker.getControllerId().equals(playerId)) {
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKER, defenderId, attackerId, playerId))) { if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKER, defenderId, attackerId, playerId))) {
game.getCombat().declareAttacker(attackerId, defenderId, game); game.getCombat().declareAttacker(attackerId, defenderId, game);
} }
@ -1767,11 +1767,17 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override @Override
public List<Permanent> getAvailableAttackers(Game game) { public List<Permanent> getAvailableAttackers(Game game) {
// TODO: get available opponents and their planeswalkers, check for each if permanent can attack one
return getAvailableAttackers(null, game);
}
@Override
public List<Permanent> getAvailableAttackers(UUID defenderId, Game game) {
FilterCreatureForCombat filter = new FilterCreatureForCombat(); FilterCreatureForCombat filter = new FilterCreatureForCombat();
List<Permanent> attackers = game.getBattlefield().getAllActivePermanents(filter, playerId, game); List<Permanent> attackers = game.getBattlefield().getAllActivePermanents(filter, playerId, game);
for (Iterator<Permanent> i = attackers.iterator(); i.hasNext();) { for (Iterator<Permanent> i = attackers.iterator(); i.hasNext();) {
Permanent entry = i.next(); Permanent entry = i.next();
if (!entry.canAttack(game)) { if (!entry.canAttack(defenderId, game)) {
i.remove(); i.remove();
} }
} }