- implement HeadlessGameController with no UI dependencies from DeckStrCal.
- Use a much simplified initialize() for DeckStrCal.master
parent
28a6c439b5
commit
7f516c3347
|
@ -1,5 +1,6 @@
|
||||||
package magic;
|
package magic;
|
||||||
|
|
||||||
|
import magic.headless.HeadlessGameController;
|
||||||
import magic.ai.MagicAI;
|
import magic.ai.MagicAI;
|
||||||
import magic.ai.MagicAIImpl;
|
import magic.ai.MagicAIImpl;
|
||||||
import magic.data.DeckUtils;
|
import magic.data.DeckUtils;
|
||||||
|
@ -8,10 +9,10 @@ import magic.model.MagicDuel;
|
||||||
import magic.model.MagicGame;
|
import magic.model.MagicGame;
|
||||||
import magic.model.MagicGameLog;
|
import magic.model.MagicGameLog;
|
||||||
import magic.model.MagicRandom;
|
import magic.model.MagicRandom;
|
||||||
import magic.ui.SwingGameController;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import magic.data.CardDefinitions;
|
||||||
import magic.exception.handler.ConsoleExceptionHandler;
|
import magic.exception.handler.ConsoleExceptionHandler;
|
||||||
|
import magic.utility.ProgressReporter;
|
||||||
|
|
||||||
public class DeckStrCal {
|
public class DeckStrCal {
|
||||||
|
|
||||||
|
@ -163,9 +164,7 @@ public class DeckStrCal {
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load cards and cubes.
|
initialize();
|
||||||
MagicMain.initializeEngine();
|
|
||||||
MagicGameLog.initialize();
|
|
||||||
|
|
||||||
for (int i = 0; i < repeat; i++) {
|
for (int i = 0; i < repeat; i++) {
|
||||||
runDuel();
|
runDuel();
|
||||||
|
@ -193,7 +192,8 @@ public class DeckStrCal {
|
||||||
while (testDuel.getGamesPlayed() < testDuel.getGamesTotal()) {
|
while (testDuel.getGamesPlayed() < testDuel.getGamesTotal()) {
|
||||||
final MagicGame game=testDuel.nextGame();
|
final MagicGame game=testDuel.nextGame();
|
||||||
game.setArtificial(true);
|
game.setArtificial(true);
|
||||||
final SwingGameController controller=new SwingGameController(game);
|
|
||||||
|
final HeadlessGameController controller = new HeadlessGameController(game);
|
||||||
|
|
||||||
//maximum duration of a game is 60 minutes
|
//maximum duration of a game is 60 minutes
|
||||||
controller.setMaxTestGameDuration(3600000);
|
controller.setMaxTestGameDuration(3600000);
|
||||||
|
@ -226,4 +226,9 @@ public class DeckStrCal {
|
||||||
(testDuel.getGamesPlayed() - testDuel.getGamesWon())
|
(testDuel.getGamesPlayed() - testDuel.getGamesWon())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void initialize() {
|
||||||
|
CardDefinitions.loadCardDefinitions(new ProgressReporter());
|
||||||
|
MagicGameLog.initialize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,7 +137,7 @@ public class MagicMain {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void initializeEngine() {
|
private static void initializeEngine() {
|
||||||
if (Boolean.getBoolean("parseMissing")) {
|
if (Boolean.getBoolean("parseMissing")) {
|
||||||
UnimplementedParser.parseScriptsMissing(reporter);
|
UnimplementedParser.parseScriptsMissing(reporter);
|
||||||
reporter.setMessage("Parsing card abilities...");
|
reporter.setMessage("Parsing card abilities...");
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue