Improved BoardOverlay, disabled color swap in natural-vs-natural game

This commit is contained in:
Kai S. K. Engelbart 2019-07-23 10:38:19 +02:00
parent 0e75aae421
commit 4e008954ad
Signed by: kske
GPG Key ID: 8BEB13EC5DF7EF13
4 changed files with 76 additions and 56 deletions

View File

@ -20,16 +20,16 @@ import dev.kske.chess.ui.OverlayComponent;
*/ */
public class Game { public class Game {
private Map<Color, Player> players; private Map<Color, Player> players;
private Board board; private Board board;
private OverlayComponent overlayComponent; private OverlayComponent overlayComponent;
private BoardComponent boardComponent; private BoardComponent boardComponent;
public Game(Map<Color, Player> players, BoardPane boardPane) { public Game(Map<Color, Player> players, BoardPane boardPane) {
this.players = players; this.players = players;
this.overlayComponent = boardPane.getOverlayComponent(); this.overlayComponent = boardPane.getOverlayComponent();
this.boardComponent = boardPane.getBoardComponent(); this.boardComponent = boardPane.getBoardComponent();
this.board = new Board(); this.board = new Board();
boardComponent.setBoard(board); boardComponent.setBoard(board);
// Initialize the game variable in each player // Initialize the game variable in each player
@ -37,8 +37,8 @@ public class Game {
} }
public static Game createNatural(BoardPane boardPane) { public static Game createNatural(BoardPane boardPane) {
Map<Color, Player> players = new HashMap<>(); Map<Color, Player> players = new HashMap<>();
OverlayComponent overlay = boardPane.getOverlayComponent(); OverlayComponent overlay = boardPane.getOverlayComponent();
players.put(Color.WHITE, new NaturalPlayer(Color.WHITE, overlay)); players.put(Color.WHITE, new NaturalPlayer(Color.WHITE, overlay));
players.put(Color.BLACK, new NaturalPlayer(Color.BLACK, overlay)); players.put(Color.BLACK, new NaturalPlayer(Color.BLACK, overlay));
@ -46,8 +46,8 @@ public class Game {
} }
public static Game createNaturalVsAI(BoardPane boardPane, int maxDepth, int alphaBeta) { public static Game createNaturalVsAI(BoardPane boardPane, int maxDepth, int alphaBeta) {
Map<Color, Player> players = new HashMap<>(); Map<Color, Player> players = new HashMap<>();
OverlayComponent overlay = boardPane.getOverlayComponent(); OverlayComponent overlay = boardPane.getOverlayComponent();
players.put(Color.WHITE, new NaturalPlayer(Color.WHITE, overlay)); players.put(Color.WHITE, new NaturalPlayer(Color.WHITE, overlay));
players.put(Color.BLACK, new AIPlayer(Color.BLACK, maxDepth, alphaBeta)); players.put(Color.BLACK, new AIPlayer(Color.BLACK, maxDepth, alphaBeta));
@ -76,18 +76,19 @@ public class Game {
System.out.println("FEN: " + board.toFEN()); System.out.println("FEN: " + board.toFEN());
GameState eventType = board.getGameEventType(board.getDest(move).getColor().opposite()); GameState eventType = board.getGameEventType(board.getDest(move).getColor().opposite());
switch (eventType) { switch (eventType) {
case CHECKMATE: case CHECKMATE:
case STALEMATE: case STALEMATE:
System.out.printf("%s in %s!%n", player.color.opposite(), eventType); System.out.printf("%s in %s!%n", player.color.opposite(), eventType);
break; break;
case CHECK: case CHECK:
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();
players.get(board.getActiveColor()).requestMove(); players.get(board.getActiveColor()).requestMove();
} }
overlayComponent.displayArrow(move); overlayComponent.displayArrow(move);
} else player.requestMove(); } else
player.requestMove();
} }
public void start() { public void start() {
@ -108,7 +109,7 @@ public class Game {
public void disconnect() { public void disconnect() {
players.values().forEach(Player::disconnect); players.values().forEach(Player::disconnect);
} }
public void swapColors() { public void swapColors() {
players.values().forEach(Player::cancelMove); players.values().forEach(Player::cancelMove);
Player white = players.get(Color.WHITE); Player white = players.get(Color.WHITE);
@ -121,4 +122,6 @@ public class Game {
} }
public Board getBoard() { return board; } public Board getBoard() { return board; }
public Map<Color, Player> getPlayers() { return players; }
} }

View File

@ -10,6 +10,7 @@ import javax.swing.JPanel;
import dev.kske.chess.board.Piece.Color; import dev.kske.chess.board.Piece.Color;
import dev.kske.chess.game.Game; import dev.kske.chess.game.Game;
import dev.kske.chess.game.NaturalPlayer;
/** /**
* Project: <strong>Chess</strong><br> * Project: <strong>Chess</strong><br>
@ -20,6 +21,7 @@ import dev.kske.chess.game.Game;
public class MainWindow { public class MainWindow {
private JFrame mframe; private JFrame mframe;
private JButton btnRestart, btnSwapColors;
private BoardPane boardPane; private BoardPane boardPane;
private Game game; private Game game;
private Color activeColor; private Color activeColor;
@ -53,7 +55,7 @@ public class MainWindow {
*/ */
private void initialize() { private void initialize() {
mframe = new JFrame(); mframe = new JFrame();
mframe.setResizable(false); mframe.setResizable(true);
mframe.setBounds(100, 100, 494, 565); mframe.setBounds(100, 100, 494, 565);
mframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); mframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
@ -62,12 +64,10 @@ public class MainWindow {
boardPane = new BoardPane(); 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"); btnRestart = new JButton("Restart");
btnRestart.addActionListener((evt) -> { btnRestart.addActionListener((evt) -> {
if (game != null) if (game != null)
game.reset(); game.reset();
@ -75,7 +75,7 @@ public class MainWindow {
}); });
activeColor = Color.WHITE; activeColor = Color.WHITE;
JButton btnSwapColors = new JButton("Play as black"); btnSwapColors = new JButton("Play as black");
btnSwapColors.addActionListener((evt) -> { btnSwapColors.addActionListener((evt) -> {
game.swapColors(); game.swapColors();
btnSwapColors.setText("Play as " + activeColor.toString().toLowerCase()); btnSwapColors.setText("Play as " + activeColor.toString().toLowerCase());
@ -85,6 +85,8 @@ public class MainWindow {
toolPanel.add(btnRestart); toolPanel.add(btnRestart);
toolPanel.add(btnSwapColors); toolPanel.add(btnSwapColors);
mframe.setJMenuBar(new MenuBar(this));
mframe.pack(); mframe.pack();
mframe.setLocationRelativeTo(null); mframe.setLocationRelativeTo(null);
} }
@ -101,5 +103,7 @@ public class MainWindow {
if (this.game != null) if (this.game != null)
this.game.disconnect(); this.game.disconnect();
this.game = game; this.game = game;
btnSwapColors.setEnabled(!(game.getPlayers().get(Color.WHITE) instanceof NaturalPlayer
&& game.getPlayers().get(Color.BLACK) instanceof NaturalPlayer));
} }
} }

View File

@ -17,12 +17,12 @@ 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 BoardPane boardPane;
public MenuBar(MainWindow mainWindow) { public MenuBar(MainWindow mainWindow) {
this.mainWindow = mainWindow; this.mainWindow = mainWindow;
boardPane = mainWindow.getBoardPane(); boardPane = mainWindow.getBoardPane();
initGameMenu(); initGameMenu();
} }
@ -30,11 +30,11 @@ public class MenuBar extends JMenuBar {
private void initGameMenu() { private void initGameMenu() {
JMenu gameMenu = new JMenu("Game"); JMenu gameMenu = new JMenu("Game");
JMenuItem naturalMenuItem = new JMenuItem("Game against natural opponent"); JMenuItem naturalMenuItem = new JMenuItem("Game against natural opponent");
JMenuItem aiMenuItem = new JMenuItem("Game against artificial opponent"); JMenuItem aiMenuItem = new JMenuItem("Game against artificial opponent");
JMenuItem aiVsAiMenuItem = new JMenuItem("Watch AI vs. AI"); JMenuItem aiVsAiMenuItem = new JMenuItem("Watch AI vs. AI");
JMenuItem uciMenuItem = new JMenuItem("UCI"); JMenuItem uciMenuItem = new JMenuItem("UCI");
naturalMenuItem.addActionListener((evt) -> startGame(Game.createNatural(boardPane))); naturalMenuItem.addActionListener((evt) -> startGame(Game.createNatural(boardPane)));
aiMenuItem.addActionListener((evt) -> { aiMenuItem.addActionListener((evt) -> {
@ -48,17 +48,16 @@ public class MenuBar extends JMenuBar {
uciMenuItem.addActionListener((evt) -> { uciMenuItem.addActionListener((evt) -> {
String enginePath = JOptionPane.showInputDialog(getParent(), String enginePath = JOptionPane.showInputDialog(getParent(),
"Enter the path to a UCI-compatible chess engine:", "Enter the path to a UCI-compatible chess engine:", "Engine selection",
"Engine selection",
JOptionPane.QUESTION_MESSAGE); JOptionPane.QUESTION_MESSAGE);
if (enginePath != null) startGame(Game.createUCI(boardPane, enginePath)); if (enginePath != null)
startGame(Game.createUCI(boardPane, enginePath));
}); });
gameMenu.add(naturalMenuItem); gameMenu.add(naturalMenuItem);
gameMenu.add(aiMenuItem); gameMenu.add(aiMenuItem);
gameMenu.add(aiVsAiMenuItem); gameMenu.add(aiVsAiMenuItem);
gameMenu.add(uciMenuItem); gameMenu.add(uciMenuItem);
add(gameMenu); add(gameMenu);

View File

@ -1,5 +1,6 @@
package dev.kske.chess.ui; package dev.kske.chess.ui;
import java.awt.BasicStroke;
import java.awt.Color; import java.awt.Color;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
@ -27,8 +28,8 @@ public class OverlayComponent extends JComponent {
private final BoardPane boardPane; private final BoardPane boardPane;
private List<Position> dots; private List<Position> dots;
private Move arrow; private Move arrow;
public OverlayComponent(BoardPane boardPane) { public OverlayComponent(BoardPane boardPane) {
this.boardPane = boardPane; this.boardPane = boardPane;
@ -47,18 +48,29 @@ public class OverlayComponent extends JComponent {
g.setColor(Color.green); g.setColor(Color.green);
int radius = tileSize / 4; int radius = tileSize / 4;
for (Position dot : dots) for (Position dot : dots)
g.fillOval(dot.x * tileSize + tileSize / 2 - radius / 2, g.fillOval(dot.x * tileSize + tileSize / 2 - radius / 2, dot.y * tileSize + tileSize / 2 - radius / 2,
dot.y * tileSize + tileSize / 2 - radius / 2, radius, radius);
radius,
radius);
} }
// Draw an arrow representing the last move and mark its position and destination
if (arrow != null) { if (arrow != null) {
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 g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(3));
g2d.setColor(Color.yellow);
g2d.drawRect(arrow.pos.x * tileSize, arrow.pos.y * tileSize, tileSize, tileSize);
g2d.drawRect(arrow.dest.x * tileSize, arrow.dest.y * tileSize, tileSize, tileSize);
Shape arrowShape = createArrowShape(pos, dest);
g.setColor(new Color(255, 0, 0, 127)); g.setColor(new Color(255, 0, 0, 127));
Point pos = new Point(arrow.pos.x * tileSize + tileSize / 2, arrow.pos.y * tileSize + tileSize / 2); g2d.fill(arrowShape);
Point dest = new Point(arrow.dest.x * tileSize + tileSize / 2, arrow.dest.y * tileSize + tileSize / 2); g2d.setColor(Color.black);
((Graphics2D) g).fill(createArrowShape(pos, dest)); g2d.draw(arrowShape);
} }
} }
private Shape createArrowShape(Point pos, Point dest) { private Shape createArrowShape(Point pos, Point dest) {
@ -73,10 +85,10 @@ public class OverlayComponent extends JComponent {
Point midPoint = midpoint(pos, dest); Point midPoint = midpoint(pos, dest);
double rotate = Math.atan2(dest.y - pos.y, dest.x - pos.x); double rotate = Math.atan2(dest.y - pos.y, dest.x - pos.x);
double ptDistance = pos.distance(dest); double ptDistance = pos.distance(dest);
double scale = ptDistance / 12.0; // 12 because it's the length of the arrow double scale = ptDistance / 12.0; // 12 because it's the length of the arrow
// polygon. // polygon.
AffineTransform transform = new AffineTransform(); AffineTransform transform = new AffineTransform();
@ -112,5 +124,7 @@ public class OverlayComponent extends JComponent {
repaint(); repaint();
} }
public int getTileSize() { return boardPane.getTileSize(); } public int getTileSize() {
return boardPane.getTileSize();
}
} }