Added tool panel and event system for check notification

+ GameEvent and GameEventListener
+ Check detection and notification in Board and BoardPanel
+ Tool panel in Chess with a restart button
This commit is contained in:
Kai S. K. Engelbart 2019-07-03 11:05:20 +02:00
parent 4b274e3d0f
commit 30074f385e
Signed by: kske
GPG Key ID: 8BEB13EC5DF7EF13
5 changed files with 117 additions and 9 deletions

View File

@ -1,8 +1,13 @@
package dev.kske.chess; package dev.kske.chess;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import dev.kske.chess.event.GameEvent;
import dev.kske.chess.event.GameEvent.GameEventType;
import dev.kske.chess.event.GameEventListener;
import dev.kske.chess.piece.Bishop; import dev.kske.chess.piece.Bishop;
import dev.kske.chess.piece.King; import dev.kske.chess.piece.King;
import dev.kske.chess.piece.Knight; import dev.kske.chess.piece.Knight;
@ -24,9 +29,12 @@ public class Board {
private Piece[][] boardArr; private Piece[][] boardArr;
private Map<Color, Position> kingPos; private Map<Color, Position> kingPos;
private List<GameEventListener> gameEventListeners;
public Board() { public Board() {
boardArr = new Piece[8][8]; boardArr = new Piece[8][8];
kingPos = new HashMap<>(); kingPos = new HashMap<>();
gameEventListeners = new ArrayList<>();
initializeDefaultPositions(); initializeDefaultPositions();
} }
@ -52,6 +60,13 @@ public class Board {
revert(move, capturePiece); revert(move, capturePiece);
return false; return false;
} }
// TODO: detecting checkmate
// Check for check on the opposite team
Color oppositeColor = piece.getColor() == Color.WHITE ? Color.BLACK : Color.WHITE;
if (checkCheck(oppositeColor))
notifyListeners(new GameEvent(this, GameEventType.CHECK, oppositeColor));
return true; return true;
} }
} }
@ -80,6 +95,14 @@ public class Board {
return false; return false;
} }
public void registerGameEventListener(GameEventListener listener) {
gameEventListeners.add(listener);
}
private void notifyListeners(GameEvent evt) {
gameEventListeners.forEach(listener -> listener.onGameEvent(evt));
}
public Piece get(Position pos) { public Piece get(Position pos) {
return boardArr[pos.x][pos.y]; return boardArr[pos.x][pos.y];
} }
@ -107,7 +130,7 @@ public class Board {
/** /**
* Initialized the board array with the default chess pieces and positions. * Initialized the board array with the default chess pieces and positions.
*/ */
private void initializeDefaultPositions() { public void initializeDefaultPositions() {
// Initialize pawns // Initialize pawns
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
boardArr[i][1] = new Pawn(Color.BLACK, this); boardArr[i][1] = new Pawn(Color.BLACK, this);
@ -143,6 +166,11 @@ public class Board {
boardArr[2][7] = new Bishop(Color.WHITE, this); boardArr[2][7] = new Bishop(Color.WHITE, this);
boardArr[5][0] = new Bishop(Color.BLACK, this); boardArr[5][0] = new Bishop(Color.BLACK, this);
boardArr[5][7] = new Bishop(Color.WHITE, this); boardArr[5][7] = new Bishop(Color.WHITE, this);
// Clear all other tiles
for (int i = 0; i < 8; i++)
for (int j = 2; j < 6; j++)
boardArr[i][j] = null;
} }
/** /**

View File

@ -13,8 +13,11 @@ import java.io.File;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.swing.JOptionPane;
import javax.swing.JPanel; import javax.swing.JPanel;
import dev.kske.chess.event.GameEvent;
import dev.kske.chess.event.GameEventListener;
import dev.kske.chess.piece.Piece; import dev.kske.chess.piece.Piece;
/** /**
@ -27,7 +30,7 @@ import dev.kske.chess.piece.Piece;
* this must be added to a parent component that allows the child to decide the * this must be added to a parent component that allows the child to decide the
* size. * size.
*/ */
public class BoardPanel extends JPanel { public class BoardPanel extends JPanel implements GameEventListener {
private static final long serialVersionUID = 6771148331334310216L; private static final long serialVersionUID = 6771148331334310216L;
@ -37,14 +40,11 @@ public class BoardPanel extends JPanel {
private Board board; private Board board;
static {
loadPieceTextures();
}
public BoardPanel(Board board) { public BoardPanel(Board board) {
this(); this();
setSize(getPreferredSize()); setSize(getPreferredSize());
setBoard(board); setBoard(board);
if (textures == null) loadPieceTextures();
} }
public BoardPanel() { public BoardPanel() {
@ -99,6 +99,23 @@ public class BoardPanel extends JPanel {
g.drawImage(getPieceTexture(board.getBoardArr()[i][j]), i * tileSize, j * tileSize, this); g.drawImage(getPieceTexture(board.getBoardArr()[i][j]), i * tileSize, j * tileSize, this);
} }
@Override
public void onGameEvent(GameEvent evt) {
switch (evt.getEventType()) {
case CHECK:
JOptionPane.showMessageDialog(this, evt.getColor().toString() + " in check!");
break;
}
}
/**
* Reverts the board to its initial state
*/
public void reset() {
board.initializeDefaultPositions();
repaint();
}
/** /**
* Load every PNG file inside the res/pieces directory. * Load every PNG file inside the res/pieces directory.
* The filenames without extensions are used as keys in the map textures. * The filenames without extensions are used as keys in the map textures.
@ -140,5 +157,10 @@ public class BoardPanel extends JPanel {
public Board getBoard() { return board; } public Board getBoard() { return board; }
public void setBoard(Board board) { this.board = board; } public void setBoard(Board board) {
this.board = board;
// Register this BoardPanel as a GameEventListener to the board
board.registerGameEventListener(this);
}
} }

View File

@ -3,7 +3,9 @@ package dev.kske.chess;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.EventQueue; import java.awt.EventQueue;
import javax.swing.JButton;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JPanel;
/** /**
@ -46,13 +48,19 @@ public class Chess {
private void initialize() { private void initialize() {
mframe = new JFrame(); mframe = new JFrame();
mframe.setResizable(false); mframe.setResizable(false);
mframe.setBounds(100, 100, 729, 737); mframe.setBounds(100, 100, 494, 565);
mframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); mframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BoardPanel boardPanel = new BoardPanel(new Board()); BoardPanel boardPanel = new BoardPanel(new Board());
boardPanel.setLayout(null); boardPanel.setLayout(null);
mframe.getContentPane().add(boardPanel, BorderLayout.CENTER); mframe.getContentPane().add(boardPanel, BorderLayout.CENTER);
JPanel toolPanel = new JPanel();
mframe.getContentPane().add(toolPanel, BorderLayout.NORTH);
JButton btnRestart = new JButton("Restart");
btnRestart.addActionListener((evt) -> boardPanel.reset());
toolPanel.add(btnRestart);
mframe.pack(); mframe.pack();
} }
} }

View File

@ -0,0 +1,38 @@
package dev.kske.chess.event;
import java.util.EventObject;
import dev.kske.chess.Board;
import dev.kske.chess.piece.Piece.Color;
/**
* Project: <strong>Chess</strong><br>
* File: <strong>GameEvent.java</strong><br>
* Created: <strong>03.07.2019</strong><br>
* Author: <strong>Kai S. K. Engelbart</strong>
*/
public class GameEvent extends EventObject {
private static final long serialVersionUID = -6783035746521826589L;
private final Board source;
private final GameEventType eventType;
private final Color color;
public GameEvent(Board source, GameEventType eventType, Color color) {
super(source);
this.source = source;
this.eventType = eventType;
this.color = color;
}
public Board getSource() { return source; }
public GameEventType getEventType() { return eventType; }
public Color getColor() { return color; }
public static enum GameEventType {
CHECK, CHECKMATE
}
}

View File

@ -0,0 +1,12 @@
package dev.kske.chess.event;
/**
* Project: <strong>Chess</strong><br>
* File: <strong>GameEventListener.java</strong><br>
* Created: <strong>03.07.2019</strong><br>
* Author: <strong>Kai S. K. Engelbart</strong>
*/
public interface GameEventListener {
void onGameEvent(GameEvent evt);
}