Compare commits
	
		
			14 Commits
		
	
	
		
			v0.1-alpha
			...
			v0.2-alpha
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| d8f5f3bbf4 | |||
| 4dcc9f7ca0 | |||
| cfd71af142 | |||
| fcd8bfb26b | |||
| cde7f63996 | |||
| 8ea0c7a603 | |||
| 8eda941284 | |||
| 7a986ab9c4 | |||
| c245cdb640 | |||
| 58340ca6ac | |||
| 199d2f06c6 | |||
| d12b06a1ff | |||
| 6d98d9a963 | |||
| c3a787c3a7 | 
@@ -1,12 +1,17 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
<classpath>
 | 
					<classpath>
 | 
				
			||||||
	<classpathentry kind="src" path="src"/>
 | 
						<classpathentry kind="src" path="src"/>
 | 
				
			||||||
 | 
						<classpathentry kind="src" path="res"/>
 | 
				
			||||||
 | 
						<classpathentry kind="src" output="bin_test" path="test">
 | 
				
			||||||
 | 
							<attributes>
 | 
				
			||||||
 | 
								<attribute name="test" value="true"/>
 | 
				
			||||||
 | 
							</attributes>
 | 
				
			||||||
 | 
						</classpathentry>
 | 
				
			||||||
	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
 | 
						<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
 | 
				
			||||||
		<attributes>
 | 
							<attributes>
 | 
				
			||||||
			<attribute name="module" value="true"/>
 | 
								<attribute name="module" value="true"/>
 | 
				
			||||||
		</attributes>
 | 
							</attributes>
 | 
				
			||||||
	</classpathentry>
 | 
						</classpathentry>
 | 
				
			||||||
	<classpathentry kind="lib" path="res"/>
 | 
					 | 
				
			||||||
	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
 | 
						<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
 | 
				
			||||||
	<classpathentry kind="output" path="bin"/>
 | 
						<classpathentry kind="output" path="bin"/>
 | 
				
			||||||
</classpath>
 | 
					</classpath>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1,6 @@
 | 
				
			|||||||
.metadata
 | 
					.metadata
 | 
				
			||||||
bin/
 | 
					bin/
 | 
				
			||||||
 | 
					/bin_test/
 | 
				
			||||||
tmp/
 | 
					tmp/
 | 
				
			||||||
*.tmp
 | 
					*.tmp
 | 
				
			||||||
*.bak
 | 
					*.bak
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ import java.util.HashMap;
 | 
				
			|||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import dev.kske.chess.board.Log.LoggedMove;
 | 
				
			||||||
import dev.kske.chess.board.Piece.Color;
 | 
					import dev.kske.chess.board.Piece.Color;
 | 
				
			||||||
