diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index a0cfcf465f..a15029bccb 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -35,6 +35,7 @@ package mage.client; import mage.Constants; +import mage.interfaces.Action; import mage.cards.Card; import mage.cards.decks.Deck; import mage.client.cards.CardsStorage; @@ -181,10 +182,10 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { this.setExtendedState(JFrame.MAXIMIZED_BOTH); session = new Session(this); - session.setEmbeddedMageServerAction(new mage.interfaces.Action() { + session.setEmbeddedMageServerAction(new Action() { @Override public void execute() { - Main.main(new String[] {}); + Main.main(new String[]{}); } }); callbackClient = new CallbackClientImpl(this); diff --git a/Mage.Common/src/mage/interfaces/Action.java b/Mage.Common/src/mage/interfaces/Action.java index 3ead56d4c2..5ff3ae5831 100644 --- a/Mage.Common/src/mage/interfaces/Action.java +++ b/Mage.Common/src/mage/interfaces/Action.java @@ -1,7 +1,8 @@ package mage.interfaces; /** - * Action interface + * Light weight action interface + * For executing actions without any context. * * @author ayratn */ diff --git a/Mage.Server/plugins/mage-deck-constructed.jar b/Mage.Server/plugins/mage-deck-constructed.jar index 8ea92082fa..0efd592ff1 100644 Binary files a/Mage.Server/plugins/mage-deck-constructed.jar and b/Mage.Server/plugins/mage-deck-constructed.jar differ diff --git a/Mage.Server/plugins/mage-deck-limited.jar b/Mage.Server/plugins/mage-deck-limited.jar index fd7b0a98b9..4ba3991905 100644 Binary files a/Mage.Server/plugins/mage-deck-limited.jar and b/Mage.Server/plugins/mage-deck-limited.jar differ diff --git a/Mage.Server/plugins/mage-game-freeforall.jar b/Mage.Server/plugins/mage-game-freeforall.jar index a10ff5eeaf..451a3fac56 100644 Binary files a/Mage.Server/plugins/mage-game-freeforall.jar and b/Mage.Server/plugins/mage-game-freeforall.jar differ diff --git a/Mage.Server/plugins/mage-game-twoplayerduel.jar b/Mage.Server/plugins/mage-game-twoplayerduel.jar index a02d71567e..6d7db63412 100644 Binary files a/Mage.Server/plugins/mage-game-twoplayerduel.jar and b/Mage.Server/plugins/mage-game-twoplayerduel.jar differ diff --git a/Mage.Server/plugins/mage-player-ai-ma.jar b/Mage.Server/plugins/mage-player-ai-ma.jar index 0f416d0b87..ea051d4d1d 100644 Binary files a/Mage.Server/plugins/mage-player-ai-ma.jar and b/Mage.Server/plugins/mage-player-ai-ma.jar differ diff --git a/Mage.Server/plugins/mage-player-ai.jar b/Mage.Server/plugins/mage-player-ai.jar index ecc3ba72d8..7bea513e96 100644 Binary files a/Mage.Server/plugins/mage-player-ai.jar and b/Mage.Server/plugins/mage-player-ai.jar differ diff --git a/Mage.Server/plugins/mage-player-aimcts.jar b/Mage.Server/plugins/mage-player-aimcts.jar index 4670c0f7d5..5cc37f6447 100644 Binary files a/Mage.Server/plugins/mage-player-aimcts.jar and b/Mage.Server/plugins/mage-player-aimcts.jar differ diff --git a/Mage.Server/plugins/mage-player-aiminimax.jar b/Mage.Server/plugins/mage-player-aiminimax.jar index 437c1572c7..be792e4a2d 100644 Binary files a/Mage.Server/plugins/mage-player-aiminimax.jar and b/Mage.Server/plugins/mage-player-aiminimax.jar differ diff --git a/Mage.Server/plugins/mage-player-human.jar b/Mage.Server/plugins/mage-player-human.jar index 9fd545ac09..e96938bf36 100644 Binary files a/Mage.Server/plugins/mage-player-human.jar and b/Mage.Server/plugins/mage-player-human.jar differ diff --git a/Mage.Server/plugins/mage-tournament-booster-draft.jar b/Mage.Server/plugins/mage-tournament-booster-draft.jar index 6729138bfb..457cf53f7e 100644 Binary files a/Mage.Server/plugins/mage-tournament-booster-draft.jar and b/Mage.Server/plugins/mage-tournament-booster-draft.jar differ diff --git a/Mage.Server/plugins/mage-tournament-sealed.jar b/Mage.Server/plugins/mage-tournament-sealed.jar index cec9fec81b..ecc7ad1b17 100644 Binary files a/Mage.Server/plugins/mage-tournament-sealed.jar and b/Mage.Server/plugins/mage-tournament-sealed.jar differ diff --git a/Mage/src/mage/Constants.java b/Mage/src/mage/Constants.java index a9bf6544de..a245704d5c 100644 --- a/Mage/src/mage/Constants.java +++ b/Mage/src/mage/Constants.java @@ -274,26 +274,36 @@ public final class Constants { } public enum PhaseStep { - UNTAP ("Untap"), - UPKEEP ("Upkeep"), - DRAW ("Draw"), - PRECOMBAT_MAIN ("Precombat Main"), - BEGIN_COMBAT ("Begin Combat"), - DECLARE_ATTACKERS ("Declare Attackers"), - DECLARE_BLOCKERS ("Declare Blockers"), - FIRST_COMBAT_DAMAGE ("First Combat Damage"), - COMBAT_DAMAGE ("Combat Damage"), - END_COMBAT ("End Combat"), - POSTCOMBAT_MAIN ("Postcombat Main"), - END_TURN ("End Turn"), - CLEANUP ("Cleanup"); + UNTAP ("Untap", 0), + UPKEEP ("Upkeep", 1), + DRAW ("Draw", 2), + PRECOMBAT_MAIN ("Precombat Main", 3), + BEGIN_COMBAT ("Begin Combat", 4), + DECLARE_ATTACKERS ("Declare Attackers", 5), + DECLARE_BLOCKERS ("Declare Blockers", 6), + FIRST_COMBAT_DAMAGE ("First Combat Damage", 7), + COMBAT_DAMAGE ("Combat Damage", 8), + END_COMBAT ("End Combat", 9), + POSTCOMBAT_MAIN ("Postcombat Main", 10), + END_TURN ("End Turn", 11), + CLEANUP ("Cleanup", 12); private String text; - PhaseStep(String text) { + /** + * Index is used for game state scoring system. + */ + private int index; + + PhaseStep(String text, int index) { this.text = text; + this.index = index; } + public int getIndex() { + return index; + } + @Override public String toString() { return text; diff --git a/Mage/src/mage/actions/MageDrawAction.java b/Mage/src/mage/actions/MageDrawAction.java new file mode 100644 index 0000000000..a530215114 --- /dev/null +++ b/Mage/src/mage/actions/MageDrawAction.java @@ -0,0 +1,90 @@ +package mage.actions; + +import mage.Constants; +import mage.actions.impl.MageAction; +import mage.actions.score.ArtificialScoringSystem; +import mage.cards.Card; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; + +import java.util.List; + +/** + * Action for drawing cards. + * + * @author ayrat + */ +public class MageDrawAction extends MageAction { + + private final Player player; + private final int amount; + private List<Card> drawnCards; + + private static final int NEGATIVE_VALUE = -1000000; + + public MageDrawAction(Player player, int amount) { + this.player = player; + this.amount = amount; + } + + /** + * Draw and set action score. + * + * @param game Game context. + */ + @Override + public int doAction(Game game) { + int numDrawn = 0; + int score = 0; + for (int i = 0; i < amount; i++) { + int value = drawCard(game); + if (value == -1) { + break; + } + score += value; + } + game.fireInformEvent(player.getName() + " draws " + Integer.toString(numDrawn) + " card" + (numDrawn > 1 ? "s" : "")); + if (player.isEmptyDraw()) { + game.doAction(new MageLoseGameAction(player, MageLoseGameAction.DRAW_REASON)); + } + + setScore(player, score); + game.setStateCheckRequired(); + + return numDrawn; + } + + /** + * Draw a card if possible (there is no replacement effect that prevent us from drawing). + * Fire event about card drawn. + * + * @param game + * @return + */ + protected int drawCard(Game game) { + if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DRAW_CARD, player.getId(), player.getId()))) { + Card card = player.getLibrary().removeFromTop(game); + if (card != null) { + card.moveToZone(Constants.Zone.HAND, null, game, false); + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DREW_CARD, card.getId(), player.getId())); + return ArtificialScoringSystem.inst.getCardScore(card); + } + } + return NEGATIVE_VALUE; + } + + /** + * Return a card back to top. + * + * @param game Game context + */ + @Override + public void undoAction(Game game) { + for (int index = drawnCards.size() - 1; index >= 0; index--) { + Card card = drawnCards.get(index); + player.getHand().remove(card); + player.getLibrary().putOnTop(card, game); + } + } +} diff --git a/Mage/src/mage/actions/MageLoseGameAction.java b/Mage/src/mage/actions/MageLoseGameAction.java new file mode 100644 index 0000000000..c31831b107 --- /dev/null +++ b/Mage/src/mage/actions/MageLoseGameAction.java @@ -0,0 +1,43 @@ +package mage.actions; + +import mage.actions.impl.MageAction; +import mage.actions.score.ArtificialScoringSystem; +import mage.game.Game; +import mage.players.Player; + +/** + * Lose game action. + * + * @author ayratn + */ +public class MageLoseGameAction extends MageAction { + + public static final String LIFE_REASON = "{L} You lose the game."; + public static final String POISON_REASON = "{L} You are poisoned. You lose the game."; + public static final String DRAW_REASON = "{L} You attempt to draw from an empty library. You lose the game."; + + private final Player player; + private final String reason; + private Player oldLosingPlayer; + + public MageLoseGameAction(final Player player, final String reason) { + this.player = player; + this.reason = reason; + } + + @Override + public int doAction(final Game game) { + oldLosingPlayer = game.getLosingPlayer(); + if (oldLosingPlayer == null && player.canLose(game)) { + setScore(player, ArtificialScoringSystem.inst.getLoseGameScore(game)); + game.setLosingPlayer(player); + game.informPlayer(player, reason); + } + return 0; + } + + @Override + public void undoAction(final Game game) { + game.setLosingPlayer(oldLosingPlayer); + } +} \ No newline at end of file diff --git a/Mage/src/mage/actions/impl/MageAction.java b/Mage/src/mage/actions/impl/MageAction.java new file mode 100644 index 0000000000..538e773e3a --- /dev/null +++ b/Mage/src/mage/actions/impl/MageAction.java @@ -0,0 +1,68 @@ +package mage.actions.impl; + +import mage.game.Game; +import mage.players.Player; + +/** + * Base class for mage actions. + * + * @author ayratn + */ +public abstract class MageAction { + + /** + * {@link Player} we count score for. + */ + private Player scorePlayer; + + /** + * Current game score for the player. + */ + private int score = 0; + + /** + * Set or change action score. + * + * @param scorePlayer Set player. + * @param score Set score value. + */ + protected void setScore(Player scorePlayer, int score) { + this.scorePlayer = scorePlayer; + this.score = score; + } + + /** + * Get game score for the {@link Player}. + * Value depends on the owner of this action. + * In case player and owner differ, negative value is returned. + * + * @param player + * @return + */ + public int getScore(final Player player) { + if (player.getId().equals(scorePlayer.getId())) { + return score; + } else { + return -score; + } + } + + /** + * Execute action. + * + * @param game Game context. + */ + public abstract int doAction(final Game game); + + /** + * Undo action. + * + * @param game Game context + */ + public abstract void undoAction(final Game game); + + @Override + public String toString() { + return ""; + } +} diff --git a/Mage/src/mage/actions/score/ArtificialScoringSystem.java b/Mage/src/mage/actions/score/ArtificialScoringSystem.java new file mode 100644 index 0000000000..8b08a2a3a0 --- /dev/null +++ b/Mage/src/mage/actions/score/ArtificialScoringSystem.java @@ -0,0 +1,38 @@ +package mage.actions.score; + +import mage.cards.Card; +import mage.game.Game; +import org.apache.log4j.Logger; + +/** + * @author ayratn + */ +public class ArtificialScoringSystem implements ScoringSystem { + + public static ArtificialScoringSystem inst; + + private static final transient Logger log = Logger.getLogger(ArtificialScoringSystem.class); + + static { + inst = new ArtificialScoringSystem(); + log.info("ArtificialScoringSystem has been instantiated."); + } + + /** + * Lose score is lowered in function of the turn and phase when it occurs. + * Encourages AI to win as fast as possible. + * + * @param game + * @return + */ + public int getLoseGameScore(final Game game) { + return ScoringConstants.LOSE_GAME_SCORE + game.getTurnNum() * 2500 + game.getStep().getType().getIndex() * 200; + } + + @Override + public int getCardScore(Card card) { + //TODO: implement + return ScoringConstants.UNKNOWN_CARD_SCORE; + } + +} \ No newline at end of file diff --git a/Mage/src/mage/actions/score/ScoringConstants.java b/Mage/src/mage/actions/score/ScoringConstants.java new file mode 100644 index 0000000000..7003e5effb --- /dev/null +++ b/Mage/src/mage/actions/score/ScoringConstants.java @@ -0,0 +1,13 @@ +package mage.actions.score; + +/** + * Constants for scoring system. + * + * @author ayratn + */ +public class ScoringConstants { + public static final int WIN_GAME_SCORE = 100000000; + public static final int LOSE_GAME_SCORE = -WIN_GAME_SCORE; + + public static final int UNKNOWN_CARD_SCORE = 300; +} diff --git a/Mage/src/mage/actions/score/ScoringSystem.java b/Mage/src/mage/actions/score/ScoringSystem.java new file mode 100644 index 0000000000..d80a387bac --- /dev/null +++ b/Mage/src/mage/actions/score/ScoringSystem.java @@ -0,0 +1,12 @@ +package mage.actions.score; + +import mage.cards.Card; +import mage.game.Game; + +/** + * @author ayratn + */ +public interface ScoringSystem { + public int getLoseGameScore(final Game game); + public int getCardScore(final Card card); +} diff --git a/Mage/src/mage/game/Game.java b/Mage/src/mage/game/Game.java index ae695a2489..14a90f9bcc 100644 --- a/Mage/src/mage/game/Game.java +++ b/Mage/src/mage/game/Game.java @@ -28,6 +28,7 @@ package mage.game; +import mage.actions.impl.MageAction; import mage.game.match.MatchType; import mage.cards.Card; import mage.game.stack.SpellStack; @@ -113,6 +114,10 @@ public interface Game extends MageItem, Serializable { public Card getLastKnownInformation(UUID objectId, Zone zone); public void rememberLKI(UUID objectId, Zone zone, Card card); public void resetLKI(); + public void setLosingPlayer(Player player); + public Player getLosingPlayer(); + public void setStateCheckRequired(); + public boolean getStateCheckRequired(); //client event methods public void addTableEventListener(Listener<TableEvent> listener); @@ -134,6 +139,7 @@ public interface Game extends MageItem, Serializable { public void fireInformEvent(String message); public void fireUpdatePlayersEvent(); public void informPlayers(String message); + public void informPlayer(Player player, String message); public void debugMessage(String message); public void fireErrorEvent(String message, Exception ex); @@ -160,6 +166,8 @@ public interface Game extends MageItem, Serializable { public void playPriority(UUID activePlayerId, boolean resuming); public boolean endTurn(UUID playerId); + public int doAction(MageAction action); + //game transaction methods public void saveState(); public int bookmarkState(); diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index dd5cfd6020..e77029ba36 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -36,6 +36,7 @@ import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffects; import mage.abilities.keyword.LeylineAbility; import mage.abilities.mana.TriggeredManaAbility; +import mage.actions.impl.MageAction; import mage.cards.Card; import mage.cards.Cards; import mage.cards.CardsImpl; @@ -108,6 +109,12 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa public static volatile int copyCount = 0; public static volatile long copyTime = 0; + private LinkedList<MageAction> actions; + private Player scorePlayer; + private int score = 0; + private Player losingPlayer; + private boolean stateCheckRequired = false; + @Override public abstract T copy(); @@ -116,6 +123,7 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa this.range = range; this.attackOption = attackOption; this.state = new GameState(); + this.actions = new LinkedList<MageAction>(); } public GameImpl(final GameImpl<T> game) { @@ -142,6 +150,9 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa copyCount++; copyTime += (System.currentTimeMillis() - t1); } + this.actions = new LinkedList<MageAction>(); + this.stateCheckRequired = game.stateCheckRequired; + this.scorePlayer = game.scorePlayer; } @Override @@ -365,6 +376,7 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa public void start(UUID choosingPlayerId, GameOptions options) { startTime = new Date(); this.gameOptions = options; + scorePlayer = state.getPlayers().values().iterator().next(); init(choosingPlayerId, options.testMode); play(startingPlayerId); saveState(); @@ -1329,6 +1341,14 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa return true; } + @Override + public int doAction(MageAction action) { + actions.add(action); + int value = action.doAction(this); + score += action.getScore(scorePlayer); + return value; + } + @Override public Date getStartTime() { return startTime; @@ -1343,4 +1363,29 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa public void setGameOptions(GameOptions options) { this.gameOptions = options; } + + @Override + public void setLosingPlayer(Player player) { + this.losingPlayer = player; + } + + @Override + public Player getLosingPlayer() { + return this.losingPlayer; + } + + @Override + public void informPlayer(Player player, String message) { + //TODO: implement personal messages + } + + @Override + public boolean getStateCheckRequired() { + return stateCheckRequired; + } + + @Override + public void setStateCheckRequired() { + stateCheckRequired = true; + } } diff --git a/Mage/src/mage/players/Library.java b/Mage/src/mage/players/Library.java index 52dc0a1199..0bdf079efb 100644 --- a/Mage/src/mage/players/Library.java +++ b/Mage/src/mage/players/Library.java @@ -197,7 +197,7 @@ public class Library implements Serializable { } - public boolean isEmtpyDraw() { + public boolean isEmptyDraw() { return emptyDraw; } diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index c955601106..3c2bcecb69 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -114,6 +114,7 @@ public interface Player extends MageItem, Copyable<Player> { public void setTopCardRevealed(boolean topCardRevealed); public UserData getUserData(); public void setUserData(UserData userData); + public boolean canLose(Game game); /** * Returns a set of players which turns under you control. diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index aa863fa2d7..de0cc796dd 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -53,6 +53,7 @@ import mage.abilities.effects.common.LoseControlOnOtherPlayersControllerEffect; import mage.abilities.keyword.*; import mage.abilities.mana.ManaAbility; import mage.abilities.mana.ManaOptions; +import mage.actions.MageDrawAction; import mage.cards.Card; import mage.cards.Cards; import mage.cards.CardsImpl; @@ -60,7 +61,6 @@ import mage.cards.decks.Deck; import mage.counters.Counter; import mage.counters.CounterType; import mage.counters.Counters; -import mage.filter.FilterAbility; import mage.filter.common.FilterCreatureForCombat; import mage.game.Game; import mage.game.combat.CombatGroup; @@ -337,29 +337,9 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser return false; } - protected boolean drawCard(Game game) { - if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DRAW_CARD, playerId, playerId))) { - Card card = getLibrary().removeFromTop(game); - if (card != null) { - card.moveToZone(Zone.HAND, null, game, false); - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DREW_CARD, card.getId(), playerId)); - return true; - } - } - return false; - } - @Override public int drawCards(int num, Game game) { - int numDrawn = 0; - for (int i = 0; i < num; i++) { - if (drawCard(game)) - numDrawn++; - else - break; - } - game.fireInformEvent(name + " draws " + Integer.toString(numDrawn) + " card" + (numDrawn > 1?"s":"")); - return numDrawn; + return game.doAction(new MageDrawAction(this, num)); } @Override @@ -992,7 +972,7 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser @Override public boolean isEmptyDraw() { - return library.isEmtpyDraw(); + return library.isEmptyDraw(); } @Override @@ -1029,7 +1009,7 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser @Override public void lost(Game game) { - if (!game.replaceEvent(new GameEvent(GameEvent.EventType.LOSES, null, null, playerId))) { + if (canLose(game)) { this.loses = true; //20100423 - 603.9 if (!this.wins) @@ -1038,6 +1018,11 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser } } + @Override + public boolean canLose(Game game) { + return !game.replaceEvent(new GameEvent(GameEvent.EventType.LOSES, null, null, playerId)); + } + @Override public void won(Game game) { if (!game.replaceEvent(new GameEvent(GameEvent.EventType.WINS, null, null, playerId))) {