fix java shit
This commit is contained in:
1 parent
3db4f94e6a
commit
c3f4512037
38 files changed
+5
No files matched your search
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,42 @@
|
||||
package reversi.controller;
|
||||
|
||||
import reversi.model.Player;
|
||||
import reversi.model.Reversi;
|
||||
import reversi.view.ReversiView;
|
||||
|
||||
/**
|
||||
* Creates a new thread for the machine move.
|
||||
*/
|
||||
public class MachineMoveThread extends Thread {
|
||||
|
||||
private Reversi model;
|
||||
private ReversiView view;
|
||||
private Player bot;
|
||||
|
||||
MachineMoveThread(final Reversi model, final ReversiView view) {
|
||||
this.model = model;
|
||||
this.view = view;
|
||||
this.bot = model.next();
|
||||
}
|
||||
@Override
|
||||
public final void run() {
|
||||
this.model.machineMove();
|
||||
javax.swing.SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public final void run() {
|
||||
view.updatePlayField(model);
|
||||
view.setMovePossible(true);
|
||||
}
|
||||
});
|
||||
|
||||
if (model.next() == bot && !model.gameOver()) {
|
||||
this.run();
|
||||
javax.swing.SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public final void run() {
|
||||
view.viewMessage("Human has to miss a turn");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,237 @@
|
||||
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<Reversi> 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<Reversi>();
|
||||
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<Integer> level = (JComboBox<Integer>) 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
||||
package reversi.main;
|
||||
|
||||
import reversi.controller.ReversiController;
|
||||
|
||||
/**
|
||||
* Starts a Reversi Game with the gui.
|
||||
*/
|
||||
public final class ReversiGame {
|
||||
|
||||
private ReversiController controller;
|
||||
|
||||
private ReversiGame() {
|
||||
this.controller = new ReversiController();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param args unused...
|
||||
*/
|
||||
public static void main(final String...args) {
|
||||
ReversiGame reversiGame = new ReversiGame();
|
||||
reversiGame.controller.showView();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,121 @@
|
||||
package reversi.model;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for a Reversi game, also known as Othello.
|
||||
*
|
||||
* A human plays against the machine.
|
||||
*/
|
||||
public interface Board extends Cloneable {
|
||||
|
||||
/**
|
||||
* The number of rows and columns of the game grid. Originally 8.
|
||||
* Here, even and at least 4.
|
||||
*/
|
||||
int SIZE = 8;
|
||||
|
||||
/**
|
||||
* Get the player who should start or already has started the game.
|
||||
*
|
||||
* @return The player who makes the initial move.
|
||||
*/
|
||||
Player getFirstPlayer();
|
||||
|
||||
/**
|
||||
* Get the player who owns the next game turn.
|
||||
*
|
||||
* @return The player who is allowed to make the next turn.
|
||||
*/
|
||||
Player next();
|
||||
|
||||
/**
|
||||
* Execute a human move.
|
||||
*
|
||||
* @param row The slot's row number where a tile of the human player should
|
||||
* be placed on.
|
||||
* @param col The slot's column number where a tile of the human player
|
||||
* should be placed on.
|
||||
* @return {@code true} if and only if move was successful, e.g., the
|
||||
* defined slot was not occupied and at least one tile of the
|
||||
* machine was reversed.
|
||||
* @throws IllegalMoveException If the game is already over, or it is not
|
||||
* the human's turn.
|
||||
* @throws IllegalArgumentException If the provided parameters are
|
||||
* invalid, e.g., the defined slot was is not on the grid.
|
||||
*/
|
||||
boolean move(int row, int col);
|
||||
|
||||
/**
|
||||
* Execute a machine move.
|
||||
*
|
||||
* @throws IllegalMoveException If the game is already over, or it is not
|
||||
* the machine's turn.
|
||||
*/
|
||||
void machineMove();
|
||||
|
||||
/**
|
||||
* Set the skill level of the machine.
|
||||
*
|
||||
* @param level The skill as a number, must be at least 1.
|
||||
*/
|
||||
void setLevel(int level);
|
||||
|
||||
/**
|
||||
* Check if the game is over. Either one player has won or there is a tie,
|
||||
* i.e., no player can perform a move any more.
|
||||
*
|
||||
* @return {@code true} if and only if the game is over.
|
||||
*/
|
||||
boolean gameOver();
|
||||
|
||||
/**
|
||||
* Check if the game state is won.
|
||||
*
|
||||
* @return The winner or nobody in case of a tie.
|
||||
*/
|
||||
Player getWinner();
|
||||
|
||||
/**
|
||||
* Get the number of human tiles currently placed on the grid.
|
||||
*
|
||||
* @return The number of human tiles.
|
||||
*/
|
||||
int getNumberOfHumanTiles();
|
||||
|
||||
/**
|
||||
* Get the number of machine tiles currently placed on the grid.
|
||||
*
|
||||
* @return The number of machine tiles.
|
||||
*/
|
||||
int getNumberOfMachineTiles();
|
||||
|
||||
/**
|
||||
* Get the content of the slot at the specified coordinates. Either it
|
||||
* contains a tile of one of the two players already or it is empty.
|
||||
*
|
||||
* @param row The row of the slot in the game grid.
|
||||
* @param col The column of the slot in the game grid.
|
||||
* @return The slot's content.
|
||||
*/
|
||||
Player getSlot(int row, int col);
|
||||
|
||||
/**
|
||||
* Deep-copy the board.
|
||||
*
|
||||
* @return A clone.
|
||||
*/
|
||||
Board clone();
|
||||
|
||||
/**
|
||||
* Get the string representation of this board as row x column matrix. Each
|
||||
* slot is represented by one the three chars '.', 'X', or 'O'. '.' means
|
||||
* that the slot currently contains no tile. 'X' means that it contains a
|
||||
* tile of the human player. 'O' means that it contains a machine tile. In
|
||||
* contrast to the rows, the columns are whitespace separated.
|
||||
*
|
||||
* @return The string representation of the current Reversi game.
|
||||
*/
|
||||
@Override
|
||||
String toString();
|
||||
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,42 @@
|
||||
package reversi.model;
|
||||
|
||||
/**
|
||||
* Class for a reversi field.
|
||||
*/
|
||||
public class Field {
|
||||
|
||||
/**
|
||||
* <code>Row</code> of the field.
|
||||
*/
|
||||
private int row;
|
||||
|
||||
/**
|
||||
* <code>Column</code> of the field.
|
||||
*/
|
||||
private int col;
|
||||
|
||||
/**
|
||||
* Constructs a new field.
|
||||
*
|
||||
* @param row row of the field
|
||||
* @param col column of the field
|
||||
*/
|
||||
public Field(final int row, final int col) {
|
||||
this.row = row;
|
||||
this.col = col;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return The row of the field.
|
||||
*/
|
||||
public final int row() {
|
||||
return row;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return the column of the field.
|
||||
*/
|
||||
public final int col() {
|
||||
return col;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,164 @@
|
||||
package reversi.model;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Class for a gamenode. Is needed to build a gametree for e.g. minmax
|
||||
* algorithm. A node represents a virtual move.
|
||||
*
|
||||
*/
|
||||
public class GameNode {
|
||||
|
||||
/**
|
||||
* Reversi-board of the node.
|
||||
*/
|
||||
private Reversi state;
|
||||
|
||||
/**
|
||||
* Scoring of the node.
|
||||
*/
|
||||
private double score;
|
||||
|
||||
/**
|
||||
* The Move.
|
||||
*/
|
||||
private Field move;
|
||||
|
||||
/**
|
||||
* Children of the node.
|
||||
*/
|
||||
private LinkedList<GameNode> children;
|
||||
|
||||
/**
|
||||
* Constructor for the root node.
|
||||
*
|
||||
* @param move Root move
|
||||
* @param state Board of the root node
|
||||
*/
|
||||
GameNode(final Field move, final Reversi state) {
|
||||
this.move = move;
|
||||
this.state = state;
|
||||
this.children = new LinkedList<GameNode>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for node.
|
||||
*
|
||||
* @param move Move of node
|
||||
* @param state Board of node
|
||||
* @param parent Parent of node
|
||||
*/
|
||||
GameNode(final Field move, final Reversi state, final GameNode parent) {
|
||||
this(move, state);
|
||||
parent.children.add(this);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param score Score to set
|
||||
*/
|
||||
public final void setScore(final double score) {
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Score
|
||||
*/
|
||||
public final double getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Board
|
||||
*/
|
||||
public final Reversi getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Move
|
||||
*/
|
||||
public final Field getMove() {
|
||||
return move;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Children
|
||||
*/
|
||||
public final LinkedList<GameNode> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is needed to calculate the combined score of the parent node.
|
||||
*
|
||||
* @return GameNode with highest score
|
||||
*/
|
||||
public final GameNode getChildrensHighestScore() {
|
||||
GameNode highest = children.getFirst();
|
||||
|
||||
for (GameNode node : children) {
|
||||
if (node.getScore() > highest.getScore()) {
|
||||
highest = node;
|
||||
}
|
||||
}
|
||||
return highest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is needed to calculate the combined score of the parent node.
|
||||
*
|
||||
* @return GameNode with lowest score
|
||||
*/
|
||||
public final GameNode getChildrensLowestScore() {
|
||||
GameNode lowest = children.getFirst();
|
||||
|
||||
for (GameNode node : children) {
|
||||
if (node.getScore() < lowest.getScore()) {
|
||||
lowest = node;
|
||||
}
|
||||
}
|
||||
return lowest;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return If node has at least one child
|
||||
*/
|
||||
public final boolean hasChild() {
|
||||
return (children.size() > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set children of a node.
|
||||
*/
|
||||
public final void fillNode() {
|
||||
for (Field move : generateChildren()) {
|
||||
Reversi tmpBoard = this.getState().clone();
|
||||
tmpBoard.performMove(move.row(), move.col());
|
||||
new GameNode(move, tmpBoard, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return <code>Linkedlist</code> with possible moves in the
|
||||
* current reversi-board.
|
||||
*/
|
||||
private LinkedList<Field> generateChildren() {
|
||||
LinkedList<Field> movesList = new LinkedList<Field>();
|
||||
|
||||
for (int row = 0; row < Board.SIZE; row++) {
|
||||
for (int col = 0; col < Board.SIZE; col++) {
|
||||
if (getState().isValidMove(row, col)) {
|
||||
movesList.add(new Field(row, col));
|
||||
}
|
||||
}
|
||||
}
|
||||
return movesList;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,75 @@
|
||||
package reversi.model;
|
||||
|
||||
/**
|
||||
* Class for a GameTree.
|
||||
*/
|
||||
public class GameTree {
|
||||
|
||||
/**
|
||||
* Root gamenode of the gametree.
|
||||
*/
|
||||
private GameNode root;
|
||||
|
||||
/**
|
||||
* Constructs a new gametree.
|
||||
*
|
||||
* @param board of the root node
|
||||
* @param level depth of the tree
|
||||
*/
|
||||
GameTree(final Reversi board, final int level) {
|
||||
this.root = new GameNode(new Field(0, 0), board.clone());
|
||||
fillTree(root, level);
|
||||
generateScores(root);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Root of the gametree.
|
||||
*/
|
||||
public final GameNode getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the Tree with nodes.
|
||||
*
|
||||
* @param node Node to fill (in most cases root gamenode)
|
||||
* @param depth depth of the tree (level)
|
||||
*/
|
||||
private void fillTree(final GameNode node, final int depth) {
|
||||
if (depth > 0) {
|
||||
node.fillNode();
|
||||
|
||||
for (GameNode n : node.getChildren()) {
|
||||
fillTree(n, (depth - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and sets the score of a Gamenode.
|
||||
*
|
||||
* @param node Gamenode to set
|
||||
*/
|
||||
public final void generateScores(final GameNode node) {
|
||||
Reversi currentBoard = node.getState();
|
||||
|
||||
if (!node.hasChild()) {
|
||||
node.setScore(currentBoard.score());
|
||||
} else {
|
||||
double score;
|
||||
score = currentBoard.score();
|
||||
|
||||
for (GameNode child : node.getChildren()) {
|
||||
generateScores(child);
|
||||
}
|
||||
|
||||
if (!currentBoard.next().getType()) {
|
||||
score += node.getChildrensHighestScore().getScore();
|
||||
} else {
|
||||
score += node.getChildrensLowestScore().getScore();
|
||||
}
|
||||
node.setScore(score);
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,31 @@
|
||||
package reversi.model;
|
||||
|
||||
import java.awt.Toolkit;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
/**
|
||||
* Class for a Illegal move in reversi game.
|
||||
*/
|
||||
public class IllegalMoveException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 240330120795325167L;
|
||||
|
||||
/**
|
||||
* Prints an error or plays a "beep" if a illegal move is made.
|
||||
* A beep is more common but not so easy to implement for various
|
||||
* OS, so the sound is only played on Windows XP or 7.
|
||||
*
|
||||
* @param s Error to print
|
||||
*/
|
||||
public IllegalMoveException(final String s) {
|
||||
if (System.getProperty("os.name").equals("Windows 7")
|
||||
|| System.getProperty("os.name").equals("Windows XP")) {
|
||||
Toolkit.getDefaultToolkit().beep();
|
||||
} else {
|
||||
JFrame screen = new JFrame();
|
||||
JOptionPane.showMessageDialog(screen, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,27 @@
|
||||
package reversi.model;
|
||||
/**
|
||||
* Class for a player of reversi.
|
||||
*/
|
||||
public class Player {
|
||||
/**
|
||||
* Indicates if a player is human.
|
||||
*/
|
||||
private Boolean isHuman;
|
||||
|
||||
/**
|
||||
* Constructs a new player.
|
||||
*
|
||||
* @param isHuman is the player human?
|
||||
*/
|
||||
public Player(final boolean isHuman) {
|
||||
this.isHuman = isHuman;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Is player human?
|
||||
*/
|
||||
public final boolean getType() {
|
||||
return isHuman;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,527 @@
|
||||
package reversi.model;
|
||||
|
||||
/**
|
||||
* Class for reversi game. A human player tries to beat a bot with modifiable
|
||||
* difficulty level.
|
||||
*/
|
||||
public class Reversi implements Board {
|
||||
|
||||
private Player bot;
|
||||
private Player human;
|
||||
|
||||
/**
|
||||
* Player with the next move.
|
||||
*/
|
||||
private Player currentPlayer;
|
||||
private Player firstPlayer;
|
||||
|
||||
/**
|
||||
* Playboard saves the tiles.
|
||||
*/
|
||||
private Player[][] playBoard;
|
||||
|
||||
/**
|
||||
* Gametree for the bot to calculate future moves.
|
||||
*/
|
||||
private GameTree gameTree;
|
||||
|
||||
/**
|
||||
* Vectors to traverse the 8 directions around a tile.
|
||||
*/
|
||||
private Field[] vectors;
|
||||
|
||||
/**
|
||||
* Saves if game is over.
|
||||
*/
|
||||
private boolean gameOver;
|
||||
|
||||
/**
|
||||
* Standard weighting of the playboard. Used by the bot to calculate
|
||||
* gametree.
|
||||
*/
|
||||
private double[][] weighting = new double[][]
|
||||
{{9999, 5, 500, 200, 200, 500, 5, 9999},
|
||||
{5, 1, 50, 150, 150, 50, 1, 5},
|
||||
{500, 50, 250, 100, 100, 250, 50, 500},
|
||||
{200, 150, 100, 50, 50, 100, 150, 200},
|
||||
{200, 150, 100, 50, 50, 100, 150, 200},
|
||||
{500, 50, 250, 100, 100, 250, 50, 500},
|
||||
{5, 1, 50, 150, 150, 50, 1, 5},
|
||||
{9999, 5, 500, 200, 200, 500, 5, 9999}};
|
||||
|
||||
/**
|
||||
* Difficulty level for the human. Represents the depth of the gametree.
|
||||
*/
|
||||
private int level = 3;
|
||||
|
||||
/**
|
||||
* Constructs a new reversi game.
|
||||
*
|
||||
* @param human Human player
|
||||
* @param bot Machine player
|
||||
* @param firstPlayer Player that starts
|
||||
*/
|
||||
public Reversi(Player human, Player bot, Player firstPlayer) {
|
||||
this.bot = bot;
|
||||
this.human = human;
|
||||
this.firstPlayer = firstPlayer;
|
||||
this.playBoard = setInitialPositions();
|
||||
currentPlayer = firstPlayer;
|
||||
this.vectors = generateVectors();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final Player getFirstPlayer() {
|
||||
return firstPlayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final Player next() {
|
||||
return currentPlayer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final boolean move(final int row, final int col) {
|
||||
if (currentPlayer != human) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isValidMove(row, col)) {
|
||||
new IllegalMoveException("Error! invalid move at ("
|
||||
+ (row + 1) + ", " + (col + 1) + ").");
|
||||
return false;
|
||||
}
|
||||
performMove(row, col);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final void machineMove() {
|
||||
if (currentPlayer != bot) {
|
||||
return;
|
||||
}
|
||||
gameTree = new GameTree(this, level);
|
||||
Field bestMove = gameTree.getRoot()
|
||||
.getChildrensHighestScore().getMove();
|
||||
|
||||
performMove(bestMove.row(), bestMove.col());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final void setLevel(final int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final boolean gameOver() {
|
||||
return gameOver;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final Player getWinner() {
|
||||
if (getNumberOfHumanTiles() > getNumberOfMachineTiles()) {
|
||||
return human;
|
||||
} else if (getNumberOfHumanTiles() == getNumberOfMachineTiles()) {
|
||||
return null;
|
||||
} else {
|
||||
return bot;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final int getNumberOfHumanTiles() {
|
||||
return numberOfTiles(human);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final int getNumberOfMachineTiles() {
|
||||
return numberOfTiles(bot);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final int getNumberOfTilesSet() {
|
||||
int n = (getNumberOfHumanTiles() + getNumberOfMachineTiles());
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final Player getSlot(final int row, final int col) {
|
||||
return playBoard[row][col];
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Reversi clone() {
|
||||
Reversi boardCopy = new Reversi(human, bot, firstPlayer);
|
||||
boardCopy.currentPlayer = currentPlayer;
|
||||
Player[][] playBoardCopy = new Player[SIZE][SIZE];
|
||||
|
||||
for (int row = 0; row < SIZE; row++) {
|
||||
for (int col = 0; col < SIZE; col++) {
|
||||
playBoardCopy[row][col] = playBoard[row][col];
|
||||
}
|
||||
}
|
||||
boardCopy.playBoard = playBoardCopy;
|
||||
return boardCopy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the vectors for the 8 directions. array[0] = 0°,
|
||||
* array[1] = 45°...
|
||||
*
|
||||
* @return <code>boolean-array</code> with all vector fields
|
||||
*/
|
||||
private Field[] generateVectors() {
|
||||
Field[] vector = {
|
||||
new Field(-1, 0),
|
||||
new Field(-1, +1),
|
||||
new Field(0, +1),
|
||||
new Field(+1, +1),
|
||||
new Field(+1, 0),
|
||||
new Field(+1, -1),
|
||||
new Field(0, -1),
|
||||
new Field(-1, -1)};
|
||||
return vector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets two tiles/player at the beginning of the match in the middle of
|
||||
* of the playboard.
|
||||
*
|
||||
* @return <code>Player-array</code>
|
||||
*/
|
||||
private Player[][] setInitialPositions() {
|
||||
Player[][] playBoard = new Player[SIZE][SIZE];
|
||||
|
||||
for (int row = 0; row < SIZE; row++) {
|
||||
for (int col = 0; col < SIZE; col++) {
|
||||
if (((SIZE / 2) - 1 == row || SIZE / 2 == row)
|
||||
&& ((SIZE - col - 1) == row)) {
|
||||
playBoard[row][col] = firstPlayer;
|
||||
} else if ((SIZE / 2 - 1 == row || SIZE / 2 == row)
|
||||
&& (col == row)) {
|
||||
playBoard[row][col] = getEnemy(firstPlayer);
|
||||
} else {
|
||||
playBoard[row][col] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return playBoard;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param self Player
|
||||
* @return Enemy of the Player
|
||||
*/
|
||||
private Player getEnemy(final Player self) {
|
||||
if (self == human) {
|
||||
return bot;
|
||||
} else {
|
||||
return human;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the currentplayer and checks if the next player can do a valid
|
||||
* move. If both players cannot move the game is over.
|
||||
* game is over
|
||||
*/
|
||||
private void switchPlayer() {
|
||||
currentPlayer = getEnemy(currentPlayer);
|
||||
|
||||
if (!movePossible()) {
|
||||
currentPlayer = getEnemy(currentPlayer);
|
||||
|
||||
if (!movePossible()) {
|
||||
gameOver = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch a tile to the current player.
|
||||
*
|
||||
* @param row Row of field
|
||||
* @param col Column of field
|
||||
*/
|
||||
private void switchTile(final int row, final int col) {
|
||||
playBoard[row][col] = currentPlayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch all corresponding enemy tiles around a <code>field</code>.
|
||||
*
|
||||
* @param row Row of field
|
||||
* @param col Column of field
|
||||
*/
|
||||
private void switchTiles(final int row, final int col) {
|
||||
boolean[] surrounders = validMoves(row, col);
|
||||
switchTile(row, col);
|
||||
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
if (surrounders[i]) {
|
||||
int x = row + vectors[i].row();
|
||||
int y = col + vectors[i].col();
|
||||
|
||||
while (playBoard[x][y] != currentPlayer) {
|
||||
switchTile(x, y);
|
||||
x = x + vectors[i].row();
|
||||
y = y + vectors[i].col();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int numberOfTiles(Player player) {
|
||||
int mount = 0;
|
||||
|
||||
for (int row = 0; row < SIZE; row++) {
|
||||
for (int col = 0; col < SIZE; col++) {
|
||||
if (playBoard[row][col] == player) {
|
||||
mount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return mount;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param row Row of the field
|
||||
* @param col Column of the field
|
||||
* @return <code>true</code> if field with <code>row</code> and
|
||||
* <code>col</code> is not set by one of the
|
||||
* player <br /> else if field is set
|
||||
*/
|
||||
private boolean isFree(final int row, final int col) {
|
||||
if (playBoard[row][col] == null) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the hole playBoard if the current player can do a valid move.
|
||||
*
|
||||
* @return <code>true</code> if another move is possible.
|
||||
*/
|
||||
private boolean movePossible() {
|
||||
for (int row = 0; row < SIZE; row++) {
|
||||
for (int col = 0; col < SIZE; col++) {
|
||||
|
||||
if (isValidMove(row, col)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk the board in a direction.
|
||||
*
|
||||
* @param row Row of field
|
||||
* @param col Column of field
|
||||
* @param x row direction to walk
|
||||
* @param y column direction to walk
|
||||
* @return if the direction has a valid move
|
||||
*/
|
||||
private boolean boardWalker(int row, int col, final int x, final int y) {
|
||||
row = row + x;
|
||||
col = col + y;
|
||||
int counter = 0;
|
||||
|
||||
while (row < SIZE && row >= 0 && col < SIZE && col >= 0) {
|
||||
if (playBoard[row][col] == null) {
|
||||
return false;
|
||||
} else if (playBoard[row][col] == currentPlayer) {
|
||||
|
||||
return (counter > 0);
|
||||
}
|
||||
row = row + x;
|
||||
col = col + y;
|
||||
counter++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a boolean array if it contains a value that is true.
|
||||
*
|
||||
* @param array Array
|
||||
* @return <code>true</code> if any value is true
|
||||
*/
|
||||
private boolean checkBoolArray(final boolean[] array) {
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (array[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param row Row of field
|
||||
* @param col Column of field
|
||||
* @return if the currentPlayer can do a move on field(row, col)
|
||||
*/
|
||||
final boolean isValidMove(final int row, final int col) {
|
||||
return isFree(row, col)
|
||||
&& checkBoolArray(validMoves(row, col));
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches a field for valid moves.
|
||||
*
|
||||
* @param row Row of field
|
||||
* @param col Column of field
|
||||
* @return <code>Array</code> of enemy tiles: <br />
|
||||
* array[0] is at 0°, array[1] is diagonal 45°,<br />
|
||||
* array[2] right 90° and so on
|
||||
*/
|
||||
private boolean[] validMoves(final int row, final int col) {
|
||||
boolean[] neighbourhood = new boolean[8];
|
||||
|
||||
for (int i = 0; i < neighbourhood.length; i++) {
|
||||
neighbourhood[i] = boardWalker(row, col,
|
||||
vectors[i].row(), vectors[i].col());
|
||||
}
|
||||
return neighbourhood;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches all tiles and changes switches player if possible.
|
||||
*
|
||||
* @param row Row of field
|
||||
* @param col Column of field
|
||||
*/
|
||||
void performMove(final int row, final int col) {
|
||||
switchTiles(row, col);
|
||||
switchPlayer();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the score of a reversi.
|
||||
*
|
||||
* @return <code>score</code>
|
||||
*/
|
||||
double score() {
|
||||
double score = (scoreT() + scoreM() + scoreP());
|
||||
return score;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the weighting score.
|
||||
*
|
||||
* @return <code>scoreT</code>
|
||||
*/
|
||||
private double scoreT() {
|
||||
double scoreSelf = 0;
|
||||
double scoreEnemy = 0;
|
||||
|
||||
for (int row = 0; row < SIZE; row++) {
|
||||
for (int col = 0; col < SIZE; col++) {
|
||||
if (playBoard[row][col] == bot) {
|
||||
scoreSelf += weighting[row][col];
|
||||
} else if (playBoard[row][col] == human) {
|
||||
scoreEnemy += weighting[row][col];
|
||||
}
|
||||
}
|
||||
}
|
||||
return (scoreSelf - (1.5 * scoreEnemy));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the mobility score.
|
||||
*
|
||||
* @return <code>scoreM</code>
|
||||
*/
|
||||
private double scoreM() {
|
||||
double scoreSelf = 0;
|
||||
double scoreEnemy = 0;
|
||||
Player tmp = currentPlayer;
|
||||
int n = getNumberOfTilesSet();
|
||||
|
||||
for (int row = 0; row < SIZE; row++) {
|
||||
for (int col = 0; col < SIZE; col++) {
|
||||
currentPlayer = bot;
|
||||
|
||||
if (isValidMove(row, col)) {
|
||||
scoreSelf++;
|
||||
}
|
||||
currentPlayer = human;
|
||||
|
||||
if (isValidMove(row, col)) {
|
||||
scoreEnemy++;
|
||||
}
|
||||
currentPlayer = bot;
|
||||
}
|
||||
}
|
||||
currentPlayer = tmp;
|
||||
return (64.0 / n) * (3.0 * scoreSelf - 4.0 * scoreEnemy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the future-potential score.
|
||||
*
|
||||
* @return <code>scoreP</code>
|
||||
*/
|
||||
private double scoreP() {
|
||||
double scoreSelf = 0;
|
||||
double scoreEnemy = 0;
|
||||
int n = getNumberOfTilesSet();
|
||||
|
||||
for (int row = 0; row < SIZE; row++) {
|
||||
for (int col = 0; col < SIZE; col++) {
|
||||
if (playBoard[row][col] == human) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int x = row + vectors[i].row();
|
||||
int y = col + vectors[i].col();
|
||||
|
||||
if ((x >= 0 && x < SIZE && y >= 0 && y < SIZE)
|
||||
&& (playBoard[x][y] == null)) {
|
||||
scoreSelf++;
|
||||
}
|
||||
}
|
||||
} else if (playBoard[row][col] == bot) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int x = row + vectors[i].row();
|
||||
int y = col + vectors[i].col();
|
||||
|
||||
if ((x >= 0 && x < SIZE && y >= 0 && y < SIZE)
|
||||
&& (playBoard[x][y] == null)) {
|
||||
scoreEnemy++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (64.0 / (2 * n)) * (2.5 * scoreSelf - 3.0 * scoreEnemy);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,83 @@
|
||||
package reversi.view;
|
||||
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.event.MouseAdapter;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import reversi.model.Field;
|
||||
import reversi.model.Player;
|
||||
|
||||
/**
|
||||
* Class for a single play field panel. Paints the circles with the color of
|
||||
* the Player.
|
||||
*/
|
||||
public class ReversiField extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = -5762403586769495603L;
|
||||
private int height;
|
||||
private int width;
|
||||
private int y;
|
||||
private int x;
|
||||
private final Field field;
|
||||
private Player player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param field <code>Field</code> with coordinates of the play field.
|
||||
*/
|
||||
public ReversiField(final Field field) {
|
||||
this.field = field;
|
||||
setPlayer(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public final void paintComponent(final Graphics g) {
|
||||
super.paintComponent(g);
|
||||
|
||||
height = (int) (super.getHeight() * 0.8);
|
||||
width = (int) (super.getWidth() * 0.8);
|
||||
y = (int) (width * 0.1 + 2);
|
||||
x = (int) (height * 0.1 + 2);
|
||||
|
||||
if (player != null && player.getType()) {
|
||||
g.setColor(Color.BLUE);
|
||||
g.fillOval(y, x, width, height);
|
||||
} else if (player != null && !player.getType()) {
|
||||
g.setColor(Color.RED);
|
||||
g.fillOval(y, x, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return <code>Field</code> with coordinates of the play field.
|
||||
*/
|
||||
public final Field getField() {
|
||||
return this.field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listener created in the controller.
|
||||
*
|
||||
* @param ma Mouseadapter
|
||||
*/
|
||||
public final void setListener(final MouseAdapter ma) {
|
||||
this.addMouseListener(ma);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a player on a field (draw the circle in right color).
|
||||
*
|
||||
* @param player Player that is set.
|
||||
*/
|
||||
public void setPlayer(Player player) {
|
||||
this.player = player;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,70 @@
|
||||
package reversi.view;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.event.MouseAdapter;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.border.MatteBorder;
|
||||
|
||||
import reversi.model.Board;
|
||||
import reversi.model.Field;
|
||||
import reversi.model.Reversi;
|
||||
|
||||
/**
|
||||
* Create a reversi playboard for the reversi game gui.
|
||||
*/
|
||||
public class ReversiPlayBoard extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = 2725183063551812386L;
|
||||
private ReversiField[][] playField = new ReversiField
|
||||
[Board.SIZE][Board.SIZE];
|
||||
|
||||
/**
|
||||
* Init array with all reversi fields. Set background and tooltip of
|
||||
* fields.
|
||||
*/
|
||||
public ReversiPlayBoard() {
|
||||
this.setLayout(new GridLayout(Board.SIZE, Board.SIZE));
|
||||
this.setBorder(new CompoundBorder(
|
||||
null, new EmptyBorder(0, 0, 10, 10)));
|
||||
|
||||
for (int row = 0; row < Board.SIZE; row++) {
|
||||
for (int col = 0; col < Board.SIZE; col++) {
|
||||
playField[row][col] = new ReversiField(new Field(row, col));
|
||||
playField[row][col].setBorder(
|
||||
new MatteBorder(1, 1, 0, 0, Color.BLACK));
|
||||
playField[row][col].setBackground(new Color(0, 170, 0));
|
||||
playField[row][col].setToolTipText("Click to make a move!");
|
||||
this.add(playField[row][col]);
|
||||
}
|
||||
}
|
||||
this.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update hole playboard players.
|
||||
*
|
||||
* @param board Board to update.
|
||||
*/
|
||||
public void updatePlayBoard(Reversi board) {
|
||||
for (int row = 0; row < Board.SIZE; row++) {
|
||||
for (int col = 0; col < Board.SIZE; col++) {
|
||||
this.playField[row][col].setPlayer(board.getSlot(row, col));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set mouselistener of <code>field</code>
|
||||
*
|
||||
* @param ma Mouseadapter
|
||||
* @param field Field
|
||||
*/
|
||||
public final void setFieldListener(
|
||||
final MouseAdapter ma, final Field field) {
|
||||
this.playField[field.row()][field.col()].setListener(ma);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,292 @@
|
||||
package reversi.view;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Container;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Font;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import reversi.model.Board;
|
||||
import reversi.model.Field;
|
||||
import reversi.model.Reversi;
|
||||
|
||||
/**
|
||||
* Create and update the gui.
|
||||
*/
|
||||
public class ReversiView extends JFrame {
|
||||
|
||||
private static final long serialVersionUID = 5382834862595792805L;
|
||||
private boolean movePossible;
|
||||
private ReversiPlayBoard playBoard;
|
||||
private JPanel horizTableLabel;
|
||||
private JPanel verticTableLabel;
|
||||
private JLabel humanTiles;
|
||||
private JLabel botTiles;
|
||||
private JComboBox<Integer> switchLevel =
|
||||
new JComboBox<Integer>(new Integer[] {1, 2, 3, 4, 5});
|
||||
private JButton newGame = new JButton("New");
|
||||
private JButton switchGame = new JButton("Switch");
|
||||
private JButton undoMove = new JButton("Undo");
|
||||
private JButton quitGame = new JButton("Quit");
|
||||
|
||||
/**
|
||||
* Create the gui for the reversi game.
|
||||
*/
|
||||
public ReversiView() {
|
||||
super("Reversi");
|
||||
initForms();
|
||||
movePossible = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create all buttons and the playfield.
|
||||
*/
|
||||
private void initForms() {
|
||||
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
this.setSize(410, 450);
|
||||
this.setLayout(new BorderLayout());
|
||||
|
||||
playBoard = new ReversiPlayBoard();
|
||||
verticTableLabel = verticTableLabel();
|
||||
horizTableLabel = horizTableLabel();
|
||||
humanTiles = tilesSet();
|
||||
humanTiles.setForeground(Color.BLUE);
|
||||
botTiles = tilesSet();
|
||||
botTiles.setForeground(Color.RED);
|
||||
|
||||
Container menuBar = new Container();
|
||||
menuBar.setLayout(new FlowLayout());
|
||||
menuBar.add(humanTiles);
|
||||
menuBar.add(switchLevel);
|
||||
menuBar.add(newGame);
|
||||
menuBar.add(switchGame);
|
||||
switchLevel.setSelectedIndex(2);
|
||||
menuBar.add(undoMove);
|
||||
menuBar.add(quitGame);
|
||||
menuBar.add(botTiles);
|
||||
|
||||
|
||||
this.getContentPane().add(playBoard, BorderLayout.CENTER);
|
||||
this.getContentPane().add(menuBar, BorderLayout.SOUTH);
|
||||
this.getContentPane().add(verticTableLabel, BorderLayout.WEST);
|
||||
this.getContentPane().add(horizTableLabel, BorderLayout.NORTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the vertical label for the playfield.
|
||||
*
|
||||
* @return JPanel The label.
|
||||
*/
|
||||
private JPanel verticTableLabel() {
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new GridLayout(Board.SIZE, 1));
|
||||
panel.setBorder(new CompoundBorder(
|
||||
null, new EmptyBorder(0, 5, 10, 5)));
|
||||
|
||||
for (int i = 1; i <= Board.SIZE; i++) {
|
||||
JLabel tmp = new JLabel(String.valueOf(i));
|
||||
panel.add(tmp);
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the horizontal label for the playfield.
|
||||
*
|
||||
* @return JPanel The label.
|
||||
*/
|
||||
private JPanel horizTableLabel() {
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new GridLayout(1, Board.SIZE));
|
||||
panel.setBorder(new CompoundBorder(
|
||||
null, new EmptyBorder(0, 20, 0, 12)));
|
||||
|
||||
for (int i = 1; i <= Board.SIZE; i++) {
|
||||
JLabel tmp = new JLabel(String.valueOf(i));
|
||||
tmp.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
panel.add(tmp);
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates both labels for the number of tiles that are set.
|
||||
*
|
||||
* @return JLabel The label.
|
||||
*/
|
||||
private JLabel tilesSet() {
|
||||
JLabel tiles = new JLabel();
|
||||
tiles.setBorder(new CompoundBorder(null, new EmptyBorder(0, 3, 0, 3)));
|
||||
tiles.setFont(new Font(Font.DIALOG, Font.BOLD, 20));
|
||||
return tiles;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Level
|
||||
*/
|
||||
public final int getLevel() {
|
||||
return (Integer) this.switchLevel.getSelectedItem();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return <code>true</code> if human is allowed to perform a move.<br />
|
||||
* <code>false</code> e.g. if a machine move is active.
|
||||
*/
|
||||
public boolean getMovePossible() {
|
||||
return movePossible;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param movePossible Set if human move is possible.
|
||||
*/
|
||||
public final void setMovePossible(final boolean movePossible) {
|
||||
this.movePossible = movePossible;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param bool Set the undo button vissibility.
|
||||
*/
|
||||
public final void setUndoButtonVisible(final boolean bool) {
|
||||
this.undoMove.setEnabled(bool);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the Tiles display in the gui.
|
||||
*
|
||||
* @param human Human amount of tiles.
|
||||
* @param bot Amount of machine tiles.
|
||||
*/
|
||||
private void setTilesSet(int human, int bot) {
|
||||
this.humanTiles.setText(String.valueOf(human));
|
||||
this.botTiles.setText(String.valueOf(bot));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the players on the playfield.
|
||||
*
|
||||
* @param model Board to update.
|
||||
*/
|
||||
public final void updatePlayField(final Reversi model) {
|
||||
javax.swing.SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public final void run() {
|
||||
playBoard.updatePlayBoard(model);
|
||||
setTilesSet(model.getNumberOfHumanTiles(),
|
||||
model.getNumberOfMachineTiles());
|
||||
|
||||
/* check movePossible just because of thread interference
|
||||
when game is over
|
||||
*/
|
||||
if (model.gameOver() && movePossible) {
|
||||
viewMessage(winnerMessage(model));
|
||||
movePossible = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a message dialog and play a system sound.
|
||||
*
|
||||
* @param msg Message to show.
|
||||
*/
|
||||
public final void viewMessage(final String msg) {
|
||||
Toolkit.getDefaultToolkit().beep();
|
||||
JOptionPane.showMessageDialog(this, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks which player has won.
|
||||
*
|
||||
* @return Message with the winner.
|
||||
*/
|
||||
private String winnerMessage(Reversi model) {
|
||||
if (model.getWinner() == null) {
|
||||
return "Tie game!";
|
||||
} else if (model.getWinner().getType()) {
|
||||
return "You have won!";
|
||||
} else {
|
||||
return "The Computer has won!";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass the field listener of the controller to ReversiPlayBoard class.
|
||||
*
|
||||
* @param ma Mouseadapter
|
||||
* @param field Field
|
||||
*/
|
||||
public final void setPlayFieldListener(
|
||||
final MouseAdapter ma, final Field field) {
|
||||
this.playBoard.setFieldListener(ma, field);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param l Actionlistener
|
||||
*/
|
||||
public final void setSwitchLevelListener(final ActionListener l) {
|
||||
this.switchLevel.addActionListener(l);
|
||||
this.switchLevel.setToolTipText("Choose a difficulty level");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param l Actionlistener
|
||||
*/
|
||||
public final void setNewGameListener(final ActionListener l) {
|
||||
this.newGame.addActionListener(l);
|
||||
this.newGame.setMnemonic(KeyEvent.VK_N);
|
||||
this.newGame.setToolTipText("Start new game");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param l Actionlistener
|
||||
*/
|
||||
public final void setSwitchGameListener(final ActionListener l) {
|
||||
this.switchGame.addActionListener(l);
|
||||
this.switchGame.setMnemonic(KeyEvent.VK_S);
|
||||
this.switchGame.setToolTipText(
|
||||
"Switch first player and start new game");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param l Actionlistener
|
||||
*/
|
||||
public final void setUndoMoveListener(final ActionListener l) {
|
||||
this.undoMove.addActionListener(l);
|
||||
this.undoMove.setMnemonic(KeyEvent.VK_U);
|
||||
this.undoMove.setToolTipText("Undo last move");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param l Actionlistener
|
||||
*/
|
||||
public final void setQuitGameListener(final ActionListener l) {
|
||||
this.quitGame.addActionListener(l);
|
||||
this.quitGame.setMnemonic(KeyEvent.VK_Q);
|
||||
this.quitGame.setToolTipText("Quit the Game");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user