1
0
Fork 0
mirror of https://github.com/correl/mage.git synced 2025-04-05 17:00:10 -09:00

[MAD2.0] New scoring and undo system. Migrated draw cards action.

This commit is contained in:
magenoxx 2011-12-25 11:42:32 +04:00
parent 24dea6a02c
commit 3f20b725db
25 changed files with 357 additions and 42 deletions

View file

@ -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);

View file

@ -1,7 +1,8 @@
package mage.interfaces;
/**
* Action interface
* Light weight action interface
* For executing actions without any context.
*
* @author ayratn
*/

View file

@ -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;

View file

@ -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);
}
}
}

View file

@ -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);
}
}

View file

@ -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 "";
}
}

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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();

View file

@ -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;
}
}

View file

@ -197,7 +197,7 @@ public class Library implements Serializable {
}
public boolean isEmtpyDraw() {
public boolean isEmptyDraw() {
return emptyDraw;
}

View file

@ -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.

View file

@ -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))) {