- implement HeadlessGameController with no UI dependencies from DeckStrCal.

- Use a much simplified initialize() for DeckStrCal.
master
lodici 2015-02-17 19:14:50 +00:00
parent 28a6c439b5
commit 7f516c3347
3 changed files with 306 additions and 7 deletions

View File

@ -1,5 +1,6 @@
package magic;
import magic.headless.HeadlessGameController;
import magic.ai.MagicAI;
import magic.ai.MagicAIImpl;
import magic.data.DeckUtils;
@ -8,10 +9,10 @@ import magic.model.MagicDuel;
import magic.model.MagicGame;
import magic.model.MagicGameLog;
import magic.model.MagicRandom;
import magic.ui.SwingGameController;
import java.io.File;
import magic.data.CardDefinitions;
import magic.exception.handler.ConsoleExceptionHandler;
import magic.utility.ProgressReporter;
public class DeckStrCal {
@ -163,9 +164,7 @@ public class DeckStrCal {
System.exit(1);
}
// Load cards and cubes.
MagicMain.initializeEngine();
MagicGameLog.initialize();
initialize();
for (int i = 0; i < repeat; i++) {
runDuel();
@ -193,7 +192,8 @@ public class DeckStrCal {
while (testDuel.getGamesPlayed() < testDuel.getGamesTotal()) {
final MagicGame game=testDuel.nextGame();
game.setArtificial(true);
final SwingGameController controller=new SwingGameController(game);
final HeadlessGameController controller = new HeadlessGameController(game);
//maximum duration of a game is 60 minutes
controller.setMaxTestGameDuration(3600000);
@ -226,4 +226,9 @@ public class DeckStrCal {
(testDuel.getGamesPlayed() - testDuel.getGamesWon())
);
}
private static void initialize() {
CardDefinitions.loadCardDefinitions(new ProgressReporter());
MagicGameLog.initialize();
}
}

View File

@ -137,7 +137,7 @@ public class MagicMain {
}
}
static void initializeEngine() {
private static void initializeEngine() {
if (Boolean.getBoolean("parseMissing")) {
UnimplementedParser.parseScriptsMissing(reporter);
reporter.setMessage("Parsing card abilities...");

View File

@ -0,0 +1,294 @@
package magic.headless;
import magic.exception.UndoClickedException;
import magic.ai.MagicAI;
import magic.model.ILogBookListener;
import magic.model.MagicCardList;
import magic.model.MagicGame;
import magic.model.MagicLogBookEvent;
import magic.model.MagicPlayer;
import magic.model.MagicSource;
import magic.model.event.MagicEvent;
import magic.model.event.MagicPriorityEvent;
import magic.model.target.MagicTarget;
import magic.model.target.MagicTargetNone;
import java.util.List;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import magic.model.IGameController;
import magic.model.MagicColor;
import magic.model.MagicManaCost;
import magic.model.MagicSubType;
import magic.model.choice.MagicPlayChoiceResult;
public class HeadlessGameController implements IGameController, ILogBookListener {
private long MAX_TEST_MODE_DURATION=10000;
private final MagicGame game;
// isDeckStrMode is true when game is run via DeckStrengthViewer or DeckStrCal.
private final boolean isDeckStrMode;
private final AtomicBoolean running = new AtomicBoolean(false);
private final AtomicBoolean isPaused = new AtomicBoolean(false);
private boolean actionClicked;
private boolean resetGame;
private MagicTarget choiceClicked = MagicTargetNone.getInstance();
private final BlockingQueue<Boolean> input = new SynchronousQueue<>();
/** Fully artificial test game. */
public HeadlessGameController(final MagicGame aGame) {
game = aGame;
isDeckStrMode = true;
}
@Override
public void enableForwardButton() {}
@Override
public void disableActionButton(final boolean thinking) {}
@Override
public void pause(final int t) {
try { //sleep
Thread.sleep(t);
} catch (final InterruptedException ex) {
throw new RuntimeException(ex);
}
}
/** Returns true when undo was clicked. */
private boolean waitForInputOrUndo() {
try {
return input.take();
} catch (final InterruptedException ex) {
throw new RuntimeException(ex);
}
}
@Override
public void waitForInput() throws UndoClickedException {
try {
final boolean undoClicked = input.take();
if (undoClicked) {
throw new UndoClickedException();
}
} catch (final InterruptedException ex) {
throw new RuntimeException(ex);
}
}
private void resume(final boolean undoClicked) {
input.offer(undoClicked);
}
private void undoClicked() {
if (game.hasUndoPoints()) {
actionClicked = false;
choiceClicked = MagicTargetNone.getInstance();
setSourceCardDefinition(MagicEvent.NO_SOURCE);
clearValidChoices();
resume(true);
}
}
@Override
public boolean isActionClicked() {
return actionClicked;
}
@SuppressWarnings("unchecked")
@Override
public <T> T getChoiceClicked() {
return (T)choiceClicked;
}
public void setMaxTestGameDuration(final long duration) {
MAX_TEST_MODE_DURATION = duration;
}
@Override
public void setSourceCardDefinition(final MagicSource source) {}
@Override
public void focusViewers(final int handGraveyard) {}
@Override
public void clearCards() {
showCards(new MagicCardList());
}
@Override
public void showCards(final MagicCardList cards) {}
@Override
public void clearValidChoices() {}
@Override
public void setValidChoices(final Set<?> aValidChoices,final boolean aCombatChoice) {}
/**
* Update/render the gui based on the model state.
*/
@Override
public void updateGameView() { }
@Override
public void showMessage(final MagicSource source, final String message) {
System.out.println(message);
}
private Object[] getArtificialNextEventChoiceResults(final MagicEvent event) {
if (!isDeckStrMode) {
disableActionButton(true);
showMessage(event.getSource(),event.getChoiceDescription());
}
//dynamically get the AI based on the player's index
final MagicPlayer player = event.getPlayer();
final MagicAI ai = game.getDuel().getAIs()[player.getIndex()];
return ai.findNextEventChoiceResults(game, player);
}
private void executeNextEventWithChoices(final MagicEvent event) {
game.executeNextEvent(getArtificialNextEventChoiceResults(event));
}
private void performUndo() {
if (resetGame) {
resetGame=false;
while (game.hasUndoPoints()) {
game.restore();
}
} else {
game.restore();
}
}
/**
* Main game loop runs on separate thread.
*/
public void runGame() {
final long startTime=System.currentTimeMillis();
running.set(true);
while (running.get()) {
if (isPaused.get()) {
pause(100);
} else if (game.isFinished()) {
doNextActionOnGameFinished();
} else {
executeNextEventOrPhase();
if (isDeckStrMode) {
if (System.currentTimeMillis() - startTime > MAX_TEST_MODE_DURATION) {
System.err.println("WARNING. Max time for AI game exceeded");
running.set(false);
}
} else {
updateGameView();
}
}
}
}
/**
* Once a game has finished determine what happens next.
* <p>
* If running an automated game then automatically start next game/duel.
* If an interactive game then wait for input from user.
*/
private void doNextActionOnGameFinished() {
if (isDeckStrMode) {
game.advanceDuel(false);
running.set(false);
} else {
game.logMessages();
clearValidChoices();
game.advanceDuel(false);
running.set(false);
}
}
private void executeNextEventOrPhase() {
if (game.hasNextEvent()) {
executeNextEvent();
} else {
game.executePhase();
}
}
private void executeNextEvent() {
final MagicEvent event=game.getNextEvent();
if (event instanceof MagicPriorityEvent) {
game.logMessages();
}
if (event.hasChoice()) {
executeNextEventWithChoices(event);
} else {
game.executeNextEvent();
}
}
@Override
public void messageLogged(final MagicLogBookEvent ev) {}
@Override
public MagicSubType getLandSubTypeChoice(final MagicSource source) throws UndoClickedException {
return null;
}
@Override
public boolean getPayBuyBackCostChoice(final MagicSource source, final String costText) throws UndoClickedException {
return false;
}
@Override
public MagicColor getColorChoice(final MagicSource source) throws UndoClickedException {
return null;
}
@Override
public int getMultiKickerCountChoice(
final MagicSource source,
final MagicManaCost cost,
final int maximumCount,
final String name) throws UndoClickedException {
return 0;
}
@Override
public int getSingleKickerCountChoice(
final MagicSource source,
final MagicManaCost cost,
final String name) throws UndoClickedException {
return 0;
}
@Override
public boolean getMayChoice(final MagicSource source, final String description) throws UndoClickedException {
return false;
}
@Override
public boolean getTakeMulliganChoice(
final MagicSource source,
final MagicPlayer player) throws UndoClickedException {
return false;
}
@Override
public int getModeChoice(final MagicSource source, final List<Integer> availableModes) throws UndoClickedException {
return 0;
}
@Override
public int getPayManaCostXChoice(final MagicSource source, final int maximumX) throws UndoClickedException {
return 0;
}
@Override
public MagicPlayChoiceResult getPlayChoice(final MagicSource source, final List<MagicPlayChoiceResult> results) throws UndoClickedException {
return null;
}
}