package reversi.controller; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Stack; import javax.swing.JComboBox; import reversi.model.Board; import reversi.model.Field; import reversi.model.IllegalMoveException; import reversi.model.Player; import reversi.model.Reversi; import reversi.view.ReversiField; import reversi.view.ReversiView; /** * Controller has an instance of the board and handles the * mouse and keyboard actions. Updates the gui if playBoard changes. */ public class ReversiController { private Player human = new Player(true); private Player bot = new Player(false); private Reversi model; private ReversiView view; private MachineMoveThread machineMoveThread; /** * Stack for UndoMove method. */ private Stack pastMoves; /** * Create the model and the view. */ public ReversiController() { this.model = new Reversi(human, bot, human); this.view = new ReversiView(); addListener(); this.view.updatePlayField(this.model); createStack(); } /** * View the gui. */ public final void showView() { this.view.setVisible(true); } /** * Opens a new reversi game. * * @param firstPlayer Player that starts * @return new reversi board */ private Reversi newGame(Player firstPlayer) { stopMachineMoveThread(); createStack(); Reversi model = new Reversi(this.human, this.bot, firstPlayer); model.setLevel(this.view.getLevel()); if (firstPlayer == bot) { performMachineMove(model); } else { this.view.updatePlayField(model); this.view.setMovePossible(true); } return model; } /** * Add all Listener in the gui. */ private void addListener() { setPlayFieldListeners(); this.view.setNewGameListener(new NewGameListener()); this.view.setSwitchLevelListener(new SwitchLevelListener()); this.view.setSwitchGameListener(new SwitchGameListener()); this.view.setUndoMoveListener(new UndoMoveListener()); this.view.setQuitGameListener(new QuitGameListener()); } /** * Set the MouseListener for each PlayField. */ private void setPlayFieldListeners() { for (int row = 0; row < Board.SIZE; row++) { for (int col = 0; col < Board.SIZE; col++) { this.view.setPlayFieldListener( new FieldListener(), new Field(row, col)); } } } /** * Init Stack and disable Undo Button. */ private void createStack() { this.pastMoves = new Stack(); view.setUndoButtonVisible(false); } /** * Return to the state of the board before the last human and bot move. */ private void undoMove() { stopMachineMoveThread(); this.model = pastMoves.pop(); this.view.updatePlayField(this.model); if (pastMoves.isEmpty()) { this.view.setUndoButtonVisible(false); } else { this.view.setMovePossible(true); } } private void performMachineMove(Reversi model) { this.view.setMovePossible(false); machineMoveThread = new MachineMoveThread(model, this.view); machineMoveThread.start(); } private void performHumanMove(Field field) { pastMoves.push(model.clone()); view.setUndoButtonVisible(true); if (model.move(field.row(), field.col())) { if (model.next() == human && !model.gameOver()) { javax.swing.SwingUtilities.invokeLater(new Runnable() { @Override public final void run() { view.viewMessage("The bot has to miss a turn"); } }); } } else { undoMove(); } view.updatePlayField(model); if (model.next() == bot) { performMachineMove(model); } } @SuppressWarnings("deprecation") private void stopMachineMoveThread() { if (machineMoveThread != null) { machineMoveThread.stop(); } } /** * Inner class FieldListener performs a move on click and starts the * machine move. After every move, game over check is done. */ private class FieldListener extends MouseAdapter { @Override public void mousePressed(final MouseEvent event) { if (!view.getMovePossible()) { new IllegalMoveException("Machine Move is active"); return; } Field field = ((ReversiField) event.getComponent()).getField(); performHumanMove(field); } } /** * Create Listener for the level switch combobox. */ private class SwitchLevelListener implements ActionListener { @Override public void actionPerformed(final ActionEvent e) { @SuppressWarnings("unchecked") JComboBox level = (JComboBox) e.getSource(); model.setLevel((Integer) level.getSelectedItem()); } } /** * Create Listener for the new game button. */ private class NewGameListener implements ActionListener { @Override public void actionPerformed(final ActionEvent e) { model = newGame(model.getFirstPlayer()); } } /** * Create Listener for the switch game button. */ private class SwitchGameListener implements ActionListener { @Override public void actionPerformed(final ActionEvent e) { if (model.getFirstPlayer() == human) { model = newGame(bot); } else { model = newGame(human); } } } /** * Create Listener for the undo move button. */ private class UndoMoveListener implements ActionListener { @Override public void actionPerformed(final ActionEvent e) { undoMove(); } } /** * Create Listener for the quit game button. */ private class QuitGameListener implements ActionListener { @Override public void actionPerformed(final ActionEvent e) { for (Frame frame : java.awt.Frame.getFrames()) { frame.dispose(); } stopMachineMoveThread(); } } }