diff --git a/app/src/main/java/chess/controller/CommandExecutor.java b/app/src/main/java/chess/controller/CommandExecutor.java index 785f968..f2a8974 100644 --- a/app/src/main/java/chess/controller/CommandExecutor.java +++ b/app/src/main/java/chess/controller/CommandExecutor.java @@ -59,7 +59,11 @@ public class CommandExecutor { } private void switchPlayerTurn() { - this.game.switchPlayerTurn(); + if(this.game.switchPlayerTurn()) { + this.dispatcher.onDraw(); + this.dispatcher.onGameEnd(); + return; + } this.dispatcher.onPlayerTurn(this.game.getPlayerTurn()); } diff --git a/app/src/main/java/chess/controller/event/GameAdaptator.java b/app/src/main/java/chess/controller/event/GameAdaptator.java index e1b7c43..5b20fe1 100644 --- a/app/src/main/java/chess/controller/event/GameAdaptator.java +++ b/app/src/main/java/chess/controller/event/GameAdaptator.java @@ -42,4 +42,7 @@ public abstract class GameAdaptator implements GameListener { @Override public void onMoveNotAllowed(Move move) {} + @Override + public void onDraw() {} + } diff --git a/app/src/main/java/chess/controller/event/GameDispatcher.java b/app/src/main/java/chess/controller/event/GameDispatcher.java index cec2dfb..d7ed206 100644 --- a/app/src/main/java/chess/controller/event/GameDispatcher.java +++ b/app/src/main/java/chess/controller/event/GameDispatcher.java @@ -10,7 +10,7 @@ import chess.model.Color; import chess.model.Coordinate; import chess.model.Move; -public class GameDispatcher implements GameListener{ +public class GameDispatcher implements GameListener { private final List listeners; private final ExecutorService executor; @@ -88,6 +88,11 @@ public class GameDispatcher implements GameListener{ asyncForEachCall((l) -> l.onMoveNotAllowed(move)); } + @Override + public void onDraw() { + asyncForEachCall((l) -> l.onDraw()); + } + public void stopService() { this.executor.shutdown(); } diff --git a/app/src/main/java/chess/controller/event/GameListener.java b/app/src/main/java/chess/controller/event/GameListener.java index 9a89938..3b2eea7 100644 --- a/app/src/main/java/chess/controller/event/GameListener.java +++ b/app/src/main/java/chess/controller/event/GameListener.java @@ -10,6 +10,11 @@ public interface GameListener { * Invoked when the display of the board should be updated */ void onBoardUpdate(); + + /** + * Invoked when a draw occurs (same position is repeated three times) + */ + void onDraw(); /** * Invoked when the game has ended (by a win or a draw) diff --git a/app/src/main/java/chess/model/ChessBoard.java b/app/src/main/java/chess/model/ChessBoard.java index 1490dc6..afb23b8 100644 --- a/app/src/main/java/chess/model/ChessBoard.java +++ b/app/src/main/java/chess/model/ChessBoard.java @@ -2,6 +2,7 @@ package chess.model; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import chess.model.visitor.KingIdentifier; import chess.model.visitor.PawnIdentifier; @@ -137,20 +138,6 @@ public class ChessBoard { } public boolean hasAllowedMoves(Color player) { - // for (int i = 0; i < Coordinate.VALUE_MAX; i++) { - // for (int j = 0; j < Coordinate.VALUE_MAX; j++) { - // Coordinate attackCoords = new Coordinate(i, j); - // Piece attackPiece = pieceAt(attackCoords); - // if (attackPiece == null) - // continue; - - // if (attackPiece.getColor() != player) - // continue; - - // if (!getAllowedMoves(attackCoords).isEmpty()) - // return true; - // } - // } return !getAllowedMoves(player).isEmpty(); } @@ -289,6 +276,19 @@ public class ChessBoard { return null; } + public int hashPlayerPieces(Color color) { + int result = 0; + for (int i = 0; i < Coordinate.VALUE_MAX; i++) { + for (int j = 0; j < Coordinate.VALUE_MAX; j++) { + Piece piece = pieceAt(new Coordinate(i, j)); + if (piece == null || piece.getColor() != color) + continue; + result = Objects.hash(result, new Coordinate(i, j), piece); + } + } + return result; + } + public Move getLastMove() { return this.lastMove; } diff --git a/app/src/main/java/chess/model/Coordinate.java b/app/src/main/java/chess/model/Coordinate.java index 5db365e..8389ebd 100644 --- a/app/src/main/java/chess/model/Coordinate.java +++ b/app/src/main/java/chess/model/Coordinate.java @@ -1,5 +1,7 @@ package chess.model; +import java.util.Objects; + public class Coordinate { private final int x; private final int y; @@ -42,4 +44,9 @@ public class Coordinate { public String toString() { return "(" + this.x + ", " + this.y + ")"; } + + @Override + public int hashCode() { + return Objects.hash(this.x, this.y); + } } diff --git a/app/src/main/java/chess/model/Game.java b/app/src/main/java/chess/model/Game.java index 92dfa39..6025b89 100644 --- a/app/src/main/java/chess/model/Game.java +++ b/app/src/main/java/chess/model/Game.java @@ -1,5 +1,7 @@ package chess.model; +import java.util.HashMap; +import java.util.Map; import java.util.Stack; import chess.controller.PlayerCommand; @@ -9,6 +11,7 @@ public class Game { private final ChessBoard board; private Color playerTurn; private final Stack movesHistory; + private final Map> traitsPos; public enum GameStatus { Check, CheckMate, OnGoing, Pat; @@ -17,6 +20,9 @@ public class Game { public Game(ChessBoard board) { this.board = board; this.movesHistory = new Stack<>(); + this.traitsPos = new HashMap<>(); + this.traitsPos.put(Color.Black, new HashMap<>()); + this.traitsPos.put(Color.White, new HashMap<>()); } public ChessBoard getBoard() { @@ -31,8 +37,26 @@ public class Game { this.playerTurn = Color.White; } - public void switchPlayerTurn() { + /** + * + * @param color the current player turn + * @return true if a draw should be declared + */ + private boolean saveTraitPiecesPos(Color color) { + int piecesHash = this.board.hashPlayerPieces(color); + Integer count = this.traitsPos.get(color).get(piecesHash); + this.traitsPos.get(color).put(piecesHash, count == null ? 1 : count + 1); + return count == null ? false : count == (3 - 1); + } + + /** + * + * @return true if a draw should occur + */ + public boolean switchPlayerTurn() { + boolean draw = saveTraitPiecesPos(this.playerTurn); playerTurn = Color.getEnemy(playerTurn); + return draw; } public GameStatus checkGameStatus() { diff --git a/app/src/main/java/chess/model/pieces/Bishop.java b/app/src/main/java/chess/model/pieces/Bishop.java index 301baea..4556963 100644 --- a/app/src/main/java/chess/model/pieces/Bishop.java +++ b/app/src/main/java/chess/model/pieces/Bishop.java @@ -15,4 +15,9 @@ public class Bishop extends Piece { return visitor.visitPiece(this); } + @Override + public int hashCode() { + return 0; + } + } diff --git a/app/src/main/java/chess/model/pieces/King.java b/app/src/main/java/chess/model/pieces/King.java index 8234b7b..36a592d 100644 --- a/app/src/main/java/chess/model/pieces/King.java +++ b/app/src/main/java/chess/model/pieces/King.java @@ -14,4 +14,9 @@ public class King extends Piece { public T accept(PieceVisitor visitor) { return visitor.visitPiece(this); } + + @Override + public int hashCode() { + return 1; + } } diff --git a/app/src/main/java/chess/model/pieces/Knight.java b/app/src/main/java/chess/model/pieces/Knight.java index 9f3f31f..7f26558 100644 --- a/app/src/main/java/chess/model/pieces/Knight.java +++ b/app/src/main/java/chess/model/pieces/Knight.java @@ -14,4 +14,9 @@ public class Knight extends Piece { public T accept(PieceVisitor visitor) { return visitor.visitPiece(this); } + + @Override + public int hashCode() { + return 2; + } } diff --git a/app/src/main/java/chess/model/pieces/Pawn.java b/app/src/main/java/chess/model/pieces/Pawn.java index 2573df4..efc2815 100644 --- a/app/src/main/java/chess/model/pieces/Pawn.java +++ b/app/src/main/java/chess/model/pieces/Pawn.java @@ -18,4 +18,9 @@ public class Pawn extends Piece { public int multiplier() { return getColor() == Color.White ? 1 : -1; } + + @Override + public int hashCode() { + return 3; + } } diff --git a/app/src/main/java/chess/model/pieces/Queen.java b/app/src/main/java/chess/model/pieces/Queen.java index 9549256..bd994bb 100644 --- a/app/src/main/java/chess/model/pieces/Queen.java +++ b/app/src/main/java/chess/model/pieces/Queen.java @@ -14,4 +14,9 @@ public class Queen extends Piece { public T accept(PieceVisitor visitor) { return visitor.visitPiece(this); } + + @Override + public int hashCode() { + return 4; + } } diff --git a/app/src/main/java/chess/model/pieces/Rook.java b/app/src/main/java/chess/model/pieces/Rook.java index d484bc3..5c7e14d 100644 --- a/app/src/main/java/chess/model/pieces/Rook.java +++ b/app/src/main/java/chess/model/pieces/Rook.java @@ -14,4 +14,9 @@ public class Rook extends Piece { public T accept(PieceVisitor visitor) { return visitor.visitPiece(this); } + + @Override + public int hashCode() { + return 5; + } } diff --git a/app/src/main/java/chess/view/consolerender/Console.java b/app/src/main/java/chess/view/consolerender/Console.java index 7fd3762..a1c2688 100644 --- a/app/src/main/java/chess/view/consolerender/Console.java +++ b/app/src/main/java/chess/view/consolerender/Console.java @@ -252,4 +252,9 @@ public class Console implements GameListener { public void onMoveNotAllowed(Move move) { System.out.println("Move not allowed."); } + + @Override + public void onDraw() { + System.out.println("Repeated positions!"); + } } diff --git a/app/src/main/java/chess/view/simplerender/Window.java b/app/src/main/java/chess/view/simplerender/Window.java index ea0fd6a..bc265c0 100644 --- a/app/src/main/java/chess/view/simplerender/Window.java +++ b/app/src/main/java/chess/view/simplerender/Window.java @@ -200,9 +200,7 @@ public class Window extends JFrame implements GameListener { @Override public void onWin(chess.model.Color color) { - SwingUtilities.invokeLater(() -> { - JOptionPane.showMessageDialog(this, "Victory of " + color); - }); + JOptionPane.showMessageDialog(this, "Victory of " + color); } @Override @@ -223,16 +221,12 @@ public class Window extends JFrame implements GameListener { @Override public void onPatSituation() { - SwingUtilities.invokeLater(() -> { - JOptionPane.showMessageDialog(this, "Pat. It's a draw!"); - }); + JOptionPane.showMessageDialog(this, "Pat. It's a draw!"); } @Override public void onSurrender(chess.model.Color color) { - SwingUtilities.invokeLater(() -> { - JOptionPane.showMessageDialog(this, color + " has surrendered."); - }); + JOptionPane.showMessageDialog(this, color + " has surrendered."); } @Override @@ -299,4 +293,9 @@ public class Window extends JFrame implements GameListener { drawInvalid(move); } + @Override + public void onDraw() { + JOptionPane.showMessageDialog(this, "Same position was repeated three times. It's a draw!"); + } + }