From ef8c43be1f3edfcc9d3664c6b2cbf3e278b0f18d Mon Sep 17 00:00:00 2001 From: kske Date: Tue, 16 Jul 2019 15:32:02 +0200 Subject: [PATCH] Fixed UI bugs, added move drawing to OverlayComponent --- src/dev/kske/chess/game/Game.java | 11 +++- src/dev/kske/chess/game/ai/AIPlayer.java | 27 +++++----- src/dev/kske/chess/ui/AIConfigDialog.java | 12 ++--- src/dev/kske/chess/ui/MenuBar.java | 9 ++-- src/dev/kske/chess/ui/OverlayComponent.java | 58 ++++++++++++++++++++- 5 files changed, 91 insertions(+), 26 deletions(-) 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(); } }