Fixed checkmate detection, simplified event handling

This commit is contained in:
Kai S. K. Engelbart 2019-07-07 21:07:58 +02:00
parent f4b399ae43
commit 31c393150b
Signed by: kske
GPG Key ID: 8BEB13EC5DF7EF13
8 changed files with 68 additions and 100 deletions

View File

@ -7,9 +7,6 @@ import java.util.Map;
import dev.kske.chess.board.Piece.Color;
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>
@ -22,12 +19,9 @@ public class Board {
private Piece[][] boardArr;
private Map<Color, Position> kingPos;
private List<GameEventListener> gameEventListeners;
public Board() {
boardArr = new Piece[8][8];
kingPos = new HashMap<>();
gameEventListeners = new ArrayList<>();
initializeDefaultPositions();
}
@ -53,11 +47,6 @@ public class Board {
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;
}
}
@ -114,6 +103,12 @@ public class Board {
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) {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++) {
@ -125,10 +120,31 @@ public class Board {
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)
? getMoves(kingPos.get(color)).isEmpty() ? GameEventType.CHECKMATE : GameEventType.CHECK
: getMoves(color).isEmpty() ? GameEventType.STALEMATE : GameEventType.NONE;
? checkCheckmate(color) ? GameState.CHECKMATE : GameState.CHECK
: getMoves(color).isEmpty() ? GameState.STALEMATE : GameState.NORMAL;
}
/**
@ -161,14 +177,6 @@ public class Board {
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.
*/

View 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;
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -14,6 +14,7 @@ import dev.kske.chess.board.Piece.Color;
public class AIPlayer extends Player {
private Move bestMove;
private int count;
public AIPlayer(Board board, Color color) {
super(board, color);
@ -21,19 +22,22 @@ public class AIPlayer extends Player {
@Override
public void requestMove() {
count = 0;
findBestMove(board, color, 0);
System.out.println("Moved processes: " + count);
game.onMove(this, bestMove);
}
private int findBestMove(Board board, Color color, int depth) {
int bestValue = Integer.MIN_VALUE;
for (Move move : board.getMoves(color)) {
++count;
Piece capturePiece = board.move(move);
int teamValue = board.evaluate(color);
int enemyValue = board.evaluate(color.opposite());
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) {
bestValue = valueChange;

View File

@ -3,6 +3,7 @@ package dev.kske.chess.game;
import java.util.Map;
import dev.kske.chess.board.Board;
import dev.kske.chess.board.GameState;
import dev.kske.chess.board.Move;
import dev.kske.chess.board.Piece.Color;
import dev.kske.chess.ui.BoardPanel;
@ -36,7 +37,18 @@ public class Game {
if (board.getPos(move).getColor() == player.color && board.attemptMove(move)) {
System.out.printf("%s: %s%n", player.color, move);
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 {
System.out.printf("%s: Illegal move!%n", player.getColor());
player.requestMove();

View File

@ -8,13 +8,10 @@ import java.awt.event.ComponentEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import dev.kske.chess.board.Board;
import dev.kske.chess.board.Move;
import dev.kske.chess.event.GameEvent;
import dev.kske.chess.event.GameEventListener;
/**
* 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
* size.
*/
public class BoardPanel extends JPanel implements GameEventListener {
public class BoardPanel extends JPanel {
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.
*
@ -142,10 +127,5 @@ public class BoardPanel extends JPanel implements GameEventListener {
public Board getBoard() { return board; }
public void setBoard(Board board) {
this.board = board;
// Register this BoardPanel as a GameEventListener to the board
board.registerGameEventListener(this);
}
public void setBoard(Board board) { this.board = board; }
}

View File

@ -49,8 +49,7 @@ public class GameModeDialog extends JDialog {
Map<Color, Player> players = new HashMap<>();
players.put(Color.WHITE, new NaturalPlayer(boardPanel.getBoard(), Color.WHITE, boardPanel));
players.put(Color.BLACK, new AIPlayer(boardPanel.getBoard(), Color.BLACK));
new Game(players, boardPanel.getBoard(), boardPanel).start();
dispose();
startGame(players, boardPanel);
});
getContentPane().add(btnAI);
@ -59,9 +58,13 @@ public class GameModeDialog extends JDialog {
Map<Color, Player> players = new HashMap<>();
players.put(Color.WHITE, new AIPlayer(boardPanel.getBoard(), Color.WHITE));
players.put(Color.BLACK, new AIPlayer(boardPanel.getBoard(), Color.BLACK));
new Game(players, boardPanel.getBoard(), boardPanel).start();
dispose();
startGame(players, boardPanel);
});
getContentPane().add(btnAI2);
}
private void startGame(Map<Color, Player> players, BoardPanel boardPanel) {
new Game(players, boardPanel.getBoard(), boardPanel).start();
dispose();
}
}