Package eu.semberal.migmang.logic

Source Code of eu.semberal.migmang.logic.Controller$TimerSchedule

package eu.semberal.migmang.logic;

import eu.semberal.migmang.enums.GameColor;
import eu.semberal.migmang.enums.PlayerType;
import eu.semberal.migmang.events.MigmangGameEvent;
import eu.semberal.migmang.exceptions.BadSaveGameException;
import eu.semberal.migmang.graphics.Graphics;
import java.io.File;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JOptionPane;

/**
* Controll class is responsible for the game loop. Receives events from the
* graphics.
*
* @author lukas.sembera
*/
public class Controller implements WindowEventsListener {

    private Graphics view;
    private GameStatus gameStatus;
    private Thread gameThread;
    /**
     * MoveGeneratorInterface will store moves here
     */
    private MoveContainer moveContainer;
    /**
     * Timer activated in case of replay
     */
    private Timer replayTimer;
    private boolean replayMode = false;
    /**
     * Indicates whether the timer is running. If not, the replay is paused.
     */
    private boolean replayTimeActive = false;

    @Override
    public GameStatus getGameStatus() {
        return gameStatus;
    }

    public Controller() {
        view = new Graphics();
        view.attachWindowEventsListener(this);
    }

    @Override
    public void onGameStarted(MigmangGameEvent e) {
        if (isGameInProgress()) {
            onGameClosed();
        }
        gameStatus = new GameStatus(e.getWhitePlayerType(), e.getBlackPlayerType(), e.getWhitePlayerDifficulty(), e.getBlackPlayerDifficulty(), view, view);
        view.redraw(gameStatus.getCurrentBoard());
        view.onStartGame(false);
        view.showMessage("New game has started");
        doMoveLoop();
    }

    /**
     * Game loop. Asks players to move, receivers and validates moves, etc...
     */
    private void doMoveLoop() {
        gameThread = new Thread(new Runnable() {

            public void run() {
                while (!(gameThread.isInterrupted()) & Referee.isGameEnded(gameStatus.getCurrentBoard()) == null) {
                    moveContainer = new MoveContainer();
                    new Thread(new Runnable() {

                        public void run() {
                            if (gameStatus.isWhiteOnTurn()) {
                                gameStatus.getWhitePlayer().makeMove(gameStatus.getCurrentBoard(), moveContainer);
                            } else {
                                gameStatus.getBlackPlayer().makeMove(gameStatus.getCurrentBoard(), moveContainer);
                            }
                        }
                    }).start();

                    /*
                     * Thread will wait until minimax or player store move in
                     * the container
                     */
                    while (moveContainer.getMove() == null) {
                        Thread.yield();
                    }
                    synchronized (moveContainer) {
                        Move move = moveContainer.getMove();
                        if (Referee.isValidMove(gameStatus.getCurrentBoard(), move, gameStatus.isWhiteOnTurn())) {
                            Board board = gameStatus.getCurrentBoard();

                            Referee.addCaptureInformationToMove(board, move);
                            board.addMoveWithCaptures(move);

                            gameStatus.addMove(move);
                            view.addMove(move);
                            gameStatus.setWhiteOnTurn(!gameStatus.isWhiteOnTurn());
                            view.showMessage(move.toString());
                        } else {
                            //this condition is here to not to report messages when the move is incorrect on purpuse (because of ending of minimax, etc...)
                            if (!move.isIncorrectMove()) {
                                view.showMessage("Incorrent move");
                            }
                        }
                    }
                    view.redraw(gameStatus.getCurrentBoard());
                }

                if (Referee.isGameEnded(gameStatus.getCurrentBoard()) != null) {
                    view.showImportantMessage("Game end", "Game end", JOptionPane.INFORMATION_MESSAGE);
                }
            }
        });
        gameThread.start();
    }

    private void setPausedGame() {
        if (!isGameInProgress() || isGamePaused()) {
            return;
        }
        gameThread.interrupt();
        synchronized (moveContainer) {
            moveContainer.setMove(Move.createIncorrectMove()); //puts incorrect move, so that the game loop continues and is not waiting anymore
        }
        view.disableMoves();
        while (gameThread.getState() != Thread.State.TERMINATED) { //waits until the thread stops
            Thread.yield();
        }
    }

    private void setGameResumed() {
        if (!isGameInProgress() || !isGamePaused()) {
            return;
        }
        doMoveLoop();
    }

    @Override
    public void onGameClosed() {
        setPausedGame();
        timerCancel();
        replayMode = false;
        replayTimer = null;
        gameStatus = null;
        view.showMessage("Game has ended");
        view.redraw(null);
        view.onQuitGame();
    }

