Fixed UI bugs, added move drawing to OverlayComponent

This commit is contained in:
Kai S. K. Engelbart 2019-07-16 15:32:02 +02:00
parent 8ea0c7a603
commit cde7f63996
5 changed files with 91 additions and 26 deletions

View File

@ -7,6 +7,8 @@ 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.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,11 +20,13 @@ 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.boardComponent = boardPane.getBoardComponent();
this.board = boardComponent.getBoard(); this.board = boardComponent.getBoard();
// Initialize the game variable in each player // Initialize the game variable in each player
@ -42,6 +46,7 @@ public class Game {
System.out.printf("%s in check!%n", player.color.opposite()); System.out.printf("%s in check!%n", player.color.opposite());
default: default:
boardComponent.repaint(); boardComponent.repaint();
overlayComponent.displayArrow(move);
players.get(player.color.opposite()).requestMove(); players.get(player.color.opposite()).requestMove();
} }
@ -56,6 +61,8 @@ public class Game {
players.forEach((k, v) -> v.cancelMove()); players.forEach((k, v) -> v.cancelMove());
board.initializeDefaultPositions(); board.initializeDefaultPositions();
boardComponent.repaint(); boardComponent.repaint();
overlayComponent.clearDots();
overlayComponent.clearArrow();
start(); start();
} }
} }

View File

@ -27,15 +27,15 @@ public class AIPlayer extends Player {
private int maxDepth; private int maxDepth;
private int alphaBetaThreshold; private int alphaBetaThreshold;
private volatile boolean exitRequested; private volatile boolean exitRequested;
private volatile ExecutorService executor; private volatile ExecutorService executor;
public AIPlayer(Board board, Color color, int maxDepth, int alphaBetaThreshold) { public AIPlayer(Board board, Color color, int maxDepth, int alphaBetaThreshold) {
super(board, color); super(board, color);
availableProcessors = Runtime.getRuntime().availableProcessors(); availableProcessors = Runtime.getRuntime().availableProcessors();
this.maxDepth = maxDepth; this.maxDepth = maxDepth;
this.alphaBetaThreshold = alphaBetaThreshold; this.alphaBetaThreshold = alphaBetaThreshold;
exitRequested = false; exitRequested = false;
} }
@Override @Override
@ -63,9 +63,8 @@ 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));
alphaBetaThreshold));
beginIndex = endIndex; 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 * Execute processors, get the best result and pass it back to the Game class
*/ */
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);
for (Future<ProcessingResult> f : futures) for (Future<ProcessingResult> f : futures)
@ -90,11 +89,13 @@ public class AIPlayer extends Player {
@Override @Override
public void cancelMove() { public void cancelMove() {
exitRequested = true; exitRequested = true;
executor.shutdownNow(); if (executor != null) {
try { executor.shutdownNow();
executor.awaitTermination(500, TimeUnit.MILLISECONDS); try {
} catch (InterruptedException e) { executor.awaitTermination(500, TimeUnit.MILLISECONDS);
e.printStackTrace(); } catch (InterruptedException e) {
e.printStackTrace();
}
} }
} }
} }

View File

