diff --git a/.classpath b/.classpath
index 8469573..afa4751 100644
--- a/.classpath
+++ b/.classpath
@@ -1,18 +1,22 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/dev/kske/chess/board/Board.java b/src/dev/kske/chess/board/Board.java
index 51bf632..d7c161d 100644
--- a/src/dev/kske/chess/board/Board.java
+++ b/src/dev/kske/chess/board/Board.java
@@ -17,7 +17,7 @@ import dev.kske.chess.event.MoveEvent;
public class Board {
private Piece[][] boardArr = new Piece[8][8];
- private Map kingPos = new HashMap<>();
+ private Map kingPos = new EnumMap<>(Color.class);
private Log log = new Log();
/**
diff --git a/src/dev/kske/chess/board/MoveNode.java b/src/dev/kske/chess/board/MoveNode.java
index 608f2ff..162612f 100644
--- a/src/dev/kske/chess/board/MoveNode.java
+++ b/src/dev/kske/chess/board/MoveNode.java
@@ -158,6 +158,18 @@ public class MoveNode {
*/
public boolean hasParent() { return parent != null; }
+ @Override
+ public String toString() {
+ return String.format("MoveNode[move=%s,capturedPiece=%s,castlingRights=%s,enPassant=%s,activeColor=%s,fullmoveCounter=%d,halfmoveClock=%d]",
+ move,
+ capturedPiece,
+ Arrays.toString(castlingRights),
+ enPassant,
+ activeColor,
+ fullmoveCounter,
+ halfmoveClock);
+ }
+
@Override
public int hashCode() {
final int prime = 31;
diff --git a/src/dev/kske/chess/board/Piece.java b/src/dev/kske/chess/board/Piece.java
index a0ea314..6bbf6f2 100644
--- a/src/dev/kske/chess/board/Piece.java
+++ b/src/dev/kske/chess/board/Piece.java
@@ -98,7 +98,7 @@ public abstract class Piece implements Cloneable {
}
@Override
- public String toString() { return getClass().getSimpleName(); }
+ public String toString() { return String.format("%s[color=%s]", getClass().getSimpleName(), color); }
@Override
public int hashCode() { return Objects.hash(color); }
@@ -122,7 +122,7 @@ public abstract class Piece implements Cloneable {
* @return The first character of this {@link Piece} in algebraic notation and
* lower case
*/
- public char firstChar() { return Character.toLowerCase(toString().charAt(0)); }
+ public char firstChar() { return Character.toLowerCase(getClass().getSimpleName().charAt(0)); }
/**
* @param firstChar the first character of a piece's name
diff --git a/src/dev/kske/chess/game/Game.java b/src/dev/kske/chess/game/Game.java
index 8905c1c..6c42cf3 100644
--- a/src/dev/kske/chess/game/Game.java
+++ b/src/dev/kske/chess/game/Game.java
@@ -1,6 +1,6 @@
package dev.kske.chess.game;
-import java.util.HashMap;
+import java.util.EnumMap;
import java.util.Map;
import javax.swing.JOptionPane;
@@ -29,7 +29,7 @@ import dev.kske.chess.ui.OverlayComponent;
*/
public class Game {
- private Map players = new HashMap<>(2);
+ private Map players = new EnumMap<>(Color.class);
private Board board;
private OverlayComponent overlayComponent;
private BoardComponent boardComponent;
@@ -69,9 +69,6 @@ public class Game {
// Initialize players
players.put(Color.WHITE, getPlayer(whiteName, Color.WHITE));
players.put(Color.BLACK, getPlayer(blackName, Color.BLACK));
-
- // Initialize the game variable in each player
- players.values().forEach(player -> player.setGame(this));
}
/**
@@ -88,12 +85,12 @@ public class Game {
private Player getPlayer(String name, Color color) {
switch (name) {
case "Natural Player":
- return new NaturalPlayer(color, overlayComponent);
+ return new NaturalPlayer(this, color, overlayComponent);
case "AI Player":
- return new AIPlayer(color, 4, -10);
+ return new AIPlayer(this, color, 4, -10);
default:
for (EngineInfo info : EngineUtil.getEngineInfos())
- if (info.name.equals(name)) return new UCIPlayer(color, info.path);
+ if (info.name.equals(name)) return new UCIPlayer(this, color, info.path);
System.err.println("Invalid player name: " + name);
return null;
}
@@ -107,7 +104,7 @@ public class Game {
* @param player the player who generated the move
* @param move the generated move
*/
- public void onMove(Player player, Move move) {
+ public synchronized void onMove(Player player, Move move) {
if (board.getPos(move).getColor() == player.color && board.attemptMove(move)) {
// Redraw
@@ -138,7 +135,7 @@ public class Game {
* Starts the game by requesting a move from the player of the currently active
* color.
*/
- public void start() {
+ public synchronized void start() {
EventBus.getInstance().dispatch(new GameStartEvent(this));
players.get(board.getLog().getActiveColor()).requestMove();
}
@@ -147,7 +144,7 @@ public class Game {
* Cancels move calculations, initializes the default position and clears the
* {@link OverlayComponent}.
*/
- public void reset() {
+ public synchronized void reset() {
players.values().forEach(Player::cancelMove);
board.initDefaultPositions();
boardComponent.repaint();
@@ -158,12 +155,12 @@ public class Game {
/**
* Stops the game by disconnecting its players from the UI.
*/
- public void stop() { players.values().forEach(Player::disconnect); }
+ public synchronized void stop() { players.values().forEach(Player::disconnect); }
/**
* Assigns the players their opposite colors.
*/
- public void swapColors() {
+ public synchronized void swapColors() {
players.values().forEach(Player::cancelMove);
Player white = players.get(Color.WHITE);
Player black = players.get(Color.BLACK);
diff --git a/src/dev/kske/chess/game/NaturalPlayer.java b/src/dev/kske/chess/game/NaturalPlayer.java
index 563b1a0..4ed1dc8 100644
--- a/src/dev/kske/chess/game/NaturalPlayer.java
+++ b/src/dev/kske/chess/game/NaturalPlayer.java
@@ -37,12 +37,13 @@ public class NaturalPlayer extends Player implements MouseListener {
/**
* Creates an instance of {@link NaturalPlayer}.
*
+ * @param game the game in which this player will be used
* @param color the piece color this player will control
* @param overlayComponent the overlay component that will be used to display
* possible moves to the user
*/
- public NaturalPlayer(Color color, OverlayComponent overlayComponent) {
- super(color);
+ public NaturalPlayer(Game game, Color color, OverlayComponent overlayComponent) {
+ super(game, color);
this.overlayComponent = overlayComponent;
name = "Player";
moveRequested = false;
diff --git a/src/dev/kske/chess/game/Player.java b/src/dev/kske/chess/game/Player.java
index 4c323b2..7a9f6de 100644
--- a/src/dev/kske/chess/game/Player.java
+++ b/src/dev/kske/chess/game/Player.java
@@ -17,17 +17,23 @@ import dev.kske.chess.board.Piece.Color;
*/
public abstract class Player {
- protected Game game;
- protected Board board;
- protected Color color;
+ protected final Game game;
+ protected final Board board;
+
protected String name;
+ protected Color color;
/**
* Initializes the color of this player.
*
+ * @param game the game in which this player will be used
* @param color the piece color that this player will control
*/
- public Player(Color color) { this.color = color; }
+ public Player(Game game, Color color) {
+ this.game = game;
+ board = game.getBoard();
+ this.color = color;
+ }
/**
* Initiates a move generation and reports the result to the game by calling
@@ -50,17 +56,6 @@ public abstract class Player {
*/
public Game getGame() { return game; }
- /**
- * Sets the game in which this player is used. The board of that game will be
- * assigned as well.
- *
- * @param game the game to set
- */
- public void setGame(Game game) {
- this.game = game;
- board = game.getBoard();
- }
-
/**
* @return the board on which this player is used
*/
diff --git a/src/dev/kske/chess/game/UCIPlayer.java b/src/dev/kske/chess/game/UCIPlayer.java
index 0a06645..d73cb75 100644
--- a/src/dev/kske/chess/game/UCIPlayer.java
+++ b/src/dev/kske/chess/game/UCIPlayer.java
@@ -27,11 +27,12 @@ public class UCIPlayer extends Player implements UCIListener {
/**
* Creates an instance of {@link UCIPlayer}.
*
+ * @param game the game in which this player will be used
* @param color the piece color that this player will control
* @param enginePath the path to the engine executable
*/
- public UCIPlayer(Color color, String enginePath) {
- super(color);
+ public UCIPlayer(Game game, Color color, String enginePath) {
+ super(game, color);
try {
handle = new UCIHandle(enginePath);
handle.registerListener(this);
diff --git a/src/dev/kske/chess/game/ai/AIPlayer.java b/src/dev/kske/chess/game/ai/AIPlayer.java
index ee68765..6dc030a 100644
--- a/src/dev/kske/chess/game/ai/AIPlayer.java
+++ b/src/dev/kske/chess/game/ai/AIPlayer.java
@@ -9,6 +9,7 @@ import javax.swing.SwingUtilities;
import dev.kske.chess.board.Board;
import dev.kske.chess.board.Move;
import dev.kske.chess.board.Piece.Color;
+import dev.kske.chess.game.Game;
import dev.kske.chess.game.Player;
/**
@@ -31,38 +32,34 @@ public class AIPlayer extends Player {
/**
* Creates an instance of {@link AIPlayer}.
*
+ * @param game the game in which this player will be used
* @param color the piece color this player will control
* @param maxDepth the maximum search depth
* @param alphaBetaThreshold the board evaluation threshold that has to be
* reached to continue searching the children of a
* move
*/
- public AIPlayer(Color color, int maxDepth, int alphaBetaThreshold) {
- super(color);
+ public AIPlayer(Game game, Color color, int maxDepth, int alphaBetaThreshold) {
+ super(game, color);
name = "AIPlayer";
availableProcessors = Runtime.getRuntime().availableProcessors();
this.maxDepth = maxDepth;
this.alphaBetaThreshold = alphaBetaThreshold;
- exitRequested = false;
}
@Override
public void requestMove() {
exitRequested = false;
- /*
- * Define some processing threads, split the available moves between them and
- * retrieve the result after their execution.
- */
+
+ // Define some processing threads, split the available moves between them and
+ // retrieve the result after their execution.
new Thread(() -> {
- /*
- * Get a copy of the board and the available moves.
- */
+
+ // Get a copy of the board and the available moves.
Board board = new Board(this.board, false);
List moves = board.getMoves(color);
- /*
- * Define move processors and split the available moves between them.
- */
+ // Define move processors and split the available moves between them.
int numThreads = Math.min(moves.size(), availableProcessors);
List processors = new ArrayList<>(numThreads);
final int step = moves.size() / numThreads;
@@ -71,23 +68,21 @@ public class AIPlayer extends Player {
for (int i = 0; i < numThreads; i++) {
if (rem-- > 0) ++endIndex;
endIndex += step;
- processors.add(new MoveProcessor(new Board(board, false), moves.subList(beginIndex, endIndex), color,
- maxDepth, alphaBetaThreshold));
+ processors.add(new MoveProcessor(new Board(board, false), moves.subList(beginIndex, endIndex), color, maxDepth, alphaBetaThreshold));
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
executor = Executors.newFixedThreadPool(numThreads);
List results = new ArrayList<>(numThreads);
try {
List> futures = executor.invokeAll(processors);
for (Future f : futures)
results.add(f.get());
- executor.shutdown();
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
+ } finally {
+ executor.shutdown();
}
results.sort((r1, r2) -> Integer.compare(r2.score, r1.score));
if (!exitRequested) SwingUtilities.invokeLater(() -> game.onMove(this, results.get(0).move));
diff --git a/src/dev/kske/chess/io/TextureUtil.java b/src/dev/kske/chess/io/TextureUtil.java
index 79d251b..d3240e5 100644
--- a/src/dev/kske/chess/io/TextureUtil.java
+++ b/src/dev/kske/chess/io/TextureUtil.java
@@ -15,7 +15,7 @@ import dev.kske.chess.board.Piece;
* Project: Chess
* File: TextureUtil.java
* Created: 01.07.2019
- *
+ *
* @since Chess v0.1-alpha
* @author Kai S. K. Engelbart
*/
@@ -32,18 +32,18 @@ public class TextureUtil {
/**
* Loads a piece texture fitting to a piece object.
- *
+ *
* @param piece The piece from which the texture properties are taken
* @return The fitting texture
*/
public static Image getPieceTexture(Piece piece) {
- String key = piece.toString().toLowerCase() + "_" + piece.getColor().toString().toLowerCase();
+ String key = piece.getClass().getSimpleName().toLowerCase() + "_" + piece.getColor().toString().toLowerCase();
return scaledTextures.get(key);
}
/**
* Scales all piece textures to fit the current tile size.
- *
+ *
* @param tileSize the new width and height of the piece textures
*/
public static void scalePieceTextures(int tileSize) {
@@ -53,7 +53,7 @@ public class TextureUtil {
/**
* Loads an image from a file in the resource folder.
- *
+ *
* @param fileName The name of the image resource
* @return The loaded image
*/
diff --git a/src/dev/kske/chess/ui/MainWindow.java b/src/dev/kske/chess/ui/MainWindow.java
index fee7aa4..86ae49f 100644
--- a/src/dev/kske/chess/ui/MainWindow.java
+++ b/src/dev/kske/chess/ui/MainWindow.java
@@ -1,7 +1,6 @@
package dev.kske.chess.ui;
import java.awt.Desktop;
-import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.dnd.DropTarget;
import java.io.File;
@@ -29,47 +28,31 @@ import dev.kske.chess.pgn.PGNGame;
*/
public class MainWindow extends JFrame {
- private static final long serialVersionUID = -3100939302567978977L;
+ private JTabbedPane tabbedPane = new JTabbedPane();
- private JTabbedPane tabbedPane;
+ private static final long serialVersionUID = -3100939302567978977L;
/**
* Launch the application.
*
* @param args command line arguments are ignored
*/
- public static void main(String[] args) {
- EventQueue.invokeLater(() -> {
- try {
- new MainWindow();
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- });
- }
+ public static void main(String[] args) { SwingUtilities.invokeLater(() -> new MainWindow()); }
/**
* Create the application.
*/
- public MainWindow() {
+ private MainWindow() {
super("Chess by Kai S. K. Engelbart");
- initialize();
- }
- /**
- * Initialize the contents of the frame.
- */
- private void initialize() {
// Configure frame
setResizable(false);
setBounds(100, 100, 494, 565);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("/pieces/queen_white.png")));
- // Add frame content
- tabbedPane = new JTabbedPane();
+ // Add tabbed pane, menu bar and drop target
getContentPane().add(tabbedPane);
-
setJMenuBar(new MenuBar(this));
new DropTarget(this, new GameDropTarget(this));