Files
3DChess/app/src/main/java/chess/model/ChessBoard.java
Persson-dev 3b38e0da1f Super IA (#5)
Reviewed-on: #5
Co-authored-by: Persson-dev <sim16.prib@gmail.com>
Co-committed-by: Persson-dev <sim16.prib@gmail.com>
2025-04-30 18:28:01 +00:00

332 lines
8.2 KiB
Java

package chess.model;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import chess.model.pieces.King;
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;
private List<Move> cachedAllowedMoves = null;
private final Coordinate kingPos[];
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;
this.kingPos = new Coordinate[Color.values().length];
}
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);
if (piece instanceof King)
this.kingPos[piece.getColor().ordinal()] = coordinate;
}
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;
return kingPos[color.ordinal()];
}
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 || attackPiece.getColor() == color)
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<Move> getAllowedMoves(Color player) {
if (this.cachedAllowedMoves != null) {
return this.cachedAllowedMoves;
}
List<Move> 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);
Piece destPiece = pieceAt(destination);
if (destPiece != null && destPiece.getColor() == player)
continue;
PiecePathChecker piecePathChecker = new PiecePathChecker(this,
move);
if (!piecePathChecker.isValid())
continue;
applyMove(move);
if (!isKingInCheck(player))
result.add(move);
undoLastMove();
}
}
}
}
this.cachedAllowedMoves = result;
return result;
}
public List<Coordinate> getAllowedMoves(Coordinate pieceCoords) {
Piece piece = pieceAt(pieceCoords);
if (piece == null)
return null;
Color player = piece.getColor();
List<Coordinate> 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;
}
@Override
public int hashCode() {
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)
continue;
result = Objects.hash(result, piece.getColor(), new Coordinate(i, j), piece);
}
}
return result;
}
public Move getLastMove() {
return this.lastMove;
}
public void setLastMove(Move lastMove) {
this.lastMove = lastMove;
this.cachedAllowedMoves = null;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ChessBoard board)
return board.hashCode() == this.hashCode();
return false;
}
}