
com.codingame.gameengine.core.GameManager Maven / Gradle / Ivy
package com.codingame.gameengine.core;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Scanner;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
/**
* The GameManager
takes care of running each turn of the game and computing each visual frame of the replay. It provides many utility
* methods that handle instances of your implementation of AbstractPlayer.
*
* @param
* Your implementation of AbstractPlayer
*/
@Singleton
public final class GameManager {
@Inject private Provider playerProvider;
@Inject private Provider refereeProvider;
@Inject private Gson gson;
protected static Log log = LogFactory.getLog(GameManager.class);
private static final int VIEW_DATA_SOFT_QUOTA = 512 * 1024;
private static final int VIEW_DATA_HARD_QUOTA = 1024 * 1024;
private List players;
private int maxTurns = 400;
private int turnMaxTime = 50;
private int firstTurnMaxTime = 1000;
private Integer turn = null;
private int frame = 0;
private boolean gameEnd = false;
private Scanner s;
private PrintStream out;
private Properties gameProperties;
private AbstractReferee referee;
private boolean newTurn;
private List currentTooltips = new ArrayList<>();
private List prevTooltips;
private List currentGameSummary = new ArrayList<>();
private List prevGameSummary;
private JsonObject currentViewData, prevViewData;
private int frameDuration = 1000;
private JsonObject globalViewData = new JsonObject();
private List registeredModules = new ArrayList<>();
private boolean initDone = false;
private boolean outputsRead = false;
private int totalViewDataBytesSent = 0;
/**
* GameManager main loop.
*
* @param is
* input stream used to read commands from Game
* @param out
* print stream used to issue commands to Game
*/
void start(InputStream is, PrintStream out) {
s = new Scanner(is);
this.out = out;
this.referee = refereeProvider.get();
// Init ---------------------------------------------------------------
log.info("Init");
InputCommand iCmd = InputCommand.parse(s.nextLine());
int playerCount = s.nextInt();
s.nextLine();
players = new ArrayList(playerCount);
for (int i = 0; i < playerCount; i++) {
T player = playerProvider.get();
player.setIndex(i);
players.add(player);
}
// create game properties
gameProperties = new Properties();
if (iCmd.lineCount > 0) {
for (int i = 0; i < (iCmd.lineCount - 1); i++) {
try {
gameProperties.load(new StringReader(s.nextLine()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
if (!gameProperties.containsKey("seed")) {
gameProperties.setProperty("seed", String.valueOf(ThreadLocalRandom.current().nextInt()));
}
prevViewData = null;
currentViewData = new JsonObject();
gameProperties = referee.init(gameProperties);
registeredModules.forEach(Module::onGameInit);
swapInfoAndViewData();
initDone = true;
// Game Loop ----------------------------------------------------------
for (turn = 0; turn < getMaxTurns() && !isGameEnd(); turn++) {
log.info("Turn " + turn);
newTurn = true;
outputsRead = false; // Set as true after first getOutputs() to forbib sendInputs
referee.gameTurn(turn);
registeredModules.forEach(Module::onAfterGameTurn);
// reset players' outputs
for (AbstractPlayer player : players) {
player.resetOutputs();
player.setHasBeenExecuted(false);
}
swapInfoAndViewData();
}
log.info("End");
referee.onEnd();
registeredModules.forEach(Module::onAfterOnEnd);
// Send last frame ----------------------------------------------------
newTurn = true;
dumpView();
dumpInfos();
dumpGameProperties();
dumpScores();
s.close();
}
/**
* Executes a player for a maximum of turnMaxTime milliseconds and store the output. Used by player.execute().
*
* @param player
* Player to execute.
*/
void execute(T player) {
if (!this.initDone) {
throw new RuntimeException("Impossible to execute a player during init phase.");
}
player.setTimeout(false);
InputCommand iCmd = InputCommand.parse(s.nextLine());
if (iCmd.cmd != InputCommand.Command.GET_GAME_INFO) {
throw new RuntimeException("Invalid command: " + iCmd.cmd);
}
dumpView();
dumpInfos();
dumpNextPlayerInput(player.getInputs().toArray(new String[0]));
dumpNextPlayerInfos(player.getIndex(), player.getExpectedOutputLines(), player.hasNeverBeenExecuted() ? firstTurnMaxTime : turnMaxTime);
// READ PLAYER OUTPUTS
iCmd = InputCommand.parse(s.nextLine());
if (iCmd.cmd == InputCommand.Command.SET_PLAYER_OUTPUT) {
List output = new ArrayList<>(iCmd.lineCount);
for (int i = 0; i < iCmd.lineCount; i++) {
output.add(s.nextLine());
}
player.setOutputs(output);
} else if (iCmd.cmd == InputCommand.Command.SET_PLAYER_TIMEOUT) {
player.setTimeout(true);
} else {
throw new RuntimeException("Invalid command: " + iCmd.cmd);
}
player.resetInputs();
newTurn = false;
}
/**
* Swap game summary and view.
*
* As these values are sent in the first call to gameManger.execute(player), this function allows to change the current view at the end of each
* gameTurn instead of the middle of the gameTurn.
*/
private void swapInfoAndViewData() {
prevViewData = currentViewData;
currentViewData = new JsonObject();
prevGameSummary = currentGameSummary;
currentGameSummary = new ArrayList<>();
prevTooltips = currentTooltips;
currentTooltips = new ArrayList<>();
}
private void dumpGameProperties() {
out.println(OutputCommand.UINPUT.format(gameProperties.size()));
for (Entry
© 2015 - 2025 Weber Informatics LLC | Privacy Policy