package de.nisble.droidsweeper.game.replay; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.List; import de.nisble.droidsweeper.config.GameConfig; import de.nisble.droidsweeper.config.Level; import de.nisble.droidsweeper.game.Field; import de.nisble.droidsweeper.game.Position; import de.nisble.droidsweeper.game.database.DSDBAdapter; import de.nisble.droidsweeper.game.database.DSDBGameEntry; import de.nisble.droidsweeper.game.jni.FieldStatus; import de.nisble.droidsweeper.game.jni.GameStatus; /** Representation of a game replay.<br> * A replay is defined as a series of changes of the {@link FieldStatus states} * of {@link Field fields} at a specific time in the game. * When the game should be replayed, a timer (with the same resolution of the * timer that was used during recording) is started. When the timer fires, the * time in the next step of the replay is compared to the timer. The GUI is * updated when the two times are compared equal.<br> * For this to work a replay is structured as follows:<br> * <ul> * <li>{@link Field}: Class that represents a single field. Each instance holds * the {@link Position}, the {@link FieldStatus state} and the count of adjacent * bombs of a single field.</li> * <li>{@link TimeStep}: Class that holds the incremental changes of the game at * a specific time. Beside the play time, the {@link GameStatus} and the * remaining bombs, this class holds an array of all {@link Field fields} that * have changed its state. So only the changes of the game matrix is recorded. * Furthermore instances of this class are only produced when there are changes * to record, not at each time step.</li> * <li>{@link Replay} (this class): This class holds a series of TimeStepS and * adds the date (as epoch time), the overall play time, the dimensions of the * game matrix (as {@link GameConfig}) and the name of the player.</li> * </ul> * The replay can than be stored in the database by * {@link #serializeTimeSteps() serializing} the list of TimeStepS. The * {@link DSDBAdapter#insertTime(Replay)} abstracts this mechanism. * @author Moritz Nisblé moritz.nisble@gmx.de * @see {@link Player} for how to play a recorded replay. * @see {@link Recorder} for how to record a replay. */ public class Replay { // private static final String CLASSNAME = Replay.class.getSimpleName(); /* GameConfig is immutable */ private GameConfig mGameConfig = new GameConfig(Level.EASY); private String mName = new String(); private long mPlayTime = 0; private long mEpochTime = 0; /* TimeStepS are immutable */ private List<TimeStep> mTimeSteps = new ArrayList<TimeStep>(); /** Create an empty {@link Replay}. */ public Replay() { } /** Copy constructor. * @param r An instance to copy. */ public Replay(Replay r) { mGameConfig = r.mGameConfig; mName = r.mName; mPlayTime = r.mPlayTime; mEpochTime = r.mEpochTime; mTimeSteps = new ArrayList<TimeStep>(r.mTimeSteps); } /** Initialize a replay from a database entry. * This leads always to a standard difficulty level. * @param ge A database entry. */ public Replay(DSDBGameEntry ge) { mGameConfig = new GameConfig(ge.LEVEL); mName = ge.NAME; mPlayTime = ge.PLAYTIME; mEpochTime = ge.EPOCHTIME; mTimeSteps = ge.TIMESTEPS; } /** @return The {@link GameConfig} of this replay. */ public GameConfig getGameConfig() { return mGameConfig; } /** Set the {@link GameConfig} of this replay. * @param gameConfig The config. */ public void setGameConfig(GameConfig gameConfig) { mGameConfig = gameConfig; } /** @return The name of the game player. */ public String getName() { return mName; } /** Set the name of the game player. * @param name The name. */ public void setName(String name) { mName = name; } /** @return The play time in milliseconds. */ public long getPlayTime() { return mPlayTime; } /** Set the reached play time. * @param milliseconds The play time. */ public void setPlayTime(long milliseconds) { mPlayTime = milliseconds; } /** @return The date as epoch time (Unix time). */ public long getEpochTime() { return mEpochTime; } /** Set the date as epoch time (Unix time). * @param epochTime The date. */ public void setEpochTime(long epochTime) { mEpochTime = epochTime; } /** Push {@link TimeStep steps} to the interal list. * @param steps The {@link TimeStep steps} to add. */ public void addTimeStep(TimeStep steps) { mTimeSteps.add(steps); } /** @return The internal list of {@link TimeStep} */ public List<TimeStep> getTimeSteps() { return mTimeSteps; } /** Serialize changes in the game matrix stored as array of {@link TimeStep * TimeStepS}. * @return A byte array with the serialized array of {@link TimeStep}. * @throws IOException Thrown on error in serialization. */ public byte[] serializeTimeSteps() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oot = new ObjectOutputStream(baos); oot.writeObject(mTimeSteps); oot.close(); return baos.toByteArray(); } }