import dev.kske.chess.board.Piece.Type;
 | 
					import dev.kske.chess.board.Piece.Type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -18,10 +19,49 @@ public class Board implements Cloneable {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	private Piece[][]				boardArr;
 | 
						private Piece[][]				boardArr;
 | 
				
			||||||
	private Map<Color, Position>	kingPos;
 | 
						private Map<Color, Position>	kingPos;
 | 
				
			||||||
 | 
						private Log						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 } });
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public Board() {
 | 
						public Board() {
 | 
				
			||||||
		boardArr	= new Piece[8][8];
 | 
							boardArr	= new Piece[8][8];
 | 
				
			||||||
		kingPos		= new HashMap<>();
 | 
							kingPos		= new HashMap<>();
 | 
				
			||||||
 | 
							log			= new Log();
 | 
				
			||||||
		initializeDefaultPositions();
 | 
							initializeDefaultPositions();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -35,12 +75,15 @@ public class Board implements Cloneable {
 | 
				
			|||||||
		Piece piece = getPos(move);
 | 
							Piece piece = getPos(move);
 | 
				
			||||||
		if (piece == null || !piece.isValidMove(move)) return false;
 | 
							if (piece == null || !piece.isValidMove(move)) return false;
 | 
				
			||||||
		else {
 | 
							else {
 | 
				
			||||||
 | 
								// Set type after validation
 | 
				
			||||||
 | 
								if (move.type == Move.Type.UNKNOWN) move.type = Move.Type.NORMAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Move piece
 | 
								// Move piece
 | 
				
			||||||
			Piece capturePiece = move(move);
 | 
								move(move);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Revert move if it caused a check for its team
 | 
								// Revert move if it caused a check for its team
 | 
				
			||||||
			if (checkCheck(piece.getColor())) {
 | 
								if (checkCheck(piece.getColor())) {
 | 
				
			||||||
				revert(move, capturePiece);
 | 
									revert();
 | 
				
			||||||
				return false;
 | 
									return false;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -54,31 +97,97 @@ public class Board implements Cloneable {
 | 
				
			|||||||
	 * @param move The move to execute
 | 
						 * @param move The move to execute
 | 
				
			||||||
	 * @return The captures piece, or null if the move's destination was empty
 | 
						 * @return The captures piece, or null if the move's destination was empty
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public Piece move(Move move) {
 | 
						public void move(Move move) {
 | 
				
			||||||
		Piece	piece			= getPos(move);
 | 
							Piece	piece			= getPos(move);
 | 
				
			||||||
		Piece	capturePiece	= getDest(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 CASTLING:
 | 
				
			||||||
 | 
									// Move the king
 | 
				
			||||||
				setDest(move, piece);
 | 
									setDest(move, piece);
 | 
				
			||||||
				setPos(move, null);
 | 
									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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Move the rook
 | 
				
			||||||
 | 
									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
 | 
							// Update the king's position if the moved piece is the king
 | 
				
			||||||
		if (piece.getType() == Type.KING) kingPos.put(piece.getColor(), move.dest);
 | 
							if (piece.getType() == Type.KING) kingPos.put(piece.getColor(), move.dest);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return capturePiece;
 | 
							// Update log
 | 
				
			||||||
 | 
							log.add(move, capturePiece);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Reverts a move.
 | 
						 * Reverts the last move.
 | 
				
			||||||
	 * 
 | 
					 | 
				
			||||||
	 * @param move          The move to revert
 | 
					 | 
				
			||||||
	 * @param capturedPiece The piece that has been captured when the move has been
 | 
					 | 
				
			||||||
	 *                      applied
 | 
					 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public void revert(Move move, Piece capturedPiece) {
 | 
						public void revert() {
 | 
				
			||||||
 | 
							LoggedMove	loggedMove		= log.getLast();
 | 
				
			||||||
 | 
							Move		move			= loggedMove.move;
 | 
				
			||||||
 | 
							Piece		capturedPiece	= loggedMove.capturedPiece;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch (move.type) {
 | 
				
			||||||
 | 
								case PAWN_PROMOTION:
 | 
				
			||||||
 | 
									setPos(move, new Pawn(getDest(move).getColor(), this));
 | 
				
			||||||
 | 
									setDest(move, capturedPiece);
 | 
				
			||||||
 | 
									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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Move the rook
 | 
				
			||||||
 | 
									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));
 | 
									setPos(move, getDest(move));
 | 
				
			||||||
				setDest(move, capturedPiece);
 | 
									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
 | 
							// 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);
 | 
							if (getPos(move).getType() == Type.KING) kingPos.put(getPos(move).getColor(), move.pos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Update log
 | 
				
			||||||
 | 
							log.removeLast();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
@@ -129,9 +238,9 @@ public class Board implements Cloneable {
 | 
				
			|||||||
		if (!getMoves(kingPos.get(color)).isEmpty()) return false;
 | 
							if (!getMoves(kingPos.get(color)).isEmpty()) return false;
 | 
				
			||||||
		else {
 | 
							else {
 | 
				
			||||||
			for (Move move : getMoves(color)) {
 | 
								for (Move move : getMoves(color)) {
 | 
				
			||||||
				Piece	capturePiece	= move(move);
 | 
									move(move);
 | 
				
			||||||
				boolean check = checkCheck(color);
 | 
									boolean check = checkCheck(color);
 | 
				
			||||||
				revert(move, capturePiece);
 | 
									revert();
 | 
				
			||||||
				if (!check) return false;
 | 
									if (!check) return false;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return true;
 | 
								return true;
 | 
				
			||||||
@@ -139,8 +248,7 @@ public class Board implements Cloneable {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public GameState getGameEventType(Color color) {
 | 
						public GameState getGameEventType(Color color) {
 | 
				
			||||||
		return checkCheck(color)
 | 
							return checkCheck(color) ? checkCheckmate(color) ? GameState.CHECKMATE : GameState.CHECK
 | 
				
			||||||
				? checkCheckmate(color) ? GameState.CHECKMATE : GameState.CHECK
 | 
					 | 
				
			||||||
				: getMoves(color).isEmpty() ? GameState.STALEMATE : GameState.NORMAL;
 | 
									: getMoves(color).isEmpty() ? GameState.STALEMATE : GameState.NORMAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -154,23 +262,27 @@ public class Board implements Cloneable {
 | 
				
			|||||||
		int score = 0;
 | 
							int score = 0;
 | 
				
			||||||
		for (int i = 0; i < 8; i++)
 | 
							for (int i = 0; i < 8; i++)
 | 
				
			||||||
			for (int j = 0; j < 8; j++)
 | 
								for (int j = 0; j < 8; j++)
 | 
				
			||||||
				if (boardArr[i][j] != null && boardArr[i][j].getColor() == color) switch (boardArr[i][j].getType()) {
 | 
									if (boardArr[i][j] != null && boardArr[i][j].getColor() == color) {
 | 
				
			||||||
 | 
										switch (boardArr[i][j].getType()) {
 | 
				
			||||||
						case QUEEN:
 | 
											case QUEEN:
 | 
				
			||||||
						score += 8;
 | 
												score += 90;
 | 
				
			||||||
							break;
 | 
												break;
 | 
				
			||||||
						case ROOK:
 | 
											case ROOK:
 | 
				
			||||||
						score += 5;
 | 
												score += 50;
 | 
				
			||||||
							break;
 | 
												break;
 | 
				
			||||||
						case KNIGHT:
 | 
											case KNIGHT:
 | 
				
			||||||
						score += 3;
 | 
												score += 30;
 | 
				
			||||||
							break;
 | 
												break;
 | 
				
			||||||
						case BISHOP:
 | 
											case BISHOP:
 | 
				
			||||||
						score += 3;
 | 
												score += 30;
 | 
				
			||||||
							break;
 | 
												break;
 | 
				
			||||||
						case PAWN:
 | 
											case PAWN:
 | 
				
			||||||
						score += 1;
 | 
												score += 10;
 | 
				
			||||||
							break;
 | 
												break;
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
										if (positionScores.containsKey(boardArr[i][j].getType()))
 | 
				
			||||||
 | 
											score += positionScores.get(boardArr[i][j].getType())[i][color == Color.WHITE ? j : 7 - j];
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
		return score;
 | 
							return score;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -243,6 +355,8 @@ public class Board implements Cloneable {
 | 
				
			|||||||
		board.kingPos = new HashMap<>();
 | 
							board.kingPos = new HashMap<>();
 | 
				
			||||||
		board.kingPos.putAll(kingPos);
 | 
							board.kingPos.putAll(kingPos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							board.log = (Log) log.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return board;
 | 
							return board;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,26 @@ public class King extends Piece {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public boolean isValidMove(Move move) {
 | 
						public boolean isValidMove(Move move) {
 | 
				
			||||||
		return move.xDist <= 1 && move.yDist <= 1 && isFreePath(move);
 | 
							// Castling
 | 
				
			||||||
 | 
							if (getMoveCounter() == 0 && move.xDist == 2 && move.yDist == 0) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Kingside
 | 
				
			||||||
 | 
								if (board.getBoardArr()[7][move.pos.y] != null && board.getBoardArr()[7][move.pos.y].getType() == Type.ROOK
 | 
				
			||||||
 | 
										&& isFreePath(new Move(new Position(5, move.pos.y), new Position(7, move.pos.y)))) {
 | 
				
			||||||
 | 
									move.type = Move.Type.CASTLING;
 | 
				
			||||||
 | 
									return true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Queenside
 | 
				
			||||||
 | 
								if (board.getBoardArr()[0][move.pos.y] != null && board.getBoardArr()[0][move.pos.y].getType() == Type.ROOK
 | 
				
			||||||
 | 
										&& isFreePath(new Move(new Position(1, move.pos.y), new Position(4, move.pos.y)))) {
 | 
				
			||||||
 | 
									move.type = Move.Type.CASTLING;
 | 
				
			||||||
 | 
									return true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return move.xDist <= 1 && move.yDist <= 1 && checkDestination(move);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
@@ -29,9 +48,33 @@ public class King extends Piece {
 | 
				
			|||||||
					Move move = new Move(pos, new Position(i, j));
 | 
										Move move = new Move(pos, new Position(i, j));
 | 
				
			||||||
					if (board.getDest(move) == null || board.getDest(move).getColor() != getColor()) moves.add(move);
 | 
										if (board.getDest(move) == null || board.getDest(move).getColor() != getColor()) moves.add(move);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Castling
 | 
				
			||||||
 | 
							// TODO: Check attacked squares in between
 | 
				
			||||||
 | 
							// TODO: Castling out of check?
 | 
				
			||||||
 | 
							if (getMoveCounter() == 0) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Kingside
 | 
				
			||||||
 | 
								if (board.getBoardArr()[7][pos.y] != null && board.getBoardArr()[7][pos.y].getType() == Type.ROOK
 | 
				
			||||||
 | 
										&& isFreePath(new Move(new Position(5, pos.y), new Position(7, pos.y))))
 | 
				
			||||||
 | 
									moves.add(new Move(pos, new Position(6, pos.y), Move.Type.CASTLING));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Queenside
 | 
				
			||||||
 | 
								if (board.getBoardArr()[0][pos.y] != null && board.getBoardArr()[0][pos.y].getType() == Type.ROOK
 | 
				
			||||||
 | 
										&& isFreePath(new Move(new Position(1, pos.y), new Position(4, pos.y))))
 | 
				
			||||||
 | 
									moves.add(new Move(pos, new Position(2, pos.y), Move.Type.CASTLING));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return moves;
 | 
							return moves;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						protected boolean isFreePath(Move move) {
 | 
				
			||||||
 | 
							for (int i = move.pos.x, j = move.pos.y; i != move.dest.x || j != move.dest.y; i += move.xSign, j += move.ySign)
 | 
				
			||||||
 | 
								if (board.getBoardArr()[i][j] != null) return false;
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public Type getType() { return Type.KING; }
 | 
						public Type getType() { return Type.KING; }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,8 @@ public class Knight extends Piece {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public boolean isValidMove(Move move) {
 | 
						public boolean isValidMove(Move move) {
 | 
				
			||||||
		return Math.abs(move.xDist - move.yDist) == 1 && (move.xDist == 1 || move.yDist == 1) && isFreePath(move);
 | 
							return Math.abs(move.xDist - move.yDist) == 1
 | 
				
			||||||
 | 
									&& (move.xDist == 1 && move.yDist == 2 || move.xDist == 2 && move.yDist == 1) && checkDestination(move);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private void checkAndInsertMove(List<Move> moves, Position pos, int offsetX, int offsetY) {
 | 
						private void checkAndInsertMove(List<Move> moves, Position pos, int offsetX, int offsetY) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										55
									
								
								src/dev/kske/chess/board/Log.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/dev/kske/chess/board/Log.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					package dev.kske.chess.board;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Project: <strong>Chess</strong><br>
 | 
				
			||||||
 | 
					 * File: <strong>Log.java</strong><br>
 | 
				
			||||||
 | 
					 * Created: <strong>09.07.2019</strong><br>
 | 
				
			||||||
 | 
					 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class Log implements Cloneable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private List<LoggedMove> moves;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public Log() {
 | 
				
			||||||
 | 
							moves = new ArrayList<>();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void add(Move move, Piece capturedPiece) {
 | 
				
			||||||
 | 
							moves.add(new LoggedMove(move, capturedPiece));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public LoggedMove getLast() {
 | 
				
			||||||
 | 
							return moves.get(moves.size() - 1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						public void removeLast() {
 | 
				
			||||||
 | 
							if (!moves.isEmpty()) moves.remove(moves.size() - 1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public Object clone() {
 | 
				
			||||||
 | 
							Log log = null;
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								log = (Log) super.clone();
 | 
				
			||||||
 | 
							} catch (CloneNotSupportedException e) {
 | 
				
			||||||
 | 
								e.printStackTrace();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							log.moves = new ArrayList<>();
 | 
				
			||||||
 | 
							log.moves.addAll(this.moves);
 | 
				
			||||||
 | 
							return log;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public static class LoggedMove {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public final Move		move;
 | 
				
			||||||
 | 
							public final Piece	capturedPiece;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public LoggedMove(Move move, Piece capturedPiece) {
 | 
				
			||||||
 | 
								this.move		= move;
 | 
				
			||||||
 | 
								this.capturedPiece	= capturedPiece;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -10,16 +10,22 @@ public class Move {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	public final Position	pos, dest;
 | 
						public final Position	pos, dest;
 | 
				
			||||||
	public final int		xDist, yDist, xSign, ySign;
 | 
						public final int		xDist, yDist, xSign, ySign;
 | 
				
			||||||
 | 
						public Type				type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public Move(Position pos, Position dest) {
 | 
						public Move(Position pos, Position dest, Type type) {
 | 
				
			||||||
		this.pos	= pos;
 | 
							this.pos	= pos;
 | 
				
			||||||
		this.dest	= dest;
 | 
							this.dest	= dest;
 | 
				
			||||||
 | 
							this.type	= type;
 | 
				
			||||||
		xDist		= Math.abs(dest.x - pos.x);
 | 
							xDist		= Math.abs(dest.x - pos.x);
 | 
				
			||||||
		yDist		= Math.abs(dest.y - pos.y);
 | 
							yDist		= Math.abs(dest.y - pos.y);
 | 
				
			||||||
		xSign		= (int) Math.signum(dest.x - pos.x);
 | 
							xSign		= (int) Math.signum(dest.x - pos.x);
 | 
				
			||||||
		ySign		= (int) Math.signum(dest.y - pos.y);
 | 
							ySign		= (int) Math.signum(dest.y - pos.y);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public Move(Position pos, Position dest) {
 | 
				
			||||||
 | 
							this(pos, dest, Type.NORMAL);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public Move(int xPos, int yPos, int xDest, int yDest) {
 | 
						public Move(int xPos, int yPos, int xDest, int yDest) {
 | 
				
			||||||
		this(new Position(xPos, yPos), new Position(xDest, yDest));
 | 
							this(new Position(xPos, yPos), new Position(xDest, yDest));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -34,4 +40,8 @@ public class Move {
 | 
				
			|||||||
	public String toString() {
 | 
						public String toString() {
 | 
				
			||||||
		return String.format("%s -> %s", pos, dest);
 | 
							return String.format("%s -> %s", pos, dest);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public static enum Type {
 | 
				
			||||||
 | 
							NORMAL, PAWN_PROMOTION, CASTLING, EN_PASSANT, UNKNOWN
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,12 +17,17 @@ public class Pawn extends Piece {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public boolean isValidMove(Move move) {
 | 
						public boolean isValidMove(Move move) {
 | 
				
			||||||
		// TODO: en passant, pawn promotion
 | 
							// TODO: en passant
 | 
				
			||||||
		boolean	step		= move.isVertical() && move.yDist == 1;
 | 
							boolean	step		= move.isVertical() && move.yDist == 1;
 | 
				
			||||||
		boolean	doubleStep	= move.isVertical() && move.yDist == 2;
 | 
							boolean	doubleStep	= move.isVertical() && move.yDist == 2;
 | 
				
			||||||
		boolean	strafe		= move.isDiagonal() && move.xDist == 1;
 | 
							boolean	strafe		= move.isDiagonal() && move.xDist == 1;
 | 
				
			||||||
		if (getColor() == Color.WHITE) doubleStep &= move.pos.y == 6;
 | 
							if (getColor() == Color.WHITE) doubleStep &= move.pos.y == 6;
 | 
				
			||||||
		else doubleStep &= move.pos.y == 1;
 | 
							else doubleStep &= move.pos.y == 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Mark move as pawn promotion if necessary
 | 
				
			||||||
 | 
							if (move.ySign == 1 && move.pos.y == 6 || move.ySign == -1 && move.pos.y == 1)
 | 
				
			||||||
 | 
								move.type = Move.Type.PAWN_PROMOTION;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return (step ^ doubleStep ^ strafe) && move.ySign == (getColor() == Color.WHITE ? -1 : 1) && isFreePath(move);
 | 
							return (step ^ doubleStep ^ strafe) && move.ySign == (getColor() == Color.WHITE ? -1 : 1) && isFreePath(move);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -43,8 +48,6 @@ public class Pawn extends Piece {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		int sign = getColor() == Color.WHITE ? -1 : 1;
 | 
							int sign = getColor() == Color.WHITE ? -1 : 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (sign == -1 && pos.y == 1 || sign == 1 && pos.y == 7) return moves;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Strafe left
 | 
							// Strafe left
 | 
				
			||||||
		if (pos.x > 0) {
 | 
							if (pos.x > 0) {
 | 
				
			||||||
			Move move = new Move(pos, new Position(pos.x - 1, pos.y + sign));
 | 
								Move move = new Move(pos, new Position(pos.x - 1, pos.y + sign));
 | 
				
			||||||
@@ -68,6 +71,11 @@ public class Pawn extends Piece {
 | 
				
			|||||||
			Move move = new Move(pos, new Position(pos.x, pos.y + 2 * sign));
 | 
								Move move = new Move(pos, new Position(pos.x, pos.y + 2 * sign));
 | 
				
			||||||
			if (isFreePath(move)) moves.add(move);
 | 
								if (isFreePath(move)) moves.add(move);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Mark moves as pawn promotions if necessary
 | 
				
			||||||
 | 
							if (sign == 1 && pos.y == 6 || sign == -1 && pos.y == 1)
 | 
				
			||||||
 | 
								moves.parallelStream().forEach(m -> m.type = Move.Type.PAWN_PROMOTION);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return moves;
 | 
							return moves;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,8 +11,9 @@ import java.util.List;
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
public abstract class Piece implements Cloneable {
 | 
					public abstract class Piece implements Cloneable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected Color	color;
 | 
						private final Color	color;
 | 
				
			||||||
	protected Board		board;
 | 
						protected Board		board;
 | 
				
			||||||
 | 
						private int			moveCounter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public Piece(Color color, Board board) {
 | 
						public Piece(Color color, Board board) {
 | 
				
			||||||
		this.color	= color;
 | 
							this.color	= color;
 | 
				
			||||||
@@ -23,10 +24,9 @@ public abstract class Piece implements Cloneable {
 | 
				
			|||||||
		List<Move> moves = getPseudolegalMoves(pos);
 | 
							List<Move> moves = getPseudolegalMoves(pos);
 | 
				
			||||||
		for (Iterator<Move> iterator = moves.iterator(); iterator.hasNext();) {
 | 
							for (Iterator<Move> iterator = moves.iterator(); iterator.hasNext();) {
 | 
				
			||||||
			Move move = iterator.next();
 | 
								Move move = iterator.next();
 | 
				
			||||||
			Piece	capturePiece	= board.move(move);
 | 
								board.move(move);
 | 
				
			||||||
			if (board.checkCheck(getColor()))
 | 
								if (board.checkCheck(getColor())) iterator.remove();
 | 
				
			||||||
				iterator.remove();
 | 
								board.revert();
 | 
				
			||||||
			board.revert(move, capturePiece);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return moves;
 | 
							return moves;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -66,8 +66,18 @@ public abstract class Piece implements Cloneable {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	public Color getColor() { return color; }
 | 
						public Color getColor() { return color; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public int getMoveCounter() { return moveCounter; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void incMoveCounter() {
 | 
				
			||||||
 | 
							++moveCounter;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void decMoveCounter() {
 | 
				
			||||||
 | 
							--moveCounter;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public static enum Type {
 | 
						public static enum Type {
 | 
				
			||||||
		KING, QUEEN, ROOK, KNIGHT, BISHOP, PAWN;
 | 
							KING, QUEEN, ROOK, KNIGHT, BISHOP, PAWN
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public static enum Color {
 | 
						public static enum Color {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,16 @@
 | 
				
			|||||||
package dev.kske.chess.game;
 | 
					package dev.kske.chess.game;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import dev.kske.chess.board.Board;
 | 
					import dev.kske.chess.board.Board;
 | 
				
			||||||
import dev.kske.chess.board.GameState;
 | 
					import dev.kske.chess.board.GameState;
 | 
				
			||||||
import dev.kske.chess.board.Move;
 | 
					import dev.kske.chess.board.Move;
 | 
				
			||||||
import dev.kske.chess.board.Piece.Color;
 | 
					import dev.kske.chess.board.Piece.Color;
 | 
				
			||||||
 | 
					import dev.kske.chess.game.ai.AIPlayer;
 | 
				
			||||||
import dev.kske.chess.ui.BoardComponent;
 | 
					import dev.kske.chess.ui.BoardComponent;
 | 
				
			||||||
 | 
					import dev.kske.chess.ui.BoardPane;
 | 
				
			||||||
 | 
					import dev.kske.chess.ui.OverlayComponent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Project: <strong>Chess</strong><br>
 | 
					 * Project: <strong>Chess</strong><br>
 | 
				
			||||||
@@ -18,19 +22,44 @@ public class Game {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	private Map<Color, Player>	players;
 | 
						private Map<Color, Player>	players;
 | 
				
			||||||
	private Board				board;
 | 
						private Board				board;
 | 
				
			||||||
 | 
						private OverlayComponent	overlayComponent;
 | 
				
			||||||
	private BoardComponent		boardComponent;
 | 
						private BoardComponent		boardComponent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public Game(Map<Color, Player> players, BoardComponent boardComponent) {
 | 
						public Game(Map<Color, Player> players, BoardPane boardPane) {
 | 
				
			||||||
		this.players			= players;
 | 
							this.players			= players;
 | 
				
			||||||
		this.boardComponent	= boardComponent;
 | 
							this.overlayComponent	= boardPane.getOverlayComponent();
 | 
				
			||||||
		this.board			= boardComponent.getBoard();
 | 
							this.boardComponent		= boardPane.getBoardComponent();
 | 
				
			||||||
 | 
							this.board				= new Board();
 | 
				
			||||||
 | 
							boardComponent.setBoard(board);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Initialize the game variable in each player
 | 
							// Initialize the game variable in each player
 | 
				
			||||||
		players.values().forEach(player -> player.setGame(this));
 | 
							players.values().forEach(player -> player.setGame(this));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public void start() {
 | 
						public static Game createNatural(BoardPane boardPane) {
 | 
				
			||||||
		players.get(Color.WHITE).requestMove();
 | 
							Map<Color, Player>	players	= new HashMap<>();
 | 
				
			||||||
 | 
							OverlayComponent	overlay	= boardPane.getOverlayComponent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							players.put(Color.WHITE, new NaturalPlayer(Color.WHITE, overlay));
 | 
				
			||||||
 | 
							players.put(Color.BLACK, new NaturalPlayer(Color.BLACK, overlay));
 | 
				
			||||||
 | 
							return new Game(players, boardPane);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public static Game createNaturalVsAI(BoardPane boardPane, int maxDepth, int alphaBeta) {
 | 
				
			||||||
 | 
							Map<Color, Player>	players	= new HashMap<>();
 | 
				
			||||||
 | 
							OverlayComponent	overlay	= boardPane.getOverlayComponent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							players.put(Color.WHITE, new NaturalPlayer(Color.WHITE, overlay));
 | 
				
			||||||
 | 
							players.put(Color.BLACK, new AIPlayer(Color.BLACK, maxDepth, alphaBeta));
 | 
				
			||||||
 | 
							return new Game(players, boardPane);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public static Game createAIVsAI(BoardPane boardPane, int maxDepthW, int maxDepthB, int alphaBetaW, int alphaBetaB) {
 | 
				
			||||||
 | 
							Map<Color, Player> players = new HashMap<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							players.put(Color.WHITE, new AIPlayer(Color.WHITE, maxDepthW, alphaBetaW));
 | 
				
			||||||
 | 
							players.put(Color.BLACK, new AIPlayer(Color.BLACK, maxDepthB, alphaBetaB));
 | 
				
			||||||
 | 
							return new Game(players, boardPane);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public void onMove(Player player, Move move) {
 | 
						public void onMove(Player player, Move move) {
 | 
				
			||||||
@@ -47,8 +76,29 @@ public class Game {
 | 
				
			|||||||
				default:
 | 
									default:
 | 
				
			||||||
					boardComponent.repaint();
 | 
										boardComponent.repaint();
 | 
				
			||||||
					players.get(player.color.opposite()).requestMove();
 | 
										players.get(player.color.opposite()).requestMove();
 | 
				
			||||||
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								overlayComponent.displayArrow(move);
 | 
				
			||||||
		} else player.requestMove();
 | 
							} else player.requestMove();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void start() {
 | 
				
			||||||
 | 
							players.get(Color.WHITE).requestMove();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void reset() {
 | 
				
			||||||
 | 
							players.forEach((k, v) -> v.cancelMove());
 | 
				
			||||||
 | 
							board.initializeDefaultPositions();
 | 
				
			||||||
 | 
							boardComponent.repaint();
 | 
				
			||||||
 | 
							overlayComponent.clearDots();
 | 
				
			||||||
 | 
							overlayComponent.clearArrow();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Removed all connections between the game and the ui.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public void disconnect() {
 | 
				
			||||||
 | 
							players.values().forEach(Player::disconnect);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public Board getBoard() { return board; }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,13 @@
 | 
				
			|||||||
package dev.kske.chess.game;
 | 
					package dev.kske.chess.game;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.awt.event.MouseAdapter;
 | 
					 | 
				
			||||||
import java.awt.event.MouseEvent;
 | 
					import java.awt.event.MouseEvent;
 | 
				
			||||||
 | 
					import java.awt.event.MouseListener;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import dev.kske.chess.board.Board;
 | 
					import dev.kske.chess.board.Board;
 | 
				
			||||||
import dev.kske.chess.board.Move;
 | 
					import dev.kske.chess.board.Move;
 | 
				
			||||||
 | 
					import dev.kske.chess.board.Move.Type;
 | 
				
			||||||
import dev.kske.chess.board.Piece.Color;
 | 
					import dev.kske.chess.board.Piece.Color;
 | 
				
			||||||
import dev.kske.chess.board.Position;
 | 
					import dev.kske.chess.board.Position;
 | 
				
			||||||
import dev.kske.chess.ui.OverlayComponent;
 | 
					import dev.kske.chess.ui.OverlayComponent;
 | 
				
			||||||
@@ -17,17 +18,36 @@ import dev.kske.chess.ui.OverlayComponent;
 | 
				
			|||||||
 * Created: <strong>06.07.2019</strong><br>
 | 
					 * Created: <strong>06.07.2019</strong><br>
 | 
				
			||||||
 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
					 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class NaturalPlayer extends Player {
 | 
					public class NaturalPlayer extends Player implements MouseListener {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private final OverlayComponent overlayComponent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private boolean		moveRequested;
 | 
						private boolean		moveRequested;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	public NaturalPlayer(Board board, Color color, OverlayComponent overlayComponent) {
 | 
					 | 
				
			||||||
		super(board, color);
 | 
					 | 
				
			||||||
		moveRequested = false;
 | 
					 | 
				
			||||||
		overlayComponent.addMouseListener(new MouseAdapter() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private Position	pos;
 | 
						private Position	pos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public NaturalPlayer(Color color, OverlayComponent overlayComponent) {
 | 
				
			||||||
 | 
							super(color);
 | 
				
			||||||
 | 
							this.overlayComponent	= overlayComponent;
 | 
				
			||||||
 | 
							moveRequested			= false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							overlayComponent.addMouseListener(this);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public void requestMove() {
 | 
				
			||||||
 | 
							moveRequested = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public void cancelMove() {
 | 
				
			||||||
 | 
							moveRequested = false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public void disconnect() {
 | 
				
			||||||
 | 
							overlayComponent.removeMouseListener(this);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void mousePressed(MouseEvent evt) {
 | 
						public void mousePressed(MouseEvent evt) {
 | 
				
			||||||
		if (!moveRequested) return;
 | 
							if (!moveRequested) return;
 | 
				
			||||||
@@ -49,15 +69,20 @@ public class NaturalPlayer extends Player {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			overlayComponent.clearDots();
 | 
								overlayComponent.clearDots();
 | 
				
			||||||
			moveRequested = false;
 | 
								moveRequested = false;
 | 
				
			||||||
					game.onMove(NaturalPlayer.this, new Move(pos, dest));
 | 
								game.onMove(NaturalPlayer.this, new Move(pos, dest, Type.UNKNOWN));
 | 
				
			||||||
			pos = null;
 | 
								pos = null;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void requestMove() {
 | 
						public void mouseClicked(MouseEvent e) {}
 | 
				
			||||||
		moveRequested = true;
 | 
					
 | 
				
			||||||
	}
 | 
						@Override
 | 
				
			||||||
 | 
						public void mouseReleased(MouseEvent e) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public void mouseEntered(MouseEvent e) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public void mouseExited(MouseEvent e) {}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,16 +15,22 @@ public abstract class Player {
 | 
				
			|||||||
	protected Board	board;
 | 
						protected Board	board;
 | 
				
			||||||
	protected Color	color;
 | 
						protected Color	color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public Player(Board board, Color color) {
 | 
						public Player(Color color) {
 | 
				
			||||||
		this.board	= board;
 | 
					 | 
				
			||||||
		this.color = color;
 | 
							this.color = color;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public abstract void requestMove();
 | 
						public abstract void requestMove();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public abstract void cancelMove();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public abstract void disconnect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public Game getGame() { return game; }
 | 
						public Game getGame() { return game; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public void setGame(Game game) { this.game = game; }
 | 
						public void setGame(Game game) {
 | 
				
			||||||
 | 
							this.game	= game;
 | 
				
			||||||
 | 
							board		= game.getBoard();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public Board getBoard() { return board; }
 | 
						public Board getBoard() { return board; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ import java.util.concurrent.ExecutionException;
 | 
				
			|||||||
import java.util.concurrent.ExecutorService;
 | 
					import java.util.concurrent.ExecutorService;
 | 
				
			||||||
import java.util.concurrent.Executors;
 | 
					import java.util.concurrent.Executors;
 | 
				
			||||||
import java.util.concurrent.Future;
 | 
					import java.util.concurrent.Future;
 | 
				
			||||||
 | 
					import java.util.concurrent.TimeUnit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javax.swing.SwingUtilities;
 | 
					import javax.swing.SwingUtilities;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -24,15 +25,22 @@ public class AIPlayer extends Player {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	private int	availableProcessors;
 | 
						private int	availableProcessors;
 | 
				
			||||||
	private int	maxDepth;
 | 
						private int	maxDepth;
 | 
				
			||||||
 | 
						private int	alphaBetaThreshold;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public AIPlayer(Board board, Color color, int maxDepth) {
 | 
						private volatile boolean			exitRequested;
 | 
				
			||||||
		super(board, color);
 | 
						private volatile ExecutorService	executor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public AIPlayer(Color color, int maxDepth, int alphaBetaThreshold) {
 | 
				
			||||||
 | 
							super(color);
 | 
				
			||||||
		availableProcessors		= Runtime.getRuntime().availableProcessors();
 | 
							availableProcessors		= Runtime.getRuntime().availableProcessors();
 | 
				
			||||||
		this.maxDepth			= maxDepth;
 | 
							this.maxDepth			= maxDepth;
 | 
				
			||||||
 | 
							this.alphaBetaThreshold	= alphaBetaThreshold;
 | 
				
			||||||
 | 
							exitRequested			= false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void requestMove() {
 | 
						public void requestMove() {
 | 
				
			||||||
 | 
							exitRequested = false;
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * Define some processing threads, split the available moves between them and
 | 
							 * Define some processing threads, split the available moves between them and
 | 
				
			||||||
		 * retrieve the result after their execution.
 | 
							 * retrieve the result after their execution.
 | 
				
			||||||
@@ -55,15 +63,15 @@ public class AIPlayer extends Player {
 | 
				
			|||||||
			for (int i = 0; i < numThreads; i++) {
 | 
								for (int i = 0; i < numThreads; i++) {
 | 
				
			||||||
				if (rem-- > 0) ++endIndex;
 | 
									if (rem-- > 0) ++endIndex;
 | 
				
			||||||
				endIndex += step;
 | 
									endIndex += step;
 | 
				
			||||||
				processors.add(
 | 
									processors.add(new MoveProcessor((Board) board.clone(), moves.subList(beginIndex, endIndex), color,
 | 
				
			||||||
						new MoveProcessor((Board) board.clone(), moves.subList(beginIndex, endIndex), color, maxDepth));
 | 
											maxDepth, alphaBetaThreshold));
 | 
				
			||||||
				beginIndex = endIndex;
 | 
									beginIndex = endIndex;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/*
 | 
								/*
 | 
				
			||||||
			 * Execute processors, get the best result and pass it back to the Game class
 | 
								 * Execute processors, get the best result and pass it back to the Game class
 | 
				
			||||||
			 */
 | 
								 */
 | 
				
			||||||
			ExecutorService			executor	= Executors.newFixedThreadPool(numThreads);
 | 
								executor = Executors.newFixedThreadPool(numThreads);
 | 
				
			||||||
			List<ProcessingResult> results = new ArrayList<>(numThreads);
 | 
								List<ProcessingResult> results = new ArrayList<>(numThreads);
 | 
				
			||||||
			try {
 | 
								try {
 | 
				
			||||||
				List<Future<ProcessingResult>> futures = executor.invokeAll(processors);
 | 
									List<Future<ProcessingResult>> futures = executor.invokeAll(processors);
 | 
				
			||||||
@@ -74,7 +82,23 @@ public class AIPlayer extends Player {
 | 
				
			|||||||
				ex.printStackTrace();
 | 
									ex.printStackTrace();
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			results.sort((r1, r2) -> Integer.compare(r2.score, r1.score));
 | 
								results.sort((r1, r2) -> Integer.compare(r2.score, r1.score));
 | 
				
			||||||
			SwingUtilities.invokeLater(() -> game.onMove(this, results.get(0).move));
 | 
								if (!exitRequested) SwingUtilities.invokeLater(() -> game.onMove(this, results.get(0).move));
 | 
				
			||||||
		}, "AIPlayer calculation setup").start();
 | 
							}, "AIPlayer calculation setup").start();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public void cancelMove() {
 | 
				
			||||||
 | 
							exitRequested = true;
 | 
				
			||||||
 | 
							if (executor != null) {
 | 
				
			||||||
 | 
								executor.shutdownNow();
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									executor.awaitTermination(500, TimeUnit.MILLISECONDS);
 | 
				
			||||||
 | 
								} catch (InterruptedException e) {
 | 
				
			||||||
 | 
									e.printStackTrace();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public void disconnect() {}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,6 @@ import java.util.concurrent.Callable;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import dev.kske.chess.board.Board;
 | 
					import dev.kske.chess.board.Board;
 | 
				
			||||||
import dev.kske.chess.board.Move;
 | 
					import dev.kske.chess.board.Move;
 | 
				
			||||||
import dev.kske.chess.board.Piece;
 | 
					 | 
				
			||||||
import dev.kske.chess.board.Piece.Color;
 | 
					import dev.kske.chess.board.Piece.Color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -17,17 +16,19 @@ import dev.kske.chess.board.Piece.Color;
 | 
				
			|||||||
public class MoveProcessor implements Callable<ProcessingResult> {
 | 
					public class MoveProcessor implements Callable<ProcessingResult> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private final Board			board;
 | 
						private final Board			board;
 | 
				
			||||||
	private final List<Move>	rootMoves;;
 | 
						private final List<Move>	rootMoves;
 | 
				
			||||||
	private final Color			color;
 | 
						private final Color			color;
 | 
				
			||||||
	private final int			maxDepth;
 | 
						private final int			maxDepth;
 | 
				
			||||||
 | 
						private final int			alphaBetaThreshold;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private Move bestMove;
 | 
						private Move bestMove;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public MoveProcessor(Board board, List<Move> rootMoves, Color color, int maxDepth) {
 | 
						public MoveProcessor(Board board, List<Move> rootMoves, Color color, int maxDepth, int alphaBetaThreshold) {
 | 
				
			||||||
		this.board		= board;
 | 
							this.board		= board;
 | 
				
			||||||
		this.rootMoves	= rootMoves;
 | 
							this.rootMoves	= rootMoves;
 | 
				
			||||||
		this.color		= color;
 | 
							this.color		= color;
 | 
				
			||||||
		this.maxDepth	= maxDepth;
 | 
							this.maxDepth	= maxDepth;
 | 
				
			||||||
 | 
							this.alphaBetaThreshold	= alphaBetaThreshold;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
@@ -39,12 +40,12 @@ public class MoveProcessor implements Callable<ProcessingResult> {
 | 
				
			|||||||
	private int miniMax(Board board, List<Move> moves, Color color, int depth) {
 | 
						private int miniMax(Board board, List<Move> moves, Color color, int depth) {
 | 
				
			||||||
		int bestValue = Integer.MIN_VALUE;
 | 
							int bestValue = Integer.MIN_VALUE;
 | 
				
			||||||
		for (Move move : moves) {
 | 
							for (Move move : moves) {
 | 
				
			||||||
			Piece	capturePiece	= board.move(move);
 | 
								board.move(move);
 | 
				
			||||||
			int		teamValue		= board.evaluate(color);
 | 
								int		teamValue		= board.evaluate(color);
 | 
				
			||||||
			int		enemyValue		= board.evaluate(color.opposite());
 | 
								int		enemyValue		= board.evaluate(color.opposite());
 | 
				
			||||||
			int		valueChange		= teamValue - enemyValue;
 | 
								int		valueChange		= teamValue - enemyValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (depth < maxDepth && valueChange >= 0)
 | 
								if (depth < maxDepth && valueChange >= alphaBetaThreshold)
 | 
				
			||||||
				valueChange -= miniMax(board, board.getMoves(color.opposite()), color.opposite(), depth + 1);
 | 
									valueChange -= miniMax(board, board.getMoves(color.opposite()), color.opposite(), depth + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (valueChange > bestValue) {
 | 
								if (valueChange > bestValue) {
 | 
				
			||||||
@@ -52,7 +53,7 @@ public class MoveProcessor implements Callable<ProcessingResult> {
 | 
				
			|||||||
				if (depth == 0) bestMove = move;
 | 
									if (depth == 0) bestMove = move;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			board.revert(move, capturePiece);
 | 
								board.revert();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return bestValue;
 | 
							return bestValue;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										82
									
								
								src/dev/kske/chess/ui/AIConfigDialog.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/dev/kske/chess/ui/AIConfigDialog.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
				
			|||||||
 | 
					package dev.kske.chess.ui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.awt.Dimension;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import javax.swing.JButton;
 | 
				
			||||||
 | 
					import javax.swing.JDialog;
 | 
				
			||||||
 | 
					import javax.swing.JLabel;
 | 
				
			||||||
 | 
					import javax.swing.JSpinner;
 | 
				
			||||||
 | 
					import javax.swing.SpinnerNumberModel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Project: <strong>Chess</strong><br>
 | 
				
			||||||
 | 
					 * File: <strong>AIConfigDialog.java</strong><br>
 | 
				
			||||||
 | 
					 * Created: <strong>16.07.2019</strong><br>
 | 
				
			||||||
 | 
					 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class AIConfigDialog extends JDialog {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static final long serialVersionUID = -8047984368152479992L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private int		maxDepth;
 | 
				
			||||||
 | 
						private int		alphaBetaThreshold;
 | 
				
			||||||
 | 
						private boolean	startGame	= false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public AIConfigDialog() {
 | 
				
			||||||
 | 
							setSize(new Dimension(337, 212));
 | 
				
			||||||
 | 
							setModal(true);
 | 
				
			||||||
 | 
							setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
 | 
				
			||||||
 | 
							setTitle("AI Configuration");
 | 
				
			||||||
 | 
							getContentPane().setLayout(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							JSpinner spAlphaBetaThreshold = new JSpinner();
 | 
				
			||||||
 | 
							spAlphaBetaThreshold.setBounds(222, 68, 95, 28);
 | 
				
			||||||
 | 
							getContentPane().add(spAlphaBetaThreshold);
 | 
				
			||||||
 | 
							spAlphaBetaThreshold.setModel(new SpinnerNumberModel(-10, -100, 100, 5));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							JSpinner spMaxDepth = new JSpinner();
 | 
				
			||||||
 | 
							spMaxDepth.setBounds(222, 6, 95, 28);
 | 
				
			||||||
 | 
							getContentPane().add(spMaxDepth);
 | 
				
			||||||
 | 
							spMaxDepth.setModel(new SpinnerNumberModel(4, 1, 10, 1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							JLabel lblAlphabetaThreshold = new JLabel("Alpha-Beta Threshold:");
 | 
				
			||||||
 | 
							lblAlphabetaThreshold.setBounds(16, 68, 194, 28);
 | 
				
			||||||
 | 
							getContentPane().add(lblAlphabetaThreshold);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							JButton btnOk = new JButton("OK");
 | 
				
			||||||
 | 
							btnOk.setBounds(16, 137, 84, 28);
 | 
				
			||||||
 | 
							getContentPane().add(btnOk);
 | 
				
			||||||
 | 
							btnOk.addActionListener((evt) -> {
 | 
				
			||||||
 | 
								maxDepth			= ((Integer) spMaxDepth.getValue()).intValue();
 | 
				
			||||||
 | 
								alphaBetaThreshold	= ((Integer) spAlphaBetaThreshold.getValue()).intValue();
 | 
				
			||||||
 | 
								startGame			= true;
 | 
				
			||||||
 | 
								dispose();
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
							btnOk.setToolTipText("Start the game");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							JButton btnCancel = new JButton("Cancel");
 | 
				
			||||||
 | 
							btnCancel.setBounds(222, 137, 95, 28);
 | 
				
			||||||
 | 
							getContentPane().add(btnCancel);
 | 
				
			||||||
 | 
							btnCancel.addActionListener((evt) -> dispose());
 | 
				
			||||||
 | 
							btnCancel.setToolTipText("Cancel the game start");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							JLabel lblMaximalRecursionDepth = new JLabel("Maximal Recursion Depth:");
 | 
				
			||||||
 | 
							lblMaximalRecursionDepth.setBounds(16, 12, 194, 16);
 | 
				
			||||||
 | 
							getContentPane().add(lblMaximalRecursionDepth);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							setLocationRelativeTo(null);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public int getMaxDepth() { return maxDepth; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void setMaxDepth(int maxDepth) { this.maxDepth = maxDepth; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public int getAlphaBetaThreshold() { return alphaBetaThreshold; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void setAlphaBetaThreshold(int alphaBetaThreshold) { this.alphaBetaThreshold = alphaBetaThreshold; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public boolean isStartGame() { return startGame; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void setStartGame(boolean startGame) { this.startGame = startGame; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,72 +0,0 @@
 | 
				
			|||||||
package dev.kske.chess.ui;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.awt.FlowLayout;
 | 
					 | 
				
			||||||
import java.util.HashMap;
 | 
					 | 
				
			||||||
import java.util.Map;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import javax.swing.JButton;
 | 
					 | 
				
			||||||
import javax.swing.JDialog;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import dev.kske.chess.board.Board;
 | 
					 | 
				
			||||||
import dev.kske.chess.board.Piece.Color;
 | 
					 | 
				
			||||||
import dev.kske.chess.game.Game;
 | 
					 | 
				
			||||||
import dev.kske.chess.game.NaturalPlayer;
 | 
					 | 
				
			||||||
import dev.kske.chess.game.Player;
 | 
					 | 
				
			||||||
import dev.kske.chess.game.ai.AIPlayer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Project: <strong>Chess</strong><br>
 | 
					 | 
				
			||||||
 * File: <strong>GameModeDialog.java</strong><br>
 | 
					 | 
				
			||||||
 * Created: <strong>06.07.2019</strong><br>
 | 
					 | 
				
			||||||
 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class GameModeDialog extends JDialog {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private static final long serialVersionUID = 5470026233924735607L;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Create the dialog.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public GameModeDialog(BoardPane boardPane) {
 | 
					 | 
				
			||||||
		super();
 | 
					 | 
				
			||||||
		setModal(true);
 | 
					 | 
				
			||||||
		setTitle("Game Mode Selection");
 | 
					 | 
				
			||||||
		setBounds(100, 100, 231, 133);
 | 
					 | 
				
			||||||
		setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
 | 
					 | 
				
			||||||
		getContentPane().setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		final BoardComponent	boardComponent		= boardPane.getBoardComponent();
 | 
					 | 
				
			||||||
		final OverlayComponent	overlayComponent	= boardPane.getOverlayComponent();
 | 
					 | 
				
			||||||
		final Board				board				= boardComponent.getBoard();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		JButton btnNatural = new JButton("Game against natural opponent");
 | 
					 | 
				
			||||||
		btnNatural.addActionListener((evt) -> {
 | 
					 | 
				
			||||||
			Map<Color, Player> players = new HashMap<>();
 | 
					 | 
				
			||||||
			players.put(Color.WHITE, new NaturalPlayer(board, Color.WHITE, overlayComponent));
 | 
					 | 
				
			||||||
			players.put(Color.BLACK, new NaturalPlayer(board, Color.BLACK, overlayComponent));
 | 
					 | 
				
			||||||
			new Game(players, boardComponent).start();
 | 
					 | 
				
			||||||
			dispose();
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
		getContentPane().add(btnNatural);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		JButton btnAI = new JButton("Game against AI");
 | 
					 | 
				
			||||||
		btnAI.addActionListener((evt) -> {
 | 
					 | 
				
			||||||
			Map<Color, Player> players = new HashMap<>();
 | 
					 | 
				
			||||||
			players.put(Color.WHITE, new NaturalPlayer(board, Color.WHITE, overlayComponent));
 | 
					 | 
				
			||||||
			players.put(Color.BLACK, new AIPlayer(board, Color.BLACK, 4));
 | 
					 | 
				
			||||||
			new Game(players, boardComponent).start();
 | 
					 | 
				
			||||||
			dispose();
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
		getContentPane().add(btnAI);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		JButton btnAI2 = new JButton("AI against AI");
 | 
					 | 
				
			||||||
		btnAI2.addActionListener((evt) -> {
 | 
					 | 
				
			||||||
			Map<Color, Player> players = new HashMap<>();
 | 
					 | 
				
			||||||
			players.put(Color.WHITE, new AIPlayer(board, Color.WHITE, 4));
 | 
					 | 
				
			||||||
			players.put(Color.BLACK, new AIPlayer(board, Color.BLACK, 3));
 | 
					 | 
				
			||||||
			new Game(players, boardComponent).start();
 | 
					 | 
				
			||||||
			dispose();
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
		getContentPane().add(btnAI2);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -2,13 +2,13 @@ package dev.kske.chess.ui;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import java.awt.BorderLayout;
 | 
					import java.awt.BorderLayout;
 | 
				
			||||||
import java.awt.EventQueue;
 | 
					import java.awt.EventQueue;
 | 
				
			||||||
 | 
					import java.awt.Toolkit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javax.swing.JButton;
 | 
					import javax.swing.JButton;
 | 
				
			||||||
import javax.swing.JFrame;
 | 
					import javax.swing.JFrame;
 | 
				
			||||||
import javax.swing.JPanel;
 | 
					import javax.swing.JPanel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import dev.kske.chess.board.Board;
 | 
					import dev.kske.chess.game.Game;
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Project: <strong>Chess</strong><br>
 | 
					 * Project: <strong>Chess</strong><br>
 | 
				
			||||||
@@ -19,6 +19,8 @@ import dev.kske.chess.board.Board;
 | 
				
			|||||||
public class MainWindow {
 | 
					public class MainWindow {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private JFrame		mframe;
 | 
						private JFrame		mframe;
 | 
				
			||||||
 | 
						private BoardPane	boardPane;
 | 
				
			||||||
 | 
						private Game		game;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Launch the application.
 | 
						 * Launch the application.
 | 
				
			||||||
@@ -53,19 +55,26 @@ public class MainWindow {
 | 
				
			|||||||
		mframe.setBounds(100, 100, 494, 565);
 | 
							mframe.setBounds(100, 100, 494, 565);
 | 
				
			||||||
		mframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 | 
							mframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		BoardPane boardPane = new BoardPane();
 | 
							mframe.setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("/pieces/king_white.png")));
 | 
				
			||||||
		boardPane.getBoardComponent().setBoard(new Board());
 | 
					
 | 
				
			||||||
 | 
							boardPane = new BoardPane();
 | 
				
			||||||
		mframe.getContentPane().add(boardPane, BorderLayout.CENTER);
 | 
							mframe.getContentPane().add(boardPane, BorderLayout.CENTER);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mframe.setJMenuBar(new MenuBar(this));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		JPanel toolPanel = new JPanel();
 | 
							JPanel toolPanel = new JPanel();
 | 
				
			||||||
		mframe.getContentPane().add(toolPanel, BorderLayout.NORTH);
 | 
							mframe.getContentPane().add(toolPanel, BorderLayout.NORTH);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		JButton btnRestart = new JButton("Restart");
 | 
							JButton btnRestart = new JButton("Restart");
 | 
				
			||||||
		btnRestart.addActionListener((evt) -> System.err.println("Resetting not implemented!"));
 | 
							btnRestart.addActionListener((evt) -> { if (game != null) game.reset(); game.start(); });
 | 
				
			||||||
		toolPanel.add(btnRestart);
 | 
							toolPanel.add(btnRestart);
 | 
				
			||||||
		mframe.pack();
 | 
							mframe.pack();
 | 
				
			||||||
 | 
							mframe.setLocationRelativeTo(null);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Display dialog for game mode selection
 | 
						public BoardPane getBoardPane() { return boardPane; }
 | 
				
			||||||
		new GameModeDialog(boardPane).setVisible(true);
 | 
					
 | 
				
			||||||
	}
 | 
						public Game getGame() { return game; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void setGame(Game game) { this.game = game; }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										64
									
								
								src/dev/kske/chess/ui/MenuBar.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/dev/kske/chess/ui/MenuBar.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
				
			|||||||
 | 
					package dev.kske.chess.ui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import javax.swing.JMenu;
 | 
				
			||||||
 | 
					import javax.swing.JMenuBar;
 | 
				
			||||||
 | 
					import javax.swing.JMenuItem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import dev.kske.chess.game.Game;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Project: <strong>Chess</strong><br>
 | 
				
			||||||
 | 
					 * File: <strong>MenuBar.java</strong><br>
 | 
				
			||||||
 | 
					 * Created: <strong>16.07.2019</strong><br>
 | 
				
			||||||
 | 
					 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class MenuBar extends JMenuBar {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static final long serialVersionUID = -7221583703531248228L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private final MainWindow	mainWindow;
 | 
				
			||||||
 | 
						private final BoardPane		boardPane;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public MenuBar(MainWindow mainWindow) {
 | 
				
			||||||
 | 
							this.mainWindow	= mainWindow;
 | 
				
			||||||
 | 
							boardPane		= mainWindow.getBoardPane();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							initGameMenu();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private void initGameMenu() {
 | 
				
			||||||
 | 
							JMenu gameMenu = new JMenu("Game");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							JMenuItem	naturalMenuItem	= new JMenuItem("Game against natural opponent");
 | 
				
			||||||
 | 
							JMenuItem	aiMenuItem		= new JMenuItem("Game against artificial opponent");
 | 
				
			||||||
 | 
							JMenuItem	aiVsAiMenuItem	= new JMenuItem("Watch AI vs. AI");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							naturalMenuItem.addActionListener((evt) -> startGame(Game.createNatural(boardPane)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							aiMenuItem.addActionListener((evt) -> {
 | 
				
			||||||
 | 
								AIConfigDialog dialog = new AIConfigDialog();
 | 
				
			||||||
 | 
								dialog.setVisible(true);
 | 
				
			||||||
 | 
								if (dialog.isStartGame())
 | 
				
			||||||
 | 
									startGame(Game.createNaturalVsAI(boardPane, dialog.getMaxDepth(), dialog.getAlphaBetaThreshold()));
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							aiVsAiMenuItem.addActionListener((evt) -> startGame(Game.createAIVsAI(boardPane, 4, 3, -10, -10)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							gameMenu.add(naturalMenuItem);
 | 
				
			||||||
 | 
							gameMenu.add(aiMenuItem);
 | 
				
			||||||
 | 
							gameMenu.add(aiVsAiMenuItem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							add(gameMenu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Start a game
 | 
				
			||||||
 | 
							naturalMenuItem.doClick();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private void startGame(Game game) {
 | 
				
			||||||
 | 
							mainWindow.setGame(game);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Update board and board component
 | 
				
			||||||
 | 
							game.reset();
 | 
				
			||||||
 | 
							game.start();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,11 +2,17 @@ package dev.kske.chess.ui;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import java.awt.Color;
 | 
					import java.awt.Color;
 | 
				
			||||||
import java.awt.Graphics;
 | 
					import java.awt.Graphics;
 | 
				
			||||||
 | 
					import java.awt.Graphics2D;
 | 
				
			||||||
 | 
					import java.awt.Point;
 | 
				
			||||||
 | 
					import java.awt.Polygon;
 | 
				
			||||||
 | 
					import java.awt.Shape;
 | 
				
			||||||
 | 
					import java.awt.geom.AffineTransform;
 | 
				
			||||||
import java.util.ArrayList;
 | 
					import java.util.ArrayList;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javax.swing.JComponent;
 | 
					import javax.swing.JComponent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import dev.kske.chess.board.Move;
 | 
				
			||||||
import dev.kske.chess.board.Position;
 | 
					import dev.kske.chess.board.Position;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -22,6 +28,7 @@ public class OverlayComponent extends JComponent {
 | 
				
			|||||||
	private final BoardPane boardPane;
 | 
						private final BoardPane boardPane;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private List<Position>	dots;
 | 
						private List<Position>	dots;
 | 
				
			||||||
 | 
						private Move			arrow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public OverlayComponent(BoardPane boardPane) {
 | 
						public OverlayComponent(BoardPane boardPane) {
 | 
				
			||||||
		this.boardPane = boardPane;
 | 
							this.boardPane = boardPane;
 | 
				
			||||||
@@ -45,6 +52,43 @@ public class OverlayComponent extends JComponent {
 | 
				
			|||||||
						radius,
 | 
											radius,
 | 
				
			||||||
						radius);
 | 
											radius);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (arrow != null) {
 | 
				
			||||||
 | 
								g.setColor(new Color(255, 0, 0, 127));
 | 
				
			||||||
 | 
								Point	pos		= new Point(arrow.pos.x * tileSize + tileSize / 2, arrow.pos.y * tileSize + tileSize / 2);
 | 
				
			||||||
 | 
								Point	dest	= new Point(arrow.dest.x * tileSize + tileSize / 2, arrow.dest.y * tileSize + tileSize / 2);
 | 
				
			||||||
 | 
								((Graphics2D) g).fill(createArrowShape(pos, dest));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private Shape createArrowShape(Point pos, Point dest) {
 | 
				
			||||||
 | 
							Polygon arrowPolygon = new Polygon();
 | 
				
			||||||
 | 
							arrowPolygon.addPoint(-6, 1);
 | 
				
			||||||
 | 
							arrowPolygon.addPoint(3, 1);
 | 
				
			||||||
 | 
							arrowPolygon.addPoint(3, 3);
 | 
				
			||||||
 | 
							arrowPolygon.addPoint(6, 0);
 | 
				
			||||||
 | 
							arrowPolygon.addPoint(3, -3);
 | 
				
			||||||
 | 
							arrowPolygon.addPoint(3, -1);
 | 
				
			||||||
 | 
							arrowPolygon.addPoint(-6, -1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Point midPoint = midpoint(pos, dest);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							double	rotate		= Math.atan2(dest.y - pos.y, dest.x - pos.x);
 | 
				
			||||||
 | 
							double	ptDistance	= pos.distance(dest);
 | 
				
			||||||
 | 
							double	scale		= ptDistance / 12.0;							// 12 because it's the length of the arrow
 | 
				
			||||||
 | 
																								// polygon.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							AffineTransform transform = new AffineTransform();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							transform.translate(midPoint.x, midPoint.y);
 | 
				
			||||||
 | 
							transform.rotate(rotate);
 | 
				
			||||||
 | 
							transform.scale(scale, 5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return transform.createTransformedShape(arrowPolygon);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private Point midpoint(Point p1, Point p2) {
 | 
				
			||||||
 | 
							return new Point((int) ((p1.x + p2.x) / 2.0), (int) ((p1.y + p2.y) / 2.0));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public void displayDots(List<Position> dots) {
 | 
						public void displayDots(List<Position> dots) {
 | 
				
			||||||
@@ -58,5 +102,15 @@ public class OverlayComponent extends JComponent {
 | 
				
			|||||||
		repaint();
 | 
							repaint();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void displayArrow(Move arrow) {
 | 
				
			||||||
 | 
							this.arrow = arrow;
 | 
				
			||||||
 | 
							repaint();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void clearArrow() {
 | 
				
			||||||
 | 
							arrow = null;
 | 
				
			||||||
 | 
							repaint();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public int getTileSize() { return boardPane.getTileSize(); }
 | 
						public int getTileSize() { return boardPane.getTileSize(); }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,8 +2,8 @@ package dev.kske.chess.ui;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import java.awt.Image;
 | 
					import java.awt.Image;
 | 
				
			||||||
import java.awt.image.BufferedImage;
 | 
					import java.awt.image.BufferedImage;
 | 
				
			||||||
import java.io.File;
 | 
					 | 
				
			||||||
import java.io.IOException;
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
import java.util.HashMap;
 | 
					import java.util.HashMap;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -19,11 +19,13 @@ import dev.kske.chess.board.Piece;
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
public class TextureUtil {
 | 
					public class TextureUtil {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static Map<String, Image> textures;
 | 
						private static Map<String, Image> textures, scaledTextures;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	static {
 | 
						static {
 | 
				
			||||||
		textures = new HashMap<>();
 | 
							textures = new HashMap<>();
 | 
				
			||||||
 | 
							scaledTextures	= new HashMap<>();
 | 
				
			||||||
		loadPieceTextures();
 | 
							loadPieceTextures();
 | 
				
			||||||
 | 
							scaledTextures.putAll(textures);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private TextureUtil() {}
 | 
						private TextureUtil() {}
 | 
				
			||||||
@@ -36,26 +38,28 @@ public class TextureUtil {
 | 
				
			|||||||
	 */
 | 
						 */
 | 
				
			||||||
	public static Image getPieceTexture(Piece piece) {
 | 
						public static Image getPieceTexture(Piece piece) {
 | 
				
			||||||
		String key = piece.getType().toString().toLowerCase() + "_" + piece.getColor().toString().toLowerCase();
 | 
							String key = piece.getType().toString().toLowerCase() + "_" + piece.getColor().toString().toLowerCase();
 | 
				
			||||||
		return textures.get(key);
 | 
							return scaledTextures.get(key);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Scales all piece textures to fit the current tile size
 | 
						 * Scales all piece textures to fit the current tile size
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public static void scalePieceTextures(int scale) {
 | 
						public static void scalePieceTextures(int scale) {
 | 
				
			||||||
		textures.replaceAll((key, img) -> img.getScaledInstance(scale, scale, Image.SCALE_SMOOTH));
 | 
							scaledTextures.clear();
 | 
				
			||||||
 | 
							textures
 | 
				
			||||||
 | 
								.forEach((key, img) -> scaledTextures.put(key, img.getScaledInstance(scale, scale, Image.SCALE_SMOOTH)));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Loads an image from a file.
 | 
						 * Loads an image from a file in the resource folder.
 | 
				
			||||||
	 * 
 | 
						 * 
 | 
				
			||||||
	 * @param file The image file
 | 
						 * @param fileName The name of the image resource
 | 
				
			||||||
	 * @return The loaded image
 | 
						 * @return The loaded image
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	private static Image loadImage(File file) {
 | 
						private static Image loadImage(String fileName) {
 | 
				
			||||||
		BufferedImage in = null;
 | 
							BufferedImage in = null;
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			in = ImageIO.read(file);
 | 
								in = ImageIO.read(TextureUtil.class.getResourceAsStream(fileName));
 | 
				
			||||||
		} catch (IOException e) {
 | 
							} catch (IOException e) {
 | 
				
			||||||
			e.printStackTrace();
 | 
								e.printStackTrace();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -67,10 +71,20 @@ public class TextureUtil {
 | 
				
			|||||||
	 * The filenames without extensions are used as keys in the map textures.
 | 
						 * The filenames without extensions are used as keys in the map textures.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	private static void loadPieceTextures() {
 | 
						private static void loadPieceTextures() {
 | 
				
			||||||
		File	dir		= new File("res/pieces");
 | 
							Arrays
 | 
				
			||||||
		File[]	files	= dir.listFiles((File parentDir, String name) -> name.toLowerCase().endsWith(".png"));
 | 
								.asList("king_white",
 | 
				
			||||||
		for (File file : files)
 | 
										"king_black",
 | 
				
			||||||
			textures.put(file.getName().replaceFirst("[.][^.]+$", ""), TextureUtil.loadImage(file));
 | 
										"queen_white",
 | 
				
			||||||
 | 
										"queen_black",
 | 
				
			||||||
 | 
										"rook_white",
 | 
				
			||||||
 | 
										"rook_black",
 | 
				
			||||||
 | 
										"knight_white",
 | 
				
			||||||
 | 
										"knight_black",
 | 
				
			||||||
 | 
										"bishop_white",
 | 
				
			||||||
 | 
										"bishop_black",
 | 
				
			||||||
 | 
										"pawn_white",
 | 
				
			||||||
 | 
										"pawn_black")
 | 
				
			||||||
 | 
								.forEach(name -> textures.put(name, loadImage("/pieces/" + name + ".png")));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,11 +12,11 @@ import dev.kske.chess.board.Queen;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Project: <strong>Chess</strong><br>
 | 
					 * Project: <strong>Chess</strong><br>
 | 
				
			||||||
 * File: <strong>BoardCloneTest.java</strong><br>
 | 
					 * File: <strong>BoardTest.java</strong><br>
 | 
				
			||||||
 * Created: <strong>08.07.2019</strong><br>
 | 
					 * Created: <strong>08.07.2019</strong><br>
 | 
				
			||||||
 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
					 * Author: <strong>Kai S. K. Engelbart</strong>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class BoardCloneTest {
 | 
					class BoardTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Board board;
 | 
						Board board;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -40,5 +40,4 @@ class BoardCloneTest {
 | 
				
			|||||||
		clone.getBoardArr()[0][0] = new Queen(Color.BLACK, clone);
 | 
							clone.getBoardArr()[0][0] = new Queen(Color.BLACK, clone);
 | 
				
			||||||
		assertNotEquals(clone.getBoardArr()[0][0], board.getBoardArr()[0][0]);
 | 
							assertNotEquals(clone.getBoardArr()[0][0], board.getBoardArr()[0][0]);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user