package de.nisble.droidsweeper.game.jni;
import java.util.ArrayList;
import java.util.List;
import de.nisble.droidsweeper.config.GameConfig;
import de.nisble.droidsweeper.game.Position;
/** Abstraction of the native library libmsm that implements the game logic.
* <ul>
* <li>Singleton: Use the public INSTANCE member.</li>
* </ul>
* @author Moritz Nisblé moritz.nisble@gmx.de */
public final class MineSweeperMatrix {
private static final String CLASSNAME = MineSweeperMatrix.class.getSimpleName();
/** Get the one and only instance of this class. */
public static final MineSweeperMatrix INSTANCE = new MineSweeperMatrix();
private List<MatrixObserver> observer = new ArrayList<MatrixObserver>();
private MineSweeperMatrix() {
System.loadLibrary("msm");
init();
}
// TODO: Find another solution!
@Override
protected void finalize() throws Throwable {
free();
super.finalize();
}
/* Native methods. */
private native void init();
private native void free();
private native void nativeSetFieldListener(Object obj, int x, int y) throws Exception;
private native void nativeCreate(int size_x, int size_y, int bombs);
private native int nativeGameStatus();
private native int nativeRemainingBombs();
private native int nativeReveal(int x, int y) throws IndexOutOfBoundsException;
private native void nativeCycleMark(int x, int y) throws IndexOutOfBoundsException;
// Called from native code after game status has changed
private void onGameStatusChanged(GameStatus gs) {
for (MatrixObserver l : observer) {
l.onGameStatusChanged(gs);
}
}
// Called from native code when remaining bomb count has changed
private void onRemainingBombsChanged(int remainingBombs) {
for (MatrixObserver l : observer) {
l.onRemainingBombsChanged(remainingBombs);
}
}
// Called from native code after a field was altered
private void afterFieldStatusChanged(int x, int y, int adjacentBombs, FieldStatus fs) {
for (MatrixObserver l : observer) {
l.afterFieldStatusChanged(new Position(x, y), fs, adjacentBombs);
}
}
/* Public interface */
/** Set the field callback for to a given widget.
* @param l A field implementing the FieldListener interface. */
public void setFieldListener(FieldListener l) throws Exception {
nativeSetFieldListener((Object) l, l.getPosition().X, l.getPosition().Y);
}
/** Add a matrix observer.
* @param l A matrix observer. */
public void addMatrixObserver(MatrixObserver l) {
if (!observer.contains(l))
observer.add(l);
}
/** Remove a matrix observer.
* @param l The observer to remove. */
public void removeObserver(MatrixObserver l) {
observer.remove(l);
}
/** Create a new game with the given config.
* @param prefs */
public void create(GameConfig c) {
nativeCreate(c.X, c.Y, c.BOMBS);
}
/** Get the current game status.
* @return The current GameStatus. */
public GameStatus gameStatus() {
return GameStatus.fromInt(nativeGameStatus());
}
/** Is libmsm initialized and ready for a new game.
* @return true on ready. */
public boolean isReady() {
return GameStatus.READY == GameStatus.fromInt(nativeGameStatus());
}
/** Is currently a game running?
* @return true on running game. */
public boolean isRunning() {
return GameStatus.RUNNING == GameStatus.fromInt(nativeGameStatus());
}
/** Game lost?
* @return true on lost. */
public boolean isLost() {
return GameStatus.LOST == GameStatus.fromInt(nativeGameStatus());
}
/** Game won?
* @return true on won. */
public boolean isWon() {
return GameStatus.WON == GameStatus.fromInt(nativeGameStatus());
}
/** Get the count of the remaining bombs on the grid.
* This number can also be negative when the player has marked more field
* than bombs are present.
* @return The count of remaining bombs. */
public int remainingBombs() {
return nativeRemainingBombs();
}
/** Reveal a field.
* @param p The position.
* @return The count of the adjacent bombs.
* @throws NullPointerException on coordinates of the position out of the
* bound of the configured game matrix. */
public int reveal(Position p) throws IndexOutOfBoundsException {
return nativeReveal(p.X, p.Y);
}
/** Cycle through the sequence of marks of a field.
* Marked (not revealable), Queried (revealable), Hidden (revealable)
* @param p The position.
* @throws NullPointerException on coordinates of the position out of the
* bound of the configured game matrix. */
public void cycleMark(Position p) throws IndexOutOfBoundsException {
nativeCycleMark(p.X, p.Y);
}
}