    private boolean isGamePaused() {
        if (gameThread == null) {
            return true;
        }
        return gameThread.getState() == Thread.State.TERMINATED;
    }

    private boolean isGameInProgress() {
        return gameStatus != null;
    }

    @Override
    public void onGameDataChanged(MigmangGameEvent e) {
        if (!isGameInProgress()) {
            return;
        }
        boolean wasPaused = isGamePaused();
        setPausedGame();
        gameStatus.getWhitePlayer().setDifficulty(e.getWhitePlayerDifficulty());
        gameStatus.getBlackPlayer().setDifficulty(e.getBlackPlayerDifficulty());
        gameStatus.getWhitePlayer().setPlayerType(e.getWhitePlayerType());
        gameStatus.getBlackPlayer().setPlayerType(e.getBlackPlayerType());
        if (!wasPaused) {
            setGameResumed();
        }
        view.showMessage("Game preferences have been changed");
    }

    @Override
    public void onSaveGame(File kam) {
        boolean wasPaused = isGamePaused();
        setPausedGame();
        SavedGame game = new SavedGame(
                gameStatus.getMovesHistoryWithoutCapturingInfo(),
                gameStatus.getWhitePlayer().getDifficulty(),
                gameStatus.getBlackPlayer().getDifficulty(),
                gameStatus.getWhitePlayer().getPlayerType(),
                gameStatus.getBlackPlayer().getPlayerType(),
                gameStatus.getUndoedMovesCount());
        try {
            OperationsIO.saveGame(kam, game);
        } catch (Exception e) {
            view.showImportantMessage("Error", "Game has NOT been saved successfully, an error occured", JOptionPane.ERROR_MESSAGE);
        }
        if (!wasPaused) {
            setGameResumed();
        }
    }

    @Override
    public void onLoadGame(File file) {
        loadGameReguested(file, true);
    }

    private void loadGameReguested(File file, boolean standardGame) {
        if (file == null) {
            return;
        }
        try {
            SavedGame loadedGame = OperationsIO.loadGame(file);
            if (isGameInProgress()) {
                onGameClosed();
            }
            int counter = 1;
            gameStatus = new GameStatus(loadedGame.getWhitePlayerType(), loadedGame.getBlackPlayerType(), loadedGame.getWhiteDifficulty(), loadedGame.getBlackDifficulty(), view, view);
            for (Move move : loadedGame.getMoveHistory()) {
                if (move.getCapturedIndexes().length != 0
                        || !Referee.isValidMove(gameStatus.getCurrentBoard(), move, gameStatus.isWhiteOnTurn())) {
                    throw new BadSaveGameException();
                }
                Referee.addCaptureInformationToMove(gameStatus.getCurrentBoard(), move);
                gameStatus.getCurrentBoard().addMoveWithCaptures(move);
                gameStatus.addMove(move);
                gameStatus.setWhiteOnTurn(!gameStatus.isWhiteOnTurn());
                view.addMove(move);
            }

            for (int i = 0; i < loadedGame.getUndoesMovesCount(); i++) {
                moveBackRequested(false);
            }
            view.redraw(gameStatus.getCurrentBoard());

            if (standardGame) {
                view.showMessage("Game has been successfully loaded. Unpause it to continue");
                view.onStartGame(false);
            } else {
                view.showMessage("Replay has been successfully loaded");
            }
        } catch (BadSaveGameException e) {
            view.showImportantMessage("Error", "Game data are inconsistent, game has not been successfully loaded", JOptionPane.ERROR_MESSAGE);
            onGameClosed();
        } catch (Exception e) {
            view.showImportantMessage("Error", "File read error, game has not been successfully loaded", JOptionPane.ERROR_MESSAGE);
            onGameClosed();
        }
    }

    @Override
    public void onBestMoveHintRequest() {
        if (Referee.isGameEnded(gameStatus.getCurrentBoard()) != null) {
            view.showMessage("Game is already over");
            return;
        }
        if (replayMode) {
            view.showMessage("Game is in replay mode");
            return;
        }
        boolean humanOnMove = (gameStatus.isWhiteOnTurn() ? gameStatus.getWhitePlayer().getPlayerType() == PlayerType.Human : gameStatus.getBlackPlayer().getPlayerType() == PlayerType.Human);
        if (!humanOnMove) {
            view.showMessage("It is computer's turn now, best move hints are disabled");
            return;
        }

        boolean wasPaused = isGamePaused();
        setPausedGame();
        Minimax minimax = new Minimax(2);

        MoveContainer container = new MoveContainer();
        minimax.move(gameStatus.getCurrentBoard(), gameStatus.isWhiteOnTurn() ? GameColor.White : GameColor.Black, container);
        view.showMessage("Best move: " + container.getMove());

        if (!wasPaused) {
            setGameResumed();
        }
    }

