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