Fixed checkmate detection, simplified event handling
This commit is contained in:
parent
f4b399ae43
commit
31c393150b
@ -7,9 +7,6 @@ import java.util.Map;
|
|||||||
|
|
||||||
import dev.kske.chess.board.Piece.Color;
|
import dev.kske.chess.board.Piece.Color;
|
||||||
import dev.kske.chess.board.Piece.Type;
|
import dev.kske.chess.board.Piece.Type;
|
||||||
import dev.kske.chess.event.GameEvent;
|
|
||||||
import dev.kske.chess.event.GameEvent.GameEventType;
|
|
||||||
import dev.kske.chess.event.GameEventListener;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Project: <strong>Chess</strong><br>
|
* Project: <strong>Chess</strong><br>
|
||||||
@ -22,12 +19,9 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,11 +47,6 @@ public class Board {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect check and stalemate on the opposite team
|
|
||||||
Color oppositeColor = piece.getColor().opposite();
|
|
||||||
GameEventType eventType = getGameEventType(oppositeColor);
|
|
||||||
if (eventType != GameEventType.NONE) notifyListeners(new GameEvent(this, eventType, oppositeColor));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,6 +103,12 @@ public class Board {
|
|||||||
return get(pos).getMoves(pos);
|
return get(pos).getMoves(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks, if the king is in check.
|
||||||
|
*
|
||||||
|
* @param color The color of the king to check
|
||||||
|
* @return {@code true}, if the king is in check
|
||||||
|
*/
|
||||||
public boolean checkCheck(Color color) {
|
public boolean checkCheck(Color color) {
|
||||||
for (int i = 0; i < 8; i++)
|
for (int i = 0; i < 8; i++)
|
||||||
for (int j = 0; j < 8; j++) {
|
for (int j = 0; j < 8; j++) {
|
||||||
@ -125,10 +120,31 @@ public class Board {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameEventType getGameEventType(Color color) {
|
/**
|
||||||
|
* Checks, if the king is in checkmate.
|
||||||
|
* This requires the king to already be in check!
|
||||||
|
*
|
||||||
|
* @param color The color of the king to check
|
||||||
|
* @return {@code true}, if the king is in checkmate
|
||||||
|
*/
|
||||||
|
public boolean checkCheckmate(Color color) {
|
||||||
|
// Return false immediately if the king can move
|
||||||
|
if (!getMoves(kingPos.get(color)).isEmpty()) return false;
|
||||||
|
else {
|
||||||
|
for (Move move : getMoves(color)) {
|
||||||
|
Piece capturePiece = move(move);
|
||||||
|
boolean check = checkCheck(color);
|
||||||
|
revert(move, capturePiece);
|
||||||
|
if (!check) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameState getGameEventType(Color color) {
|
||||||
return checkCheck(color)
|
return checkCheck(color)
|
||||||
? getMoves(kingPos.get(color)).isEmpty() ? GameEventType.CHECKMATE : GameEventType.CHECK
|
? checkCheckmate(color) ? GameState.CHECKMATE : GameState.CHECK
|
||||||
: getMoves(color).isEmpty() ? GameEventType.STALEMATE : GameEventType.NONE;
|
: getMoves(color).isEmpty() ? GameState.STALEMATE : GameState.NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -161,14 +177,6 @@ public class Board {
|
|||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerGameEventListener(GameEventListener listener) {
|
|
||||||
gameEventListeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyListeners(GameEvent evt) {
|
|
||||||
gameEventListeners.forEach(listener -> listener.onGameEvent(evt));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialized the board array with the default chess pieces and positions.
|
* Initialized the board array with the default chess pieces and positions.
|
||||||
*/
|
*/
|
||||||
|
11
src/dev/kske/chess/board/GameState.java
Normal file
11
src/dev/kske/chess/board/GameState.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package dev.kske.chess.board;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project: <strong>Chess</strong><br>
|
||||||
|
* File: <strong>GameState.java</strong><br>
|
||||||
|
* Created: <strong>07.07.2019</strong><br>
|
||||||
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
|
*/
|
||||||
|
public enum GameState {
|
||||||
|
CHECK, CHECKMATE, STALEMATE, NORMAL;
|
||||||
|
}
|
@ -1,38 +0,0 @@
|
|||||||
package dev.kske.chess.event;
|
|
||||||
|
|
||||||
import java.util.EventObject;
|
|
||||||
|
|
||||||
import dev.kske.chess.board.Board;
|
|
||||||
import dev.kske.chess.board.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, STALEMATE, NONE;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
@ -14,6 +14,7 @@ import dev.kske.chess.board.Piece.Color;
|
|||||||
public class AIPlayer extends Player {
|
public class AIPlayer extends Player {
|
||||||
|
|
||||||
private Move bestMove;
|
private Move bestMove;
|
||||||
|
private int count;
|
||||||
|
|
||||||
public AIPlayer(Board board, Color color) {
|
public AIPlayer(Board board, Color color) {
|
||||||
super(board, color);
|
super(board, color);
|
||||||
@ -21,19 +22,22 @@ public class AIPlayer extends Player {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void requestMove() {
|
public void requestMove() {
|
||||||
|
count = 0;
|
||||||
findBestMove(board, color, 0);
|
findBestMove(board, color, 0);
|
||||||
|
System.out.println("Moved processes: " + count);
|
||||||
game.onMove(this, bestMove);
|
game.onMove(this, bestMove);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int findBestMove(Board board, Color color, int depth) {
|
private int findBestMove(Board board, Color color, int depth) {
|
||||||
int bestValue = Integer.MIN_VALUE;
|
int bestValue = Integer.MIN_VALUE;
|
||||||
for (Move move : board.getMoves(color)) {
|
for (Move move : board.getMoves(color)) {
|
||||||
|
++count;
|
||||||
Piece capturePiece = board.move(move);
|
Piece capturePiece = board.move(move);
|
||||||
int teamValue = board.evaluate(color);
|
int teamValue = board.evaluate(color);
|
||||||
int enemyValue = board.evaluate(color.opposite());
|
int enemyValue = board.evaluate(color.opposite());
|
||||||
int valueChange = teamValue - enemyValue;
|
int valueChange = teamValue - enemyValue;
|
||||||
|
|
||||||
if (depth < 4) valueChange -= findBestMove(board, color.opposite(), depth + 1);
|
if (depth <= 3 && valueChange >= 0) valueChange -= findBestMove(board, color.opposite(), depth + 1);
|
||||||
|
|
||||||
if (valueChange > bestValue) {
|
if (valueChange > bestValue) {
|
||||||
bestValue = valueChange;
|
bestValue = valueChange;
|
||||||
|
@ -3,6 +3,7 @@ package dev.kske.chess.game;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import dev.kske.chess.board.Board;
|
import dev.kske.chess.board.Board;
|
||||||
|
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.BoardPanel;
|
import dev.kske.chess.ui.BoardPanel;
|
||||||
@ -36,7 +37,18 @@ public class Game {
|
|||||||
if (board.getPos(move).getColor() == player.color && board.attemptMove(move)) {
|
if (board.getPos(move).getColor() == player.color && board.attemptMove(move)) {
|
||||||
System.out.printf("%s: %s%n", player.color, move);
|
System.out.printf("%s: %s%n", player.color, move);
|
||||||
boardPanel.repaint();
|
boardPanel.repaint();
|
||||||
players.get(player.color.opposite()).requestMove();
|
|
||||||
|
GameState eventType = board.getGameEventType(board.getDest(move).getColor().opposite());
|
||||||
|
switch (eventType) {
|
||||||
|
case CHECKMATE:
|
||||||
|
case STALEMATE:
|
||||||
|
System.out.printf("%s in %s!%n", player.color, eventType);
|
||||||
|
break;
|
||||||
|
case CHECK:
|
||||||
|
System.out.printf("%s in check!%n", player.color);
|
||||||
|
default:
|
||||||
|
players.get(player.color.opposite()).requestMove();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
System.out.printf("%s: Illegal move!%n", player.getColor());
|
System.out.printf("%s: Illegal move!%n", player.getColor());
|
||||||
player.requestMove();
|
player.requestMove();
|
||||||
|
@ -8,13 +8,10 @@ import java.awt.event.ComponentEvent;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
|
||||||
import dev.kske.chess.board.Board;
|
import dev.kske.chess.board.Board;
|
||||||
import dev.kske.chess.board.Move;
|
import dev.kske.chess.board.Move;
|
||||||
import dev.kske.chess.event.GameEvent;
|
|
||||||
import dev.kske.chess.event.GameEventListener;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Project: <strong>Chess</strong><br>
|
* Project: <strong>Chess</strong><br>
|
||||||
@ -26,7 +23,7 @@ import dev.kske.chess.event.GameEventListener;
|
|||||||
* 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 implements GameEventListener {
|
public class BoardPanel extends JPanel {
|
||||||
|
|
||||||
private static final long serialVersionUID = 6771148331334310216L;
|
private static final long serialVersionUID = 6771148331334310216L;
|
||||||
|
|
||||||
@ -90,18 +87,6 @@ public class BoardPanel extends JPanel implements GameEventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onGameEvent(GameEvent evt) {
|
|
||||||
switch (evt.getEventType()) {
|
|
||||||
case CHECK:
|
|
||||||
JOptionPane.showMessageDialog(this, evt.getColor().toString() + " in check!");
|
|
||||||
break;
|
|
||||||
case CHECKMATE:
|
|
||||||
JOptionPane.showMessageDialog(this, evt.getColor().toString() + " in checkmate!");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays move destinations on the board.
|
* Displays move destinations on the board.
|
||||||
*
|
*
|
||||||
@ -142,10 +127,5 @@ public class BoardPanel extends JPanel implements GameEventListener {
|
|||||||
|
|
||||||
public Board getBoard() { return board; }
|
public Board getBoard() { return board; }
|
||||||
|
|
||||||
public void setBoard(Board board) {
|
public void setBoard(Board board) { this.board = board; }
|
||||||
this.board = board;
|
|
||||||
|
|
||||||
// Register this BoardPanel as a GameEventListener to the board
|
|
||||||
board.registerGameEventListener(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -49,8 +49,7 @@ public class GameModeDialog extends JDialog {
|
|||||||
Map<Color, Player> players = new HashMap<>();
|
Map<Color, Player> players = new HashMap<>();
|
||||||
players.put(Color.WHITE, new NaturalPlayer(boardPanel.getBoard(), Color.WHITE, boardPanel));
|
players.put(Color.WHITE, new NaturalPlayer(boardPanel.getBoard(), Color.WHITE, boardPanel));
|
||||||
players.put(Color.BLACK, new AIPlayer(boardPanel.getBoard(), Color.BLACK));
|
players.put(Color.BLACK, new AIPlayer(boardPanel.getBoard(), Color.BLACK));
|
||||||
new Game(players, boardPanel.getBoard(), boardPanel).start();
|
startGame(players, boardPanel);
|
||||||
dispose();
|
|
||||||
});
|
});
|
||||||
getContentPane().add(btnAI);
|
getContentPane().add(btnAI);
|
||||||
|
|
||||||
@ -59,9 +58,13 @@ public class GameModeDialog extends JDialog {
|
|||||||
Map<Color, Player> players = new HashMap<>();
|
Map<Color, Player> players = new HashMap<>();
|
||||||
players.put(Color.WHITE, new AIPlayer(boardPanel.getBoard(), Color.WHITE));
|
players.put(Color.WHITE, new AIPlayer(boardPanel.getBoard(), Color.WHITE));
|
||||||
players.put(Color.BLACK, new AIPlayer(boardPanel.getBoard(), Color.BLACK));
|
players.put(Color.BLACK, new AIPlayer(boardPanel.getBoard(), Color.BLACK));
|
||||||
new Game(players, boardPanel.getBoard(), boardPanel).start();
|
startGame(players, boardPanel);
|
||||||
dispose();
|
|
||||||
});
|
});
|
||||||
getContentPane().add(btnAI2);
|
getContentPane().add(btnAI2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startGame(Map<Color, Player> players, BoardPanel boardPanel) {
|
||||||
|
new Game(players, boardPanel.getBoard(), boardPanel).start();
|
||||||
|
dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user