Moved castling right logging to Log
* Removed move counter from Piece * Added castling right array to MoveNode and Log * Removed castling right map from Board * Added castling right serialization and deserialization to FENString * Modified LogTest
This commit is contained in:
		| @@ -20,63 +20,55 @@ import dev.kske.chess.board.Piece.Type; | ||||
|  */ | ||||
| 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 Piece[][]				boardArr	= new Piece[8][8]; | ||||
| 	private Map<Color, Position>	kingPos		= 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[][] { 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[] { -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 } }); | ||||
| 						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 } }); | ||||
| 						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[][] { 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 } }); | ||||
| 						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 } }); | ||||
| 						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[][] { 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(); | ||||
| 	} | ||||
| 	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); | ||||
| 	} | ||||
| 	public Board(String fen) { initFromFEN(fen); } | ||||
|  | ||||
| 	/** | ||||
| 	 * Creates a copy of another {@link Board} instance.<br> | ||||
| @@ -95,10 +87,6 @@ public class Board { | ||||
| 			} | ||||
|  | ||||
| 		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); | ||||
| 	} | ||||
|  | ||||
| @@ -158,8 +146,6 @@ public class Board { | ||||
| 						: 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); | ||||
| @@ -171,17 +157,11 @@ public class Board { | ||||
| 				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 | ||||
| 		// Update the king's position if the moved piece is the king | ||||
| 		if (piece.getType() == Type.KING) kingPos.put(piece.getColor(), move.dest); | ||||
|  | ||||
| 		// Update log | ||||
| 		log.add(move, capturePiece, piece.getType() == Type.PAWN); | ||||
|  | ||||
| 		updateCastlingRights(); | ||||
| 		log.add(move, piece, capturePiece); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| @@ -195,9 +175,7 @@ public class Board { | ||||
| 				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}|\\#)?$")); | ||||
| 				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}|\\#)?$")); | ||||
|  | ||||
| @@ -228,8 +206,7 @@ public class Board { | ||||
| 					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")); | ||||
| 						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": | ||||
| @@ -282,8 +259,6 @@ public class Board { | ||||
| 						: 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); | ||||
| @@ -295,38 +270,11 @@ public class Board { | ||||
| 				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); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| @@ -339,14 +287,11 @@ public class Board { | ||||
| 		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))); | ||||
| 				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); | ||||
| 	} | ||||
| 	public List<Move> getMoves(Position pos) { return get(pos).getMoves(pos); } | ||||
|  | ||||
| 	/** | ||||
| 	 * Checks, if the king is in check. | ||||
| @@ -358,9 +303,7 @@ public class Board { | ||||
| 		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; | ||||
| 				if (get(pos) != null && get(pos).getColor() != color && get(pos).isValidMove(new Move(pos, kingPos.get(color)))) return true; | ||||
| 			} | ||||
| 		return false; | ||||
| 	} | ||||
| @@ -388,8 +331,7 @@ public class Board { | ||||
|  | ||||
| 	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; | ||||
| 				: getMoves(color).isEmpty() || log.getLast().halfmoveClock >= 50 ? GameState.STALEMATE : GameState.NORMAL; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| @@ -471,15 +413,6 @@ public class Board { | ||||
| 			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(); | ||||
| 	} | ||||
|  | ||||
| @@ -524,9 +457,7 @@ public class Board { | ||||
| 							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); | ||||
| 							System.err.printf("Unknown character '%c' in board declaration of FEN string '%s'%n", places[k], fen); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| @@ -550,11 +481,10 @@ public class Board { | ||||
| 				case '-': | ||||
| 					break; | ||||
| 				default: | ||||
| 					System.err | ||||
| 						.printf("Unknown character '%c' in castling rights declaration of FEN string '%s'", c, fen); | ||||
| 					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); | ||||
| 		// castlingRights.put(Color.WHITE, whiteCastling); | ||||
| 		// castlingRights.put(Color.BLACK, blackCastling); | ||||
|  | ||||
| 		// En passant availability | ||||
| 		if (!parts[3].equals("-")) log.setEnPassant(Position.fromLAN(parts[3])); | ||||
| @@ -597,10 +527,10 @@ public class Board { | ||||
| 		// 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 (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); | ||||
|  | ||||
| @@ -623,7 +553,7 @@ public class Board { | ||||
| 		final int	prime	= 31; | ||||
| 		int			result	= 1; | ||||
| 		result	= prime * result + Arrays.deepHashCode(boardArr); | ||||
| 		result	= prime * result + Objects.hash(castlingRights, kingPos, log); | ||||
| 		result	= prime * result + Objects.hash(kingPos, log); | ||||
| 		return result; | ||||
| 	} | ||||
|  | ||||
| @@ -633,17 +563,14 @@ public class Board { | ||||
| 		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); | ||||
| 		return Arrays.deepEquals(boardArr, other.boardArr) && 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]; | ||||
| 	} | ||||
| 	public Piece get(Position pos) { return boardArr[pos.x][pos.y]; } | ||||
|  | ||||
| 	/** | ||||
| 	 * Searches for a {@link Piece} inside a file (A - H). | ||||
| @@ -656,9 +583,7 @@ public class Board { | ||||
| 	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; | ||||
| 			if (boardArr[x][i] != null && boardArr[x][i].getType() == type && boardArr[x][i].getColor() == log.getActiveColor()) return 8 - i; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| @@ -673,8 +598,7 @@ public class Board { | ||||
| 	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()) | ||||
| 			if (boardArr[i][y] != null && boardArr[i][y].getType() == type && boardArr[i][y].getColor() == log.getActiveColor()) | ||||
| 				return (char) (i + 97); | ||||
| 		return '-'; | ||||
| 	} | ||||
| @@ -689,8 +613,7 @@ public class Board { | ||||
| 	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()) { | ||||
| 				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; | ||||
| 				} | ||||
| @@ -703,25 +626,19 @@ public class Board { | ||||
| 	 * @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; | ||||
| 	} | ||||
| 	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); | ||||
| 	} | ||||
| 	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); | ||||
| 	} | ||||
| 	public Piece getDest(Move move) { return get(move.dest); } | ||||
|  | ||||
| 	/** | ||||
| 	 * Places a piece at the position of a move. | ||||
| @@ -729,9 +646,7 @@ public class Board { | ||||
| 	 * @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); | ||||
| 	} | ||||
| 	public void setPos(Move move, Piece piece) { set(move.pos, piece); } | ||||
|  | ||||
| 	/** | ||||
| 	 * Places a piece at the destination of a move. | ||||
| @@ -739,9 +654,7 @@ public class Board { | ||||
| 	 * @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); | ||||
| 	} | ||||
| 	public void setDest(Move move, Piece piece) { set(move.dest, piece); } | ||||
|  | ||||
| 	/** | ||||
| 	 * @return The board array | ||||
|   | ||||
| @@ -109,7 +109,24 @@ public class FENString { | ||||
| 		// Active color | ||||
| 		board.getLog().setActiveColor(activeColor); | ||||
|  | ||||
| 		// TODO: Castling availability | ||||
| 		// Castling availability | ||||
| 		boolean castlingRights[] = new boolean[4]; | ||||
| 		for (char c : castlingAvailability.toCharArray()) | ||||
| 			switch (c) { | ||||
| 				case 'K': | ||||
| 					castlingRights[MoveNode.WHITE_KINGSIDE] = true; | ||||
| 					break; | ||||
| 				case 'Q': | ||||
| 					castlingRights[MoveNode.WHITE_QUEENSIDE] = true; | ||||
| 					break; | ||||
| 				case 'k': | ||||
| 					castlingRights[MoveNode.BLACK_KINGSIDE] = true; | ||||
| 					break; | ||||
| 				case 'q': | ||||
| 					castlingRights[MoveNode.BLACK_QUEENSIDE] = true; | ||||
| 					break; | ||||
| 			} | ||||
| 		board.getLog().setCastlingRights(castlingRights); | ||||
|  | ||||
| 		// En passant square | ||||
| 		board.getLog().setEnPassant(enPassantTargetSquare); | ||||
| @@ -166,7 +183,12 @@ public class FENString { | ||||
| 		// Active color | ||||
| 		activeColor = board.getLog().getActiveColor(); | ||||
|  | ||||
| 		// TODO: Castling availability | ||||
| 		// Castling availability | ||||
| 		castlingAvailability = ""; | ||||
| 		final char castlingRightsChars[] = new char[] { 'K', 'Q', 'k', 'q' }; | ||||
| 		for (int i = 0; i < 4; i++) | ||||
| 			if (board.getLog().getCastlingRights()[i]) castlingAvailability += castlingRightsChars[i]; | ||||
| 		if (castlingAvailability.isEmpty()) castlingAvailability = "-"; | ||||
|  | ||||
| 		// En passant availability | ||||
| 		enPassantTargetSquare = board.getLog().getEnPassant(); | ||||
|   | ||||
| @@ -11,9 +11,7 @@ import java.util.List; | ||||
|  */ | ||||
| public class King extends Piece { | ||||
|  | ||||
| 	public King(Color color, Board board) { | ||||
| 		super(color, board); | ||||
| 	} | ||||
| 	public King(Color color, Board board) { super(color, board); } | ||||
|  | ||||
| 	@Override | ||||
| 	public boolean isValidMove(Move move) { | ||||
| @@ -33,22 +31,20 @@ public class King extends Piece { | ||||
| 	} | ||||
|  | ||||
| 	public boolean canCastleKingside() { | ||||
| 		if (getMoveCounter() == 0) { | ||||
| 		if (board.getLog().getCastlingRights()[getColor() == Color.WHITE ? MoveNode.WHITE_KINGSIDE : MoveNode.BLACK_KINGSIDE]) { | ||||
| 			int			y		= getColor() == Color.WHITE ? 7 : 0; | ||||
| 			Position	rookPos	= new Position(7, y); | ||||
| 			Piece		rook	= board.get(rookPos); | ||||
| 			return rook != null && rook.getType() == Type.ROOK && rook.getMoveCounter() == 0 | ||||
| 					&& isFreePath(new Move(new Position(4, y), new Position(6, y))); | ||||
| 			return rook != null && rook.getType() == Type.ROOK && isFreePath(new Move(new Position(4, y), new Position(6, y))); | ||||
| 		} else return false; | ||||
| 	} | ||||
|  | ||||
| 	public boolean canCastleQueenside() { | ||||
| 		if (getMoveCounter() == 0) { | ||||
| 		if (board.getLog().getCastlingRights()[getColor() == Color.WHITE ? MoveNode.WHITE_QUEENSIDE : MoveNode.BLACK_QUEENSIDE]) { | ||||
| 			int			y		= getColor() == Color.WHITE ? 7 : 0; | ||||
| 			Position	rookPos	= new Position(0, y); | ||||
| 			Piece		rook	= board.get(rookPos); | ||||
| 			return rook != null && rook.getType() == Type.ROOK && rook.getMoveCounter() == 0 | ||||
| 					&& isFreePath(new Move(new Position(4, y), new Position(1, y))); | ||||
| 			return rook != null && rook.getType() == Type.ROOK && isFreePath(new Move(new Position(4, y), new Position(1, y))); | ||||
| 		} else return false; | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -1,9 +1,11 @@ | ||||
| package dev.kske.chess.board; | ||||
|  | ||||
| import java.util.Arrays; | ||||
| import java.util.Iterator; | ||||
| import java.util.Objects; | ||||
|  | ||||
| import dev.kske.chess.board.Piece.Color; | ||||
| import dev.kske.chess.board.Piece.Type; | ||||
|  | ||||
| /** | ||||
|  * Project: <strong>Chess</strong><br> | ||||
| @@ -15,13 +17,12 @@ public class Log implements Iterable<MoveNode> { | ||||
|  | ||||
| 	private MoveNode root, current; | ||||
|  | ||||
| 	private Position	enPassant; | ||||
| 	private Color		activeColor; | ||||
| 	private boolean[]	castlingRights; | ||||
| 	private Position	enPassant; | ||||
| 	private int			fullmoveNumber, halfmoveClock; | ||||
|  | ||||
| 	public Log() { | ||||
| 		reset(); | ||||
| 	} | ||||
| 	public Log() { reset(); } | ||||
|  | ||||
| 	/** | ||||
| 	 * Creates a (partially deep) copy of another {@link Log} instance which begins | ||||
| @@ -34,6 +35,7 @@ public class Log implements Iterable<MoveNode> { | ||||
| 	 */ | ||||
| 	public Log(Log other, boolean copyVariations) { | ||||
| 		enPassant		= other.enPassant; | ||||
| 		castlingRights	= other.castlingRights.clone(); | ||||
| 		activeColor		= other.activeColor; | ||||
| 		fullmoveNumber	= other.fullmoveNumber; | ||||
| 		halfmoveClock	= other.halfmoveClock; | ||||
| @@ -54,9 +56,7 @@ public class Log implements Iterable<MoveNode> { | ||||
| 			private boolean		hasNext	= true; | ||||
|  | ||||
| 			@Override | ||||
| 			public boolean hasNext() { | ||||
| 				return hasNext; | ||||
| 			} | ||||
| 			public boolean hasNext() { return hasNext; } | ||||
|  | ||||
| 			@Override | ||||
| 			public MoveNode next() { | ||||
| @@ -72,16 +72,20 @@ public class Log implements Iterable<MoveNode> { | ||||
| 	 * Adds a move to the move history and adjusts the log to the new position. | ||||
| 	 *  | ||||
| 	 * @param move          The move to log | ||||
| 	 * @param piece         The piece that performed the move | ||||
| 	 * @param capturedPiece The piece captured with the move | ||||
| 	 * @param pawnMove      {@code true} if the move was made by a pawn | ||||
| 	 */ | ||||
| 	public void add(Move move, Piece capturedPiece, boolean pawnMove) { | ||||
| 		enPassant = pawnMove && move.yDist == 2 ? new Position(move.pos.x, move.pos.y + move.ySign) : null; | ||||
| 	public void add(Move move, Piece piece, Piece capturedPiece) { | ||||
| 		enPassant = piece.getType() == Type.PAWN && move.yDist == 2 ? new Position(move.pos.x, move.pos.y + move.ySign) : null; | ||||
| 		if (activeColor == Color.BLACK) ++fullmoveNumber; | ||||
| 		if (pawnMove || capturedPiece != null) halfmoveClock = 0; | ||||
| 		if (piece.getType() == Type.PAWN || capturedPiece != null) halfmoveClock = 0; | ||||
| 		else++halfmoveClock; | ||||
| 		activeColor = activeColor.opposite(); | ||||
| 		final MoveNode leaf = new MoveNode(move, capturedPiece, enPassant, activeColor, fullmoveNumber, halfmoveClock); | ||||
|  | ||||
| 		// Disable castling rights if a king or a rook has been moved | ||||
| 		if (piece.getType() == Type.KING || piece.getType() == Type.ROOK) disableCastlingRights(piece, move.pos); | ||||
|  | ||||
| 		final MoveNode leaf = new MoveNode(move, capturedPiece, castlingRights.clone(), enPassant, activeColor, fullmoveNumber, halfmoveClock); | ||||
|  | ||||
| 		if (isEmpty()) { | ||||
| 			root	= leaf; | ||||
| @@ -106,9 +110,7 @@ public class Log implements Iterable<MoveNode> { | ||||
|  | ||||
| 	public boolean isEmpty() { return root == null; } | ||||
|  | ||||
| 	public boolean hasParent() { | ||||
| 		return !isEmpty() && current.hasParent(); | ||||
| 	} | ||||
| 	public boolean hasParent() { return !isEmpty() && current.hasParent(); } | ||||
|  | ||||
| 	/** | ||||
| 	 * Reverts the log to its initial state corresponding to the default board | ||||
| @@ -117,6 +119,7 @@ public class Log implements Iterable<MoveNode> { | ||||
| 	public void reset() { | ||||
| 		root			= null; | ||||
| 		current			= null; | ||||
| 		castlingRights	= new boolean[] { true, true, true, true }; | ||||
| 		enPassant		= null; | ||||
| 		activeColor		= Color.WHITE; | ||||
| 		fullmoveNumber	= 1; | ||||
| @@ -124,8 +127,9 @@ public class Log implements Iterable<MoveNode> { | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Changes the current node to one of its children (variations). | ||||
| 	 *  | ||||
| 	 * @param index | ||||
| 	 * @param index the index of the variation to select | ||||
| 	 */ | ||||
| 	public void selectNextNode(int index) { | ||||
| 		if (!isEmpty() && current.hasVariations() && index < current.getVariations().size()) { | ||||
| @@ -156,14 +160,29 @@ public class Log implements Iterable<MoveNode> { | ||||
|  | ||||
| 	private void update() { | ||||
| 		activeColor		= current.activeColor; | ||||
| 		castlingRights	= current.castlingRights.clone(); | ||||
| 		enPassant		= current.enPassant; | ||||
| 		fullmoveNumber	= current.fullmoveCounter; | ||||
| 		halfmoveClock	= current.halfmoveClock; | ||||
| 	} | ||||
|  | ||||
| 	private void disableCastlingRights(Piece piece, Position initialPosition) { | ||||
| 		// Kingside | ||||
| 		if (piece.getType() == Type.KING || piece.getType() == Type.ROOK && initialPosition.x == 7) | ||||
| 			castlingRights[piece.getColor() == Color.WHITE ? MoveNode.WHITE_KINGSIDE : MoveNode.BLACK_KINGSIDE] = false; | ||||
|  | ||||
| 		// Queenside | ||||
| 		if (piece.getType() == Type.KING || piece.getType() == Type.ROOK && initialPosition.x == 0) | ||||
| 			castlingRights[piece.getColor() == Color.WHITE ? MoveNode.WHITE_QUEENSIDE : MoveNode.BLACK_QUEENSIDE] = false; | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public int hashCode() { | ||||
| 		return Objects.hash(activeColor, current, enPassant, fullmoveNumber, halfmoveClock, root); | ||||
| 		final int	prime	= 31; | ||||
| 		int			result	= 1; | ||||
| 		result	= prime * result + Arrays.hashCode(castlingRights); | ||||
| 		result	= prime * result + Objects.hash(activeColor, current, enPassant, fullmoveNumber, halfmoveClock); | ||||
| 		return result; | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| @@ -172,9 +191,8 @@ public class Log implements Iterable<MoveNode> { | ||||
| 		if (obj == null) return false; | ||||
| 		if (getClass() != obj.getClass()) return false; | ||||
| 		Log other = (Log) obj; | ||||
| 		return activeColor == other.activeColor && Objects.equals(current, other.current) | ||||
| 				&& Objects.equals(enPassant, other.enPassant) && fullmoveNumber == other.fullmoveNumber | ||||
| 				&& halfmoveClock == other.halfmoveClock && Objects.equals(root, other.root); | ||||
| 		return activeColor == other.activeColor && Arrays.equals(castlingRights, other.castlingRights) && Objects.equals(current, other.current) | ||||
| 				&& Objects.equals(enPassant, other.enPassant) && fullmoveNumber == other.fullmoveNumber && halfmoveClock == other.halfmoveClock; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| @@ -187,6 +205,10 @@ public class Log implements Iterable<MoveNode> { | ||||
| 	 */ | ||||
| 	public MoveNode getLast() { return current; } | ||||
|  | ||||
| 	public boolean[] getCastlingRights() { return castlingRights; } | ||||
|  | ||||
| 	public void setCastlingRights(boolean[] castlingRights) { this.castlingRights = castlingRights; } | ||||
|  | ||||
| 	public Position getEnPassant() { return enPassant; } | ||||
|  | ||||
| 	public void setEnPassant(Position enPassant) { this.enPassant = enPassant; } | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package dev.kske.chess.board; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.List; | ||||
| import java.util.Objects; | ||||
|  | ||||
| @@ -14,8 +15,11 @@ import dev.kske.chess.board.Piece.Color; | ||||
|  */ | ||||
| public class MoveNode { | ||||
|  | ||||
| 	public static final int WHITE_KINGSIDE = 0, WHITE_QUEENSIDE = 1, BLACK_KINGSIDE = 2, BLACK_QUEENSIDE = 3; | ||||
|  | ||||
| 	public final Move		move; | ||||
| 	public final Piece		capturedPiece; | ||||
| 	public final boolean[]	castlingRights; | ||||
| 	public final Position	enPassant; | ||||
| 	public final Color		activeColor; | ||||
| 	public final int		fullmoveCounter, halfmoveClock; | ||||
| @@ -34,10 +38,11 @@ public class MoveNode { | ||||
| 	 * @param fullmoveCounter | ||||
| 	 * @param halfmoveClock | ||||
| 	 */ | ||||
| 	public MoveNode(Move move, Piece capturedPiece, Position enPassant, Color activeColor, int fullmoveCounter, | ||||
| 			int halfmoveClock) { | ||||
| 	public MoveNode(Move move, Piece capturedPiece, boolean castlingRights[], Position enPassant, Color activeColor, | ||||
| 			int fullmoveCounter, int halfmoveClock) { | ||||
| 		this.move				= move; | ||||
| 		this.capturedPiece		= capturedPiece; | ||||
| 		this.castlingRights		= castlingRights; | ||||
| 		this.enPassant			= enPassant; | ||||
| 		this.activeColor		= activeColor; | ||||
| 		this.fullmoveCounter	= fullmoveCounter; | ||||
| @@ -53,8 +58,8 @@ public class MoveNode { | ||||
| 	 *                       considers subsequent variations | ||||
| 	 */ | ||||
| 	public MoveNode(MoveNode other, boolean copyVariations) { | ||||
| 		this(other.move, other.capturedPiece, other.enPassant, other.activeColor, other.fullmoveCounter, | ||||
| 				other.halfmoveClock); | ||||
| 		this(other.move, other.capturedPiece, other.castlingRights.clone(), other.enPassant, other.activeColor, | ||||
| 				other.fullmoveCounter, other.halfmoveClock); | ||||
| 		if (copyVariations && other.variations != null) { | ||||
| 			if (variations == null) variations = new ArrayList<>(); | ||||
| 			other.variations.forEach(variation -> { | ||||
| @@ -97,8 +102,12 @@ public class MoveNode { | ||||
|  | ||||
| 	@Override | ||||
| 	public int hashCode() { | ||||
| 		return Objects | ||||
| 			.hash(activeColor, capturedPiece, enPassant, fullmoveCounter, halfmoveClock, move, parent, variations); | ||||
| 		final int	prime	= 31; | ||||
| 		int			result	= 1; | ||||
| 		result	= prime * result + Arrays.hashCode(castlingRights); | ||||
| 		result	= prime * result | ||||
| 				+ Objects.hash(activeColor, capturedPiece, enPassant, fullmoveCounter, halfmoveClock, move); | ||||
| 		return result; | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| @@ -108,8 +117,8 @@ public class MoveNode { | ||||
| 		if (getClass() != obj.getClass()) return false; | ||||
| 		MoveNode other = (MoveNode) obj; | ||||
| 		return activeColor == other.activeColor && Objects.equals(capturedPiece, other.capturedPiece) | ||||
| 				&& Objects.equals(enPassant, other.enPassant) && fullmoveCounter == other.fullmoveCounter | ||||
| 				&& halfmoveClock == other.halfmoveClock && Objects.equals(move, other.move) | ||||
| 				&& Objects.equals(parent, other.parent) && Objects.equals(variations, other.variations); | ||||
| 				&& Arrays.equals(castlingRights, other.castlingRights) && Objects.equals(enPassant, other.enPassant) | ||||
| 				&& fullmoveCounter == other.fullmoveCounter && halfmoveClock == other.halfmoveClock | ||||
| 				&& Objects.equals(move, other.move); | ||||
| 	} | ||||
| } | ||||
| @@ -14,7 +14,6 @@ public abstract class Piece implements Cloneable { | ||||
|  | ||||
| 	private final Color	color; | ||||
| 	protected Board		board; | ||||
| 	private int			moveCounter; | ||||
|  | ||||
| 	public Piece(Color color, Board board) { | ||||
| 		this.color	= color; | ||||
| @@ -75,19 +74,9 @@ public abstract class Piece implements Cloneable { | ||||
|  | ||||
| 	public Color getColor() { return color; } | ||||
|  | ||||
| 	public int getMoveCounter() { return moveCounter; } | ||||
|  | ||||
| 	public void incMoveCounter() { | ||||
| 		++moveCounter; | ||||
| 	} | ||||
|  | ||||
| 	public void decMoveCounter() { | ||||
| 		--moveCounter; | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public int hashCode() { | ||||
| 		return Objects.hash(color, moveCounter); | ||||
| 		return Objects.hash(color); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| @@ -96,7 +85,7 @@ public abstract class Piece implements Cloneable { | ||||
| 		if (obj == null) return false; | ||||
| 		if (getClass() != obj.getClass()) return false; | ||||
| 		Piece other = (Piece) obj; | ||||
| 		return color == other.color && moveCounter == other.moveCounter; | ||||
| 		return color == other.color; | ||||
| 	} | ||||
|  | ||||
| 	public static enum Type { | ||||
|   | ||||
| @@ -43,10 +43,10 @@ class LogTest { | ||||
| 		log.setActiveColor(Color.WHITE); | ||||
| 		other.setActiveColor(Color.BLACK); | ||||
| 		assertNotEquals(log.getActiveColor(), other.getActiveColor()); | ||||
| 		log.add(Move.fromLAN("a2a4"), null, true); | ||||
| 		log.add(Move.fromLAN("a4a5"), null, true); | ||||
| 		other.add(Move.fromLAN("a2a4"), null, true); | ||||
| 		other.add(Move.fromLAN("a4a5"), null, true); | ||||
| 		log.add(Move.fromLAN("a2a4"), new Pawn(Color.WHITE, null), null); | ||||
| 		log.add(Move.fromLAN("a4a5"), new Pawn(Color.WHITE, null), null); | ||||
| 		other.add(Move.fromLAN("a2a4"), new Pawn(Color.WHITE, null), null); | ||||
| 		other.add(Move.fromLAN("a4a5"), new Pawn(Color.WHITE, null), null); | ||||
| 		assertNotEquals(log.getRoot(), other.getRoot()); | ||||
| 		assertNotEquals(log.getRoot().getVariations(), other.getRoot().getVariations()); | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user