    /**
     * Go half-move back in the move history
     *
     * @param handlePausing Whether pausing should be handled. This method is either called by the user in GUI. In such case
     * it is necessary to pause the game and do halfmove back. But this method might be also called many times when rewind to initial
     * board position was requested. In such case it is not necessary to pause the game because pausing has already been taken care of.
     * @return Whether is was possible to move back (not yet on the beginning)
     */
    public synchronized boolean moveBackRequested(boolean handlePausing) {
        if (!isGameInProgress()) {
            return false;
        }

        boolean wasPaused = true;
        if (handlePausing) {
            wasPaused = isGamePaused();
            setPausedGame();
        }
        try {
            gameStatus.moveBackward();
            view.dropLastMove();
            view.redraw(gameStatus.getCurrentBoard());
        } catch (Exception e) {
            return false;
        } finally {
            if (handlePausing && !wasPaused) {
                setGameResumed();
            }
        }


        return true;
    }

    @Override
    public void onMoveBackward() {
        moveBackRequested(true);
    }

    /**
     * Move half-move forward in the history
     * @see moveBackRequested()
     */
    private synchronized boolean moveForwardRequested(boolean handlePausing) {
        if (!isGameInProgress()) {
            return false;
        }

        boolean wasPaused = true;
        if (handlePausing) {
            wasPaused = isGamePaused();
            setPausedGame();
        }
        try {
            Move t = gameStatus.moveForward();
            view.redraw(gameStatus.getCurrentBoard());
            view.addMove(t);

        } catch (Exception e) {
            return false;
        } finally {
            if (handlePausing && !wasPaused) {
                setGameResumed();
            }
        }

        return true;
    }

    @Override
    public void onMoveForward() {
        moveForwardRequested(true);


    }

    @Override
    public void onMoveToStartRequested() {
        boolean wasPaused = isGamePaused();
        setPausedGame();
        while (moveBackRequested(false)) {
        }
        if (!wasPaused) {
            setGameResumed();
        }
    }

    @SuppressWarnings("empty-statement")
    @Override
    public void moveToEndRequested() {
        boolean wasPaused = isGamePaused();
        setPausedGame();
        while (moveForwardRequested(false));
        if (!wasPaused) {
            setGameResumed();
        }
    }

    @Override
    public void onTogglePausedState() {
        if (replayMode) {
            if (replayTimeActive) {
                view.showMessage("Replay has been paused");
                timerCancel();
            } else {
                view.showMessage("Replay has been resumed");
                timerStart();
            }
        } else {

            if (isGamePaused()) {
                view.showMessage("Game has been resumed");
                setGameResumed();
            } else {
                view.showMessage("Game has been paused");
                setPausedGame();
            }
        }
    }

    @Override
    public void loadReplayRequested(File file) {
        loadGameReguested(file, false);

        if (gameStatus == null) {

            return;
        }

        /*
         * If loaded game is not a replay => quit
         */
        if (Referee.isGameEnded(gameStatus.getCurrentBoard()) == null) {
            view.showImportantMessage("Load Replay error", "Saved game is not a replay", JOptionPane.WARNING_MESSAGE);
            this.onGameClosed();
            return;
        }

        onMoveToStartRequested();
        replayMode = true;
        timerStart();
        view.onStartGame(true);

    }

    @Override
    public boolean quitReplayMode() {
        if (!replayMode) {
            return false;
        }
        timerCancel();
        replayTimer = null;
        replayMode = false;
        doMoveLoop();
        return true;
    }

    private void timerStart() {
        replayTimer = new Timer();
        replayTimeActive = true;
        replayTimer.schedule(new TimerSchedule(), 0, 1500);

    }

    private void timerCancel() {
        if (!replayTimeActive) {
            return;
        }
        replayTimeActive = false;
        replayTimer.cancel();
    }

    private class TimerSchedule extends TimerTask {

        @Override
        public void run() {
            synchronized (gameStatus) {
                boolean canMoveForward = moveForwardRequested(false);
                if (!canMoveForward) { //if it cannot move forward anymore, quit the timer
                    timerCancel();
                }
            }
        }
    }
}
TOP

Related Classes of eu.semberal.migmang.logic.Controller$TimerSchedule

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.