Fixed checkmate detection, simplified event handling
This commit is contained in:
parent
dd5170066b
commit
55e9cfb620
@ -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.
|
||||
*/
|
||||
|
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 {
|
||||
|
||||
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;
|
||||
|
@ -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();
|
||||
|
||||
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();
|
||||
|
@ -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; }
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user