@ -23,24 +23,24 @@ public class AIConfigDialog extends JDialog {
private boolean startGame = false; private boolean startGame = false;
public AIConfigDialog() { public AIConfigDialog() {
setSize(new Dimension(293, 212)); setSize(new Dimension(337, 212));
setModal(true); setModal(true);
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
setTitle("AI Configuration"); setTitle("AI Configuration");
getContentPane().setLayout(null); getContentPane().setLayout(null);
JSpinner spAlphaBetaThreshold = new JSpinner(); JSpinner spAlphaBetaThreshold = new JSpinner();
spAlphaBetaThreshold.setBounds(170, 0, 95, 28); spAlphaBetaThreshold.setBounds(222, 68, 95, 28);
getContentPane().add(spAlphaBetaThreshold); getContentPane().add(spAlphaBetaThreshold);
spAlphaBetaThreshold.setModel(new SpinnerNumberModel(-10, -100, 100, 5)); spAlphaBetaThreshold.setModel(new SpinnerNumberModel(-10, -100, 100, 5));
JSpinner spMaxDepth = new JSpinner(); JSpinner spMaxDepth = new JSpinner();
spMaxDepth.setBounds(170, 68, 95, 28); spMaxDepth.setBounds(222, 6, 95, 28);
getContentPane().add(spMaxDepth); getContentPane().add(spMaxDepth);
spMaxDepth.setModel(new SpinnerNumberModel(4, 1, 10, 1)); spMaxDepth.setModel(new SpinnerNumberModel(4, 1, 10, 1));
JLabel lblAlphabetaThreshold = new JLabel("Alpha-Beta Threshold:"); JLabel lblAlphabetaThreshold = new JLabel("Alpha-Beta Threshold:");
lblAlphabetaThreshold.setBounds(16, 68, 119, 28); lblAlphabetaThreshold.setBounds(16, 68, 194, 28);
getContentPane().add(lblAlphabetaThreshold); getContentPane().add(lblAlphabetaThreshold);
JButton btnOk = new JButton("OK"); JButton btnOk = new JButton("OK");
@ -55,13 +55,13 @@ public class AIConfigDialog extends JDialog {
btnOk.setToolTipText("Start the game"); btnOk.setToolTipText("Start the game");
JButton btnCancel = new JButton("Cancel"); JButton btnCancel = new JButton("Cancel");
btnCancel.setBounds(170, 137, 95, 28); btnCancel.setBounds(222, 137, 95, 28);
getContentPane().add(btnCancel); getContentPane().add(btnCancel);
btnCancel.addActionListener((evt) -> dispose()); btnCancel.addActionListener((evt) -> dispose());
btnCancel.setToolTipText("Cancel the game start"); btnCancel.setToolTipText("Cancel the game start");
JLabel lblMaximalRecursionDepth = new JLabel("Maximal Recursion Depth:"); JLabel lblMaximalRecursionDepth = new JLabel("Maximal Recursion Depth:");
lblMaximalRecursionDepth.setBounds(16, 6, 141, 16); lblMaximalRecursionDepth.setBounds(16, 12, 194, 16);
getContentPane().add(lblMaximalRecursionDepth); getContentPane().add(lblMaximalRecursionDepth);
setLocationRelativeTo(null); setLocationRelativeTo(null);

View File

@ -25,6 +25,7 @@ public class MenuBar extends JMenuBar {
private static final long serialVersionUID = -7221583703531248228L; private static final long serialVersionUID = -7221583703531248228L;
private final MainWindow mainWindow; private final MainWindow mainWindow;
private final BoardPane boardPane;
private final OverlayComponent overlayComponent; private final OverlayComponent overlayComponent;
private final BoardComponent boardComponent; private final BoardComponent boardComponent;
private final Board board; private final Board board;
@ -32,8 +33,9 @@ public class MenuBar extends JMenuBar {
public MenuBar(MainWindow mainWindow) { public MenuBar(MainWindow mainWindow) {
this.mainWindow = mainWindow; this.mainWindow = mainWindow;
overlayComponent = mainWindow.getBoardPane().getOverlayComponent(); boardPane = mainWindow.getBoardPane();
boardComponent = mainWindow.getBoardPane().getBoardComponent(); overlayComponent = boardPane.getOverlayComponent();
boardComponent = boardPane.getBoardComponent();
board = boardComponent.getBoard(); board = boardComponent.getBoard();
players = new HashMap<>(); players = new HashMap<>();
@ -78,7 +80,8 @@ public class MenuBar extends JMenuBar {
} }
private void startGame() { 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); mainWindow.setGame(game);
game.start(); game.start();
} }

View File

@ -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,11 +28,12 @@ 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;
setSize(boardPane.getPreferredSize()); setSize(boardPane.getPreferredSize());
dots = new ArrayList<>(); dots = new ArrayList<>();
} }
@Override @Override
@ -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(); }
} }