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; import chess.model.visitor.PiecePathChecker; public class ChessBoard { public static class Cell { private Piece piece; public Cell() { this.piece = null; } public Piece getPiece() { return piece; } public void setPiece(Piece piece) { this.piece = piece; } } private final Cell[][] cells; private Move lastVirtualMove; private Move lastMove; private Piece lastEjectedPiece; public ChessBoard() { this.cells = new Cell[Coordinate.VALUE_MAX][Coordinate.VALUE_MAX]; for (int i = 0; i < Coordinate.VALUE_MAX; i++) { for (int j = 0; j < Coordinate.VALUE_MAX; j++) { this.cells[i][j] = new Cell(); } } this.lastVirtualMove = null; this.lastMove = null; this.lastEjectedPiece = null; } public void applyMove(Move move) { assert move.isValid() : "Invalid move !"; Piece deadPiece = pieceAt(move.getDeadPieceCoords()); if (deadPiece != null) { this.lastEjectedPiece = deadPiece; } else { this.lastEjectedPiece = null; } Piece movingPiece = pieceAt(move.getStart()); pieceLeaves(move.getDeadPieceCoords()); pieceLeaves(move.getStart()); pieceComes(movingPiece, move.getFinish()); movingPiece.move(); this.lastVirtualMove = move; } public void undoLastMove() { assert this.lastVirtualMove != null : "Can't undo at the beginning!"; undoMove(this.lastVirtualMove, this.lastEjectedPiece); } public void undoMove(Move move, Piece deadPiece) { Piece movingPiece = pieceAt(move.getFinish()); pieceComes(movingPiece, move.getStart()); pieceLeaves(move.getFinish()); pieceComes(deadPiece, move.getDeadPieceCoords()); assert movingPiece != null; movingPiece.unMove(); } public boolean isCellEmpty(Coordinate coordinate) { return pieceAt(coordinate) == null; } public Piece pieceAt(Coordinate coordinate) { if (!coordinate.isValid()) return null; return cellAt(coordinate).getPiece(); } public void clearBoard() { for (int i = 0; i < Coordinate.VALUE_MAX; i++) { for (int j = 0; j < Coordinate.VALUE_MAX; j++) { pieceLeaves(new Coordinate(i, j)); } } } private Cell cellAt(Coordinate coordinate) { return this.cells[coordinate.getX()][coordinate.getY()]; } public void pieceComes(Piece piece, Coordinate coordinate) { cellAt(coordinate).setPiece(piece); } public void pieceLeaves(Coordinate coordinate) { cellAt(coordinate).setPiece(null); } public Coordinate findKing(Color color) { KingIdentifier kingIdentifier = new KingIdentifier(color); for (int i = 0; i < Coordinate.VALUE_MAX; i++) { for (int j = 0; j < Coordinate.VALUE_MAX; j++) { Coordinate coordinate = new Coordinate(i, j); Piece piece = pieceAt(coordinate); if (kingIdentifier.isKing(piece)) { return coordinate; } } } assert false : "No king found ?!"; return null; } public boolean isKingInCheck(Color color) { Coordinate kingPos = findKing(color); assert kingPos.isValid() : "King position is invalid!"; 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; PiecePathChecker checker = new PiecePathChecker(this, new Move(attackCoords, kingPos)); if (checker.isValid()) return true; } } return false; } public boolean hasAllowedMoves(Color player) { return !getAllowedMoves(player).isEmpty(); } public List getAllowedMoves(Color player) { List result = new ArrayList<>(); for (int x = 0; x < Coordinate.VALUE_MAX; x++) { for (int y = 0; y < Coordinate.VALUE_MAX; y++) { Coordinate start = new Coordinate(x, y); Piece piece = pieceAt(start); if (piece == null || piece.getColor() != player) continue; for (int i = 0; i < Coordinate.VALUE_MAX; i++) { for (int j = 0; j < Coordinate.VALUE_MAX; j++) { Coordinate destination = new Coordinate(i, j); Move move = new Move(start, destination); PiecePathChecker piecePathChecker = new PiecePathChecker(this, move); if (!piecePathChecker.isValid()) continue; applyMove(move); if (!isKingInCheck(player)) result.add(move); undoLastMove(); } } } } return result; } public List getAllowedMoves(Coordinate pieceCoords) { Piece piece = pieceAt(pieceCoords); if (piece == null) return null; Color player = piece.getColor(); List result = new ArrayList<>(); for (int i = 0; i < Coordinate.VALUE_MAX; i++) { for (int j = 0; j < Coordinate.VALUE_MAX; j++) { Coordinate destination = new Coordinate(i, j); Move move = new Move(pieceCoords, destination); PiecePathChecker piecePathChecker = new PiecePathChecker(this, move); if (!piecePathChecker.isValid()) continue; applyMove(move); if (!isKingInCheck(player)) result.add(destination); undoLastMove(); } } return result; } private boolean canCastle(Color color, int rookX, Direction kingDirection) { if (isKingInCheck(color)) return false; int colorLine = color == Color.White ? 7 : 0; Coordinate kingCoords = new Coordinate(4, colorLine); Coordinate rookCoords = new Coordinate(rookX, colorLine); Piece king = pieceAt(kingCoords); Piece rook = pieceAt(rookCoords); if (king == null || rook == null || king.hasMoved() || rook.hasMoved()) return false; for (int step = 1; step <= 2; step++) { Coordinate dest = Coordinate.fromIndex(kingCoords.toIndex() + step * kingDirection.getIndexOffset()); Piece obstacle = pieceAt(dest); if (obstacle != null) return false; applyMove(new Move(kingCoords, dest)); if (isKingInCheck(color)) { undoLastMove(); return false; } undoLastMove(); } Coordinate rookObstacleCoords = Coordinate.fromIndex(rookCoords.toIndex() - kingDirection.getIndexOffset()); Piece obstacle = pieceAt(rookObstacleCoords); return obstacle == null; } public boolean canSmallCastle(Color color) { return canCastle(color, 7, Direction.Right); } public boolean canBigCastle(Color color) { return canCastle(color, 0, Direction.Left); } public boolean pawnShouldBePromoted() { return pawnPromotePosition() != null; } /** * * @return Null if there is no pawn to promote */ public Coordinate pawnPromotePosition() { Coordinate piecePos = pawnPromotePosition(Color.White); if (piecePos != null) return piecePos; return pawnPromotePosition(Color.Black); } /** * * @return Null if there is no pawn to promote */ private Coordinate pawnPromotePosition(Color color) { int enemyLineY = color == Color.White ? 0 : 7; PawnIdentifier identifier = new PawnIdentifier(color); for (int x = 0; x < Coordinate.VALUE_MAX; x++) { Coordinate pieceCoords = new Coordinate(x, enemyLineY); Piece piece = pieceAt(pieceCoords); if (identifier.isPawn(piece)) return pieceCoords; } 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; } public void setLastMove(Move lastMove) { this.lastMove = lastMove; } @Override public boolean equals(Object otherBoard){ if (!(otherBoard instanceof ChessBoard)) return false; return ((hashPlayerPieces(Color.White) == ((ChessBoard)otherBoard).hashPlayerPieces(Color.White)) && (hashPlayerPieces(Color.Black) == ((ChessBoard)otherBoard).hashPlayerPieces(Color.Black))); } }