diff --git a/src/dev/kske/chess/game/Game.java b/src/dev/kske/chess/game/Game.java
index 09ba1dd..e4e0a5f 100644
--- a/src/dev/kske/chess/game/Game.java
+++ b/src/dev/kske/chess/game/Game.java
@@ -7,6 +7,8 @@ import dev.kske.chess.board.GameState;
import dev.kske.chess.board.Move;
import dev.kske.chess.board.Piece.Color;
import dev.kske.chess.ui.BoardComponent;
+import dev.kske.chess.ui.BoardPane;
+import dev.kske.chess.ui.OverlayComponent;
/**
* Project: Chess
@@ -18,11 +20,13 @@ public class Game {
private Map players;
private Board board;
+ private OverlayComponent overlayComponent;
private BoardComponent boardComponent;
- public Game(Map players, BoardComponent boardComponent) {
+ public Game(Map players, BoardPane boardPane) {
this.players = players;
- this.boardComponent = boardComponent;
+ this.overlayComponent = boardPane.getOverlayComponent();
+ this.boardComponent = boardPane.getBoardComponent();
this.board = boardComponent.getBoard();
// Initialize the game variable in each player
@@ -42,6 +46,7 @@ public class Game {
System.out.printf("%s in check!%n", player.color.opposite());
default:
boardComponent.repaint();
+ overlayComponent.displayArrow(move);
players.get(player.color.opposite()).requestMove();
}
@@ -56,6 +61,8 @@ public class Game {
players.forEach((k, v) -> v.cancelMove());
board.initializeDefaultPositions();
boardComponent.repaint();
+ overlayComponent.clearDots();
+ overlayComponent.clearArrow();
start();
}
}
diff --git a/src/dev/kske/chess/game/ai/AIPlayer.java b/src/dev/kske/chess/game/ai/AIPlayer.java
index 1373dbd..a17b6ef 100644
--- a/src/dev/kske/chess/game/ai/AIPlayer.java
+++ b/src/dev/kske/chess/game/ai/AIPlayer.java
@@ -27,15 +27,15 @@ public class AIPlayer extends Player {
private int maxDepth;
private int alphaBetaThreshold;
- private volatile boolean exitRequested;
+ private volatile boolean exitRequested;
private volatile ExecutorService executor;
public AIPlayer(Board board, Color color, int maxDepth, int alphaBetaThreshold) {
super(board, color);
- availableProcessors = Runtime.getRuntime().availableProcessors();
- this.maxDepth = maxDepth;
+ availableProcessors = Runtime.getRuntime().availableProcessors();
+ this.maxDepth = maxDepth;
this.alphaBetaThreshold = alphaBetaThreshold;
- exitRequested = false;
+ exitRequested = false;
}
@Override
@@ -63,9 +63,8 @@ public class AIPlayer extends Player {
for (int i = 0; i < numThreads; i++) {
if (rem-- > 0) ++endIndex;
endIndex += step;
- processors.add(
- new MoveProcessor((Board) board.clone(), moves.subList(beginIndex, endIndex), color, maxDepth,
- alphaBetaThreshold));
+ processors.add(new MoveProcessor((Board) board.clone(), moves.subList(beginIndex, endIndex), color,
+ maxDepth, alphaBetaThreshold));
beginIndex = endIndex;
}
@@ -73,7 +72,7 @@ public class AIPlayer extends Player {
* Execute processors, get the best result and pass it back to the Game class
*/
executor = Executors.newFixedThreadPool(numThreads);
- List results = new ArrayList<>(numThreads);
+ List results = new ArrayList<>(numThreads);
try {
List> futures = executor.invokeAll(processors);
for (Future f : futures)
@@ -90,11 +89,13 @@ public class AIPlayer extends Player {
@Override
public void cancelMove() {
exitRequested = true;
- executor.shutdownNow();
- try {
- executor.awaitTermination(500, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- e.printStackTrace();
+ if (executor != null) {
+ executor.shutdownNow();
+ try {
+ executor.awaitTermination(500, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
}
}
}
diff --git a/src/dev/kske/chess/ui/AIConfigDialog.java b/src/dev/kske/chess/ui/AIConfigDialog.java
index 85424bf..6b7ee8d 100644
--- a/src/dev/kske/chess/ui/AIConfigDialog.java
+++ b/src/dev/kske/chess/ui/AIConfigDialog.java
@@ -23,24 +23,24 @@ public class AIConfigDialog extends JDialog {
private boolean startGame = false;
public AIConfigDialog() {
- setSize(new Dimension(293, 212));
+ setSize(new Dimension(337, 212));
setModal(true);
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
setTitle("AI Configuration");
getContentPane().setLayout(null);
JSpinner spAlphaBetaThreshold = new JSpinner();
- spAlphaBetaThreshold.setBounds(170, 0, 95, 28);
+ spAlphaBetaThreshold.setBounds(222, 68, 95, 28);
getContentPane().add(spAlphaBetaThreshold);
spAlphaBetaThreshold.setModel(new SpinnerNumberModel(-10, -100, 100, 5));
JSpinner spMaxDepth = new JSpinner();
- spMaxDepth.setBounds(170, 68, 95, 28);
+ 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, 119, 28);
+ lblAlphabetaThreshold.setBounds(16, 68, 194, 28);
getContentPane().add(lblAlphabetaThreshold);
JButton btnOk = new JButton("OK");
@@ -55,13 +55,13 @@ public class AIConfigDialog extends JDialog {
btnOk.setToolTipText("Start the game");
JButton btnCancel = new JButton("Cancel");
- btnCancel.setBounds(170, 137, 95, 28);
+ 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, 6, 141, 16);
+ lblMaximalRecursionDepth.setBounds(16, 12, 194, 16);
getContentPane().add(lblMaximalRecursionDepth);
setLocationRelativeTo(null);
diff --git a/src/dev/kske/chess/ui/MenuBar.java b/src/dev/kske/chess/ui/MenuBar.java
index 334d1fc..f6ba11a 100644
--- a/src/dev/kske/chess/ui/MenuBar.java
+++ b/src/dev/kske/chess/ui/MenuBar.java
@@ -25,6 +25,7 @@ public class MenuBar extends JMenuBar {
private static final long serialVersionUID = -7221583703531248228L;
private final MainWindow mainWindow;
+ private final BoardPane boardPane;
private final OverlayComponent overlayComponent;
private final BoardComponent boardComponent;
private final Board board;
@@ -32,8 +33,9 @@ public class MenuBar extends JMenuBar {
public MenuBar(MainWindow mainWindow) {
this.mainWindow = mainWindow;
- overlayComponent = mainWindow.getBoardPane().getOverlayComponent();
- boardComponent = mainWindow.getBoardPane().getBoardComponent();
+ boardPane = mainWindow.getBoardPane();
+ overlayComponent = boardPane.getOverlayComponent();
+ boardComponent = boardPane.getBoardComponent();
board = boardComponent.getBoard();
players = new HashMap<>();
@@ -78,7 +80,8 @@ public class MenuBar extends JMenuBar {
}
private void startGame() {
- Game game = new Game(players, boardComponent);
+ // TODO: Re-init board and overlay component
+ Game game = new Game(players, boardPane);
mainWindow.setGame(game);
game.start();
}
diff --git a/src/dev/kske/chess/ui/OverlayComponent.java b/src/dev/kske/chess/ui/OverlayComponent.java
index 86557ad..4cc8015 100644
--- a/src/dev/kske/chess/ui/OverlayComponent.java
+++ b/src/dev/kske/chess/ui/OverlayComponent.java
@@ -2,11 +2,17 @@ package dev.kske.chess.ui;
import java.awt.Color;
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.List;
import javax.swing.JComponent;
+import dev.kske.chess.board.Move;
import dev.kske.chess.board.Position;
/**
@@ -22,11 +28,12 @@ public class OverlayComponent extends JComponent {
private final BoardPane boardPane;
private List dots;
+ private Move arrow;
public OverlayComponent(BoardPane boardPane) {
this.boardPane = boardPane;
setSize(boardPane.getPreferredSize());
- dots = new ArrayList<>();
+ dots = new ArrayList<>();
}
@Override
@@ -34,7 +41,7 @@ public class OverlayComponent extends JComponent {
super.paintComponent(g);
final int tileSize = getTileSize();
-
+
// Draw possible moves if a piece was selected
if (!dots.isEmpty()) {
g.setColor(Color.green);
@@ -45,6 +52,43 @@ public class OverlayComponent extends JComponent {
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 dots) {
@@ -58,5 +102,15 @@ public class OverlayComponent extends JComponent {
repaint();
}
+ public void displayArrow(Move arrow) {
+ this.arrow = arrow;
+ repaint();
+ }
+
+ public void clearArrow() {
+ arrow = null;
+ repaint();
+ }
+
public int getTileSize() { return boardPane.getTileSize(); }
}