This repository has been archived on 2021-02-18. You can view files and clone it, but cannot push or open issues or pull requests.
chess/src/dev/kske/chess/board/Board.java

756 lines
24 KiB
Java

package dev.kske.chess.board;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import dev.kske.chess.board.Piece.Color;
import dev.kske.chess.board.Piece.Type;
/**
* Project: <strong>Chess</strong><br>
* File: <strong>Board.java</strong><br>
* Created: <strong>01.07.2019</strong><br>
* Author: <strong>Kai S. K. Engelbart</strong>
*/
public class Board {
private Piece[][] boardArr = new Piece[8][8];
private Map<Color, Position> kingPos = new HashMap<>();
private Map<Color, Map<Type, Boolean>> castlingRights = new HashMap<>();
private Log log = new Log();
private static final Map<Type, int[][]> positionScores;
static {
positionScores = new HashMap<>();
positionScores.put(Type.KING,
new int[][] { new int[] { -3, -4, -4, -5, -5, -4, -4, -3 },
new int[] { -3, -4, -4, -5, -4, -4, -4, -3 }, new int[] { -3, -4, -4, -5, -4, -4, -4, -3 },
new int[] { -3, -4, -4, -5, -4, -4, -4, -3 }, new int[] { -2, -3, -3, -2, -2, -2, -2, -1 },
new int[] { -1, -2, -2, -2, -2, -2, -2, -1 }, new int[] { 2, 2, 0, 0, 0, 0, 2, 2 },
new int[] { 2, 3, 1, 0, 0, 1, 3, 2 } });
positionScores.put(Type.QUEEN,
new int[][] { new int[] { -2, -1, -1, -1, -1, -1, -1, -2 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 },
new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 0, 1, 1, 1, 1, 0, -1 },
new int[] { 0, 0, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 1, 1, 1, 1, 1, 0, -1 },
new int[] { -1, 0, 1, 0, 0, 0, 0, -1 }, new int[] { -2, -1, -1, -1, -1, -1, -1, -2 } });
positionScores.put(Type.ROOK,
new int[][] { new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }, new int[] { 1, 1, 1, 1, 1, 1, 1, 1 },
new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 },
new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 },
new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { 0, 0, 0, 1, 1, 0, 0, 0 } });
positionScores.put(Type.KNIGHT,
new int[][] { new int[] { -5, -4, -3, -3, -3, -3, -4, -5 }, new int[] { -4, -2, 0, 0, 0, 0, -2, -4 },
new int[] { -3, 0, 1, 2, 2, 1, 0, -3 }, new int[] { -3, 1, 2, 2, 2, 2, 1, -3 },
new int[] { -3, 0, 2, 2, 2, 2, 0, -1 }, new int[] { -3, 1, 1, 2, 2, 1, 1, -3 },
new int[] { -4, -2, 0, 1, 1, 0, -2, -4 }, new int[] { -5, -4, -3, -3, -3, -3, -4, -5 } });
positionScores.put(Type.BISHOP,
new int[][] { new int[] { -2, -1, -1, -1, -1, -1, -1, 2 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 },
new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 1, 1, 1, 1, 1, 1, -1 },
new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 1, 1, 1, 1, 1, 1, -1 },
new int[] { -1, 1, 0, 0, 0, 0, 1, -1 }, new int[] { -2, -1, -1, -1, -1, -1, -1, -2 } });
positionScores.put(Type.PAWN,
new int[][] { new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }, new int[] { 5, 5, 5, 5, 5, 5, 5, 5 },
new int[] { 1, 1, 2, 3, 3, 2, 1, 1 }, new int[] { 0, 0, 1, 3, 3, 1, 0, 0 },
new int[] { 0, 0, 0, 2, 2, 0, 0, 0 }, new int[] { 0, 0, -1, 0, 0, -1, 0, 0 },
new int[] { 0, 1, 1, -2, -2, 1, 1, 0 }, new int[] { 0, 0, 0, 0, 0, 0, 0, 0 } });
}
/**
* Initializes the board with the default chess starting position.
*/
public Board() {
initDefaultPositions();
}
/**
* Initializes the board with data from a FEN-string.
*
* @param fen The FEN-string to initialize the board from
*/
public Board(String fen) {
initFromFEN(fen);
}
/**
* Creates a copy of another {@link Board} instance.<br>
* The created object is a deep copy, but does not contain any move history
* apart from the current {@link MoveNode}.
*
* @param other The {@link Board} instance to copy
*/
public Board(Board other) {
boardArr = new Piece[8][8];
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++) {
if (other.boardArr[i][j] == null) continue;
boardArr[i][j] = (Piece) other.boardArr[i][j].clone();
boardArr[i][j].board = this;
}
kingPos.putAll(other.kingPos);
Map<Type, Boolean> whiteCastling = new HashMap<>(other.castlingRights.get(Color.WHITE)),
blackCastling = new HashMap<>(other.castlingRights.get(Color.BLACK));
castlingRights.put(Color.WHITE, whiteCastling);
castlingRights.put(Color.BLACK, blackCastling);
log = new Log(other.log, false);
}
/**
* Moves a piece across the board if the move is legal.
*
* @param move The move to execute
* @return {@code true}, if the attempted move was legal and thus executed
*/
public boolean attemptMove(Move move) {
Piece piece = getPos(move);
if (piece == null || !piece.isValidMove(move)) return false;
else {
// Set type after validation
if (move.type == Move.Type.UNKNOWN) move.type = Move.Type.NORMAL;
// Move piece
move(move);
// Revert move if it caused a check for its team
if (checkCheck(piece.getColor())) {
revert();
return false;
}
return true;
}
}
/**
* Moves a piece across the board without checking if the move is legal.
*
* @param move The move to execute
*/
public void move(Move move) {
Piece piece = getPos(move);
Piece capturePiece = getDest(move);
switch (move.type) {
case PAWN_PROMOTION:
setPos(move, null);
// TODO: Select promotion
setDest(move, new Queen(piece.getColor(), this));
break;
case EN_PASSANT:
setDest(move, piece);
setPos(move, null);
boardArr[move.dest.x][move.dest.y - move.ySign] = null;
break;
case CASTLING:
// Move the king
setDest(move, piece);
setPos(move, null);
// Move the rook
Move rookMove = move.dest.x == 6 ? new Move(7, move.pos.y, 5, move.pos.y) // Kingside
: new Move(0, move.pos.y, 3, move.pos.y); // Queenside
setDest(rookMove, getPos(rookMove));
setPos(rookMove, null);
getDest(rookMove).incMoveCounter();
break;
case UNKNOWN:
System.err.printf("Move of unknown type %s found!%n", move);
case NORMAL:
setDest(move, piece);
setPos(move, null);
break;
default:
System.err.printf("Move %s of unimplemented type found!%n", move);
}
// Increment move counter
getDest(move).incMoveCounter();
// Update the king's position if the moved piece is the king and castling
// availability
if (piece.getType() == Type.KING) kingPos.put(piece.getColor(), move.dest);
// Update log
log.add(move, capturePiece, piece.getType() == Type.PAWN);
updateCastlingRights();
}
/**
* Moves a piece across the board without checking if the move is legal.
*
* @param sanMove The move to execute in SAN (Standard Algebraic Notation)
*/
public void move(String sanMove) {
Map<String, Pattern> patterns = new HashMap<>();
patterns.put("pieceMove",
Pattern.compile(
"^(?<pieceType>[NBRQK])(?:(?<fromFile>[a-h])|(?<fromRank>[1-8])|(?<fromSquare>[a-h][1-8]))?x?(?<toSquare>[a-h][1-8])(?:\\+{0,2}|\\#)$"));
patterns.put("pawnCapture",
Pattern
.compile(
"^(?<fromFile>[a-h])(?<fromRank>[1-8])?x(?<toSquare>[a-h][1-8])(?<promotedTo>[NBRQK])?(?:\\+{0,2}|\\#)?$"));
patterns.put("pawnPush", Pattern.compile("^(?<toSquare>[a-h][1-8])(?<promotedTo>[NBRQK])?(?:\\+{0,2}|\\#)$"));
patterns.put("castling", Pattern.compile("^(?<queenside>O-O-O)|(?<kingside>O-O)(?:\\+{0,2}|\\#)?$"));
patterns.forEach((patternName, pattern) -> {
Matcher m = pattern.matcher(sanMove);
if (m.find()) {
Position pos = null, dest = null;
Move.Type moveType = Move.Type.NORMAL;
switch (patternName) {
case "pieceMove":
dest = Position.fromLAN(m.group("toSquare"));
if (m.group("fromSquare") != null) pos = Position.fromLAN(m.group("fromSquare"));
else {
Type type = Type.fromFirstChar(m.group("pieceType").charAt(0));
char file;
int rank;
if (m.group("fromFile") != null) {
file = m.group("fromFile").charAt(0);
rank = get(type, file);
pos = Position.fromLAN(String.format("%c%d", file, rank));
} else if (m.group("fromRank") != null) {
rank = Integer.parseInt(m.group("fromRank").substring(0, 1));
file = get(type, rank);
pos = Position.fromLAN(String.format("%c%d", file, rank));
} else pos = get(type, dest);
}
break;
case "pawnCapture":
dest = Position.fromLAN(m.group("toSquare"));
char file = m.group("fromFile").charAt(0);
int rank = m.group("fromRank") == null ? get(Type.PAWN, file)
: Integer.parseInt(m.group("fromRank"));
pos = Position.fromLAN(String.format("%c%d", file, rank));
break;
case "pawnPush":
dest = Position.fromLAN(m.group("toSquare"));
// TODO: Pawn promotion
int step = log.getActiveColor() == Color.WHITE ? 1 : -1;
// One step forward
if (boardArr[dest.x][dest.y + step] != null) pos = new Position(dest.x, dest.y + step);
// Double step forward
else pos = new Position(dest.x, dest.y + 2 * step);
break;
case "castling":
pos = new Position(4, log.getActiveColor() == Color.WHITE ? 7 : 0);
dest = new Position(m.group("kingside") != null ? 6 : 2, pos.y);
moveType = Move.Type.CASTLING;
break;
}
move(new Move(pos, dest, moveType));
return;
}
});
}
/**
* Reverts the last move.
*/
public void revert() {
MoveNode moveNode = log.getLast();
Move move = moveNode.move;
Piece capturedPiece = moveNode.capturedPiece;
switch (move.type) {
case PAWN_PROMOTION:
setPos(move, new Pawn(getDest(move).getColor(), this));
setDest(move, capturedPiece);
break;
case EN_PASSANT:
setPos(move, getDest(move));
setDest(move, null);
boardArr[move.dest.x][move.dest.y - move.ySign] = new Pawn(getPos(move).getColor().opposite(), this);
break;
case CASTLING:
// Move the king
setPos(move, getDest(move));
setDest(move, null);
// Move the rook
Move rookMove = move.dest.x == 6 ? new Move(5, move.pos.y, 7, move.pos.y) // Kingside
: new Move(3, move.pos.y, 0, move.pos.y); // Queenside
setDest(rookMove, getPos(rookMove));
setPos(rookMove, null);
getDest(rookMove).decMoveCounter();
break;
case UNKNOWN:
System.err.printf("Move of unknown type %s found!%n", move);
case NORMAL:
setPos(move, getDest(move));
setDest(move, capturedPiece);
break;
default:
System.err.printf("Move %s of unimplemented type found!%n", move);
}
// Decrement move counter
getPos(move).decMoveCounter();
// Update the king's position if the moved piece is the king
if (getPos(move).getType() == Type.KING) kingPos.put(getPos(move).getColor(), move.pos);
// Update log
log.removeLast();
updateCastlingRights();
}
private void updateCastlingRights() {
// White
if (new Position(4, 7).equals(kingPos.get(Color.WHITE))) {
final King king = (King) get(kingPos.get(Color.WHITE));
castlingRights.get(Color.WHITE).put(Type.KING, king.canCastleKingside());
castlingRights.get(Color.WHITE).put(Type.QUEEN, king.canCastleQueenside());
} else {
castlingRights.get(Color.WHITE).put(Type.KING, false);
castlingRights.get(Color.WHITE).put(Type.QUEEN, false);
}
// Black
if (new Position(4, 0).equals(kingPos.get(Color.BLACK))) {
final King king = (King) get(kingPos.get(Color.BLACK));
castlingRights.get(Color.BLACK).put(Type.KING, king.canCastleKingside());
castlingRights.get(Color.BLACK).put(Type.QUEEN, king.canCastleQueenside());
} else {
castlingRights.get(Color.BLACK).put(Type.KING, false);
castlingRights.get(Color.BLACK).put(Type.QUEEN, false);
}
}
/**
* Generated every legal move for one color
*
* @param color The color to generate the moves for
* @return A list of all legal moves
*/
public List<Move> getMoves(Color color) {
List<Move> moves = new ArrayList<>();
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
if (boardArr[i][j] != null && boardArr[i][j].getColor() == color)
moves.addAll(boardArr[i][j].getMoves(new Position(i, j)));
return moves;
}
public List<Move> getMoves(Position pos) {
return get(pos).getMoves(pos);
}
/**
* Checks, if the king is in check.
*
* @param color The color of the king to check
* @return {@code true}, if the king is in check
*/
public boolean checkCheck(Color color) {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++) {
Position pos = new Position(i, j);
if (get(pos) != null && get(pos).getColor() != color
&& get(pos).isValidMove(new Move(pos, kingPos.get(color))))
return true;
}
return false;
}
/**
* Checks, if the king is in checkmate.
* This requires the king to already be in check!
*
* @param color The color of the king to check
* @return {@code true}, if the king is in checkmate
*/
public boolean checkCheckmate(Color color) {
// Return false immediately if the king can move
if (!getMoves(kingPos.get(color)).isEmpty()) return false;
else {
for (Move move : getMoves(color)) {
move(move);
boolean check = checkCheck(color);
revert();
if (!check) return false;
}
return true;
}
}
public GameState getGameEventType(Color color) {
return checkCheck(color) ? checkCheckmate(color) ? GameState.CHECKMATE : GameState.CHECK
: getMoves(color).isEmpty() || log.getLast().halfmoveClock >= 50 ? GameState.STALEMATE
: GameState.NORMAL;
}
/**
* Evaluated the board.
*
* @param color The color to evaluate for
* @return An positive number representing how good the position is
*/
public int evaluate(Color color) {
int score = 0;
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
if (boardArr[i][j] != null && boardArr[i][j].getColor() == color) {
switch (boardArr[i][j].getType()) {
case QUEEN:
score += 90;
break;
case ROOK:
score += 50;
break;
case KNIGHT:
score += 30;
break;
case BISHOP:
score += 30;
break;
case PAWN:
score += 10;
break;
}
if (positionScores.containsKey(boardArr[i][j].getType()))
score += positionScores.get(boardArr[i][j].getType())[i][color == Color.WHITE ? j : 7 - j];
}
return score;
}
/**
* Initialized the board array with the default chess pieces and positions.
*/
public void initDefaultPositions() {
// Initialize pawns
for (int i = 0; i < 8; i++) {
boardArr[i][1] = new Pawn(Color.BLACK, this);
boardArr[i][6] = new Pawn(Color.WHITE, this);
}
// Initialize kings
boardArr[4][0] = new King(Color.BLACK, this);
boardArr[4][7] = new King(Color.WHITE, this);
// Initialize king position objects
kingPos.put(Color.BLACK, new Position(4, 0));
kingPos.put(Color.WHITE, new Position(4, 7));
// Initialize queens
boardArr[3][0] = new Queen(Color.BLACK, this);
boardArr[3][7] = new Queen(Color.WHITE, this);
// Initialize rooks
boardArr[0][0] = new Rook(Color.BLACK, this);
boardArr[0][7] = new Rook(Color.WHITE, this);
boardArr[7][0] = new Rook(Color.BLACK, this);
boardArr[7][7] = new Rook(Color.WHITE, this);
// Initialize knights
boardArr[1][0] = new Knight(Color.BLACK, this);
boardArr[1][7] = new Knight(Color.WHITE, this);
boardArr[6][0] = new Knight(Color.BLACK, this);
boardArr[6][7] = new Knight(Color.WHITE, this);
// Initialize bishops
boardArr[2][0] = new Bishop(Color.BLACK, this);
boardArr[2][7] = new Bishop(Color.WHITE, this);
boardArr[5][0] = new Bishop(Color.BLACK, this);
boardArr[5][7] = new Bishop(Color.WHITE, this);
// Clear all other tiles
for (int i = 0; i < 8; i++)
for (int j = 2; j < 6; j++)
boardArr[i][j] = null;
// Initialize castling rights
Map<Type, Boolean> whiteCastling = new HashMap<>(), blackCastling = new HashMap<>();
whiteCastling.put(Type.KING, true);
whiteCastling.put(Type.QUEEN, true);
blackCastling.put(Type.KING, true);
blackCastling.put(Type.QUEEN, true);
castlingRights.put(Color.WHITE, whiteCastling);
castlingRights.put(Color.BLACK, blackCastling);
log.reset();
}
/**
* Initialized the board with a position specified in a FEN-encoded string.
*
* @param fen The FEN-encoded string representing target state of the board
*/
public void initFromFEN(String fen) {
String[] parts = fen.split(" ");
log.reset();
// Piece placement (from white's perspective)
String[] rows = parts[0].split("/");
for (int i = 0; i < 8; i++) {
char[] places = rows[i].toCharArray();
for (int j = 0, k = 0; k < places.length; j++, k++) {
if (Character.isDigit(places[k])) {
for (int l = j; l < Character.digit(places[k], 10); l++, j++)
boardArr[j][i] = null;
--j;
} else {
Color color = Character.isUpperCase(places[k]) ? Color.WHITE : Color.BLACK;
switch (Character.toLowerCase(places[k])) {
case 'k':
boardArr[j][i] = new King(color, this);
kingPos.put(color, new Position(j, i));
break;
case 'q':
boardArr[j][i] = new Queen(color, this);
break;
case 'r':
boardArr[j][i] = new Rook(color, this);
break;
case 'n':
boardArr[j][i] = new Knight(color, this);
break;
case 'b':
boardArr[j][i] = new Bishop(color, this);
break;
case 'p':
boardArr[j][i] = new Pawn(color, this);
break;
default:
System.err.printf("Unknown character '%c' in board declaration of FEN string '%s'%n",
places[k],
fen);
}
}
}
}
// Active color
log.setActiveColor(Color.fromFirstChar(parts[1].charAt(0)));
// Castling rights
Map<Type, Boolean> whiteCastling = new HashMap<>(), blackCastling = new HashMap<>();
for (char c : parts[2].toCharArray())
switch (c) {
case 'K':
whiteCastling.put(Type.KING, true);
case 'Q':
whiteCastling.put(Type.QUEEN, true);
case 'k':
blackCastling.put(Type.KING, true);
case 'q':
blackCastling.put(Type.QUEEN, true);
case '-':
break;
default:
System.err
.printf("Unknown character '%c' in castling rights declaration of FEN string '%s'", c, fen);
}
castlingRights.put(Color.WHITE, whiteCastling);
castlingRights.put(Color.BLACK, blackCastling);
// En passant availability
if (!parts[3].equals("-")) log.setEnPassant(Position.fromLAN(parts[3]));
// Halfmove clock
log.setHalfmoveClock(Integer.parseInt(parts[4]));
// Fullmove counter
log.setFullmoveNumber(Integer.parseInt(parts[5]));
}
/**
* @return a FEN-encoded string representing the board
*/
public String toFEN() {
StringBuilder sb = new StringBuilder();
// Piece placement (from white's perspective)
for (int i = 0; i < 8; i++) {
int emptyCount = 0;
for (int j = 0; j < 8; j++) {
final Piece piece = boardArr[j][i];
if (piece == null) ++emptyCount;
else {
if (emptyCount != 0) {
sb.append(emptyCount);
emptyCount = 0;
}
char p = boardArr[j][i].getType().firstChar();
sb.append(piece.getColor() == Color.WHITE ? Character.toUpperCase(p) : p);
}
}
if (emptyCount != 0) sb.append(emptyCount);
if (i < 7) sb.append('/');
}
// Active color
sb.append(" " + log.getActiveColor().firstChar());
// Castling Rights
sb.append(' ');
StringBuilder castlingSb = new StringBuilder();
if (castlingRights.get(Color.WHITE).get(Type.KING)) castlingSb.append('K');
if (castlingRights.get(Color.WHITE).get(Type.QUEEN)) castlingSb.append('Q');
if (castlingRights.get(Color.BLACK).get(Type.KING)) castlingSb.append('k');
if (castlingRights.get(Color.BLACK).get(Type.QUEEN)) castlingSb.append('q');
if (castlingSb.length() == 0) sb.append("-");
sb.append(castlingSb);
final MoveNode lastMove = log.getLast();
// En passant availability
sb.append(" " + (lastMove == null || lastMove.enPassant == null ? "-" : lastMove.enPassant.toLAN()));
// Halfmove clock
sb.append(" " + String.valueOf(lastMove == null ? 0 : lastMove.halfmoveClock));
// Fullmove counter
sb.append(" " + String.valueOf(lastMove == null ? 1 : lastMove.fullmoveCounter));
return sb.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.deepHashCode(boardArr);
result = prime * result + Objects.hash(castlingRights, kingPos, log);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
Board other = (Board) obj;
return Arrays.deepEquals(boardArr, other.boardArr) && Objects.equals(castlingRights, other.castlingRights)
&& Objects.equals(kingPos, other.kingPos) && Objects.equals(log, other.log);
}
/**
* @param pos The position from which to return a piece
* @return The piece at the position
*/
public Piece get(Position pos) {
return boardArr[pos.x][pos.y];
}
/**
* Searches for a {@link Piece} inside a file (A - H).
*
* @param type The {@link Type} of the piece to search for
* @param file The file in which to search for the piece
* @return The rank (1 - 8) of the first piece with the specified type and
* current color in the file, or {@code -1} if there isn't any
*/
public int get(Type type, char file) {
int x = file - 97;
for (int i = 0; i < 8; i++)
if (boardArr[x][i] != null && boardArr[x][i].getType() == type
&& boardArr[x][i].getColor() == log.getActiveColor())
return 8 - i;
return -1;
}
/**
* Searches for a {@link Piece} inside a rank (1 - 8).
*
* @param type The {@link Type} of the piece to search for
* @param rank The rank in which to search for the piece
* @return The file (A - H) of the first piece with the specified type and
* current color in the file, or {@code -} if there isn't any
*/
public char get(Type type, int rank) {
int y = rank - 1;
for (int i = 0; i < 8; i++)
if (boardArr[i][y] != null && boardArr[i][y].getType() == type
&& boardArr[i][y].getColor() == log.getActiveColor())
return (char) (i + 97);
return '-';
}
/**
* Searches for a {@link Piece} that can move to a {@link Position}.
*
* @param type The {@link Type} of the piece to search for
* @param dest The destination that the piece is required to reach
* @return The position of a piece that can move to the specified destination
*/
public Position get(Type type, Position dest) {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
if (boardArr[i][j] != null && boardArr[i][j].getType() == type
&& boardArr[i][j].getColor() == log.getActiveColor()) {
Position pos = new Position(i, j);
if (boardArr[i][j].isValidMove(new Move(pos, dest))) return pos;
}
return null;
}
/**
* Places a piece at a position.
*
* @param pos The position to place the piece at
* @param piece The piece to place
*/
public void set(Position pos, Piece piece) {
boardArr[pos.x][pos.y] = piece;
}
/**
* @param move The move from which position to return a piece
* @return The piece at the position of the move
*/
public Piece getPos(Move move) {
return get(move.pos);
}
/**
* @param move The move from which destination to return a piece
* @return The piece at the destination of the move
*/
public Piece getDest(Move move) {
return get(move.dest);
}
/**
* Places a piece at the position of a move.
*
* @param move The move at which position to place the piece
* @param piece The piece to place
*/
public void setPos(Move move, Piece piece) {
set(move.pos, piece);
}
/**
* Places a piece at the destination of a move.
*
* @param move The move at which destination to place the piece
* @param piece The piece to place
*/
public void setDest(Move move, Piece piece) {
set(move.dest, piece);
}
/**
* @return The board array
*/
public Piece[][] getBoardArr() { return boardArr; }
/**
* @return The move log
*/
public Log getLog() { return log; }
}