From 55e9cfb62045e1725b68ce3eb820e331574f182c Mon Sep 17 00:00:00 2001 From: CyB3RC0nN0R Date: Sun, 7 Jul 2019 21:07:58 +0200 Subject: [PATCH] Fixed checkmate detection, simplified event handling --- src/dev/kske/chess/board/Board.java | 52 +++++++++++-------- src/dev/kske/chess/board/GameState.java | 11 ++++ src/dev/kske/chess/event/GameEvent.java | 38 -------------- .../kske/chess/event/GameEventListener.java | 12 ----- src/dev/kske/chess/game/AIPlayer.java | 6 ++- src/dev/kske/chess/game/Game.java | 14 ++++- src/dev/kske/chess/ui/BoardPanel.java | 24 +-------- src/dev/kske/chess/ui/GameModeDialog.java | 11 ++-- 8 files changed, 68 insertions(+), 100 deletions(-) create mode 100644 src/dev/kske/chess/board/GameState.java delete mode 100644 src/dev/kske/chess/event/GameEvent.java delete mode 100644 src/dev/kske/chess/event/GameEventListener.java diff --git a/src/dev/kske/chess/board/Board.java b/src/dev/kske/chess/board/Board.java index 9c29145..b34c68c 100644 --- a/src/dev/kske/chess/board/Board.java +++ b/src/dev/kske/chess/board/Board.java @@ -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: Chess
@@ -22,12 +19,9 @@ public class Board { private Piece[][] boardArr; private Map kingPos; - private List 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. */ diff --git a/src/dev/kske/chess/board/GameState.java b/src/dev/kske/chess/board/GameState.java new file mode 100644 index 0000000..07579dd --- /dev/null +++ b/src/dev/kske/chess/board/GameState.java @@ -0,0 +1,11 @@ +package dev.kske.chess.board; + +/** + * Project: Chess
+ * File: GameState.java
+ * Created: 07.07.2019
+ * Author: Kai S. K. Engelbart + */ +public enum GameState { + CHECK, CHECKMATE, STALEMATE, NORMAL; +} diff --git a/src/dev/kske/chess/event/GameEvent.java b/src/dev/kske/chess/event/GameEvent.java deleted file mode 100644 index ae5d6c9..0000000 --- a/src/dev/kske/chess/event/GameEvent.java +++ /dev/null @@ -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: Chess
- * File: GameEvent.java
- * Created: 03.07.2019
- * Author: Kai S. K. Engelbart - */ -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; - } -} diff --git a/src/dev/kske/chess/event/GameEventListener.java b/src/dev/kske/chess/event/GameEventListener.java deleted file mode 100644 index f01ee6a..0000000 --- a/src/dev/kske/chess/event/GameEventListener.java +++ /dev/null @@ -1,12 +0,0 @@ -package dev.kske.chess.event; - -/** - * Project: Chess
- * File: GameEventListener.java
- * Created: 03.07.2019
- * Author: Kai S. K. Engelbart - */ -public interface GameEventListener { - - void onGameEvent(GameEvent evt); -} diff --git a/src/dev/kske/chess/game/AIPlayer.java b/src/dev/kske/chess/game/AIPlayer.java index 4dad40d..4cfc702 100644 --- a/src/dev/kske/chess/game/AIPlayer.java +++ b/src/dev/kske/chess/game/AIPlayer.java @@ -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; diff --git a/src/dev/kske/chess/game/Game.java b/src/dev/kske/chess/game/Game.java index 487a421..2179d64 100644 --- a/src/dev/kske/chess/game/Game.java +++ b/src/dev/kske/chess/game/Game.java @@ -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(); diff --git a/src/dev/kske/chess/ui/BoardPanel.java b/src/dev/kske/chess/ui/BoardPanel.java index a068ce5..5cf4b3c 100644 --- a/src/dev/kske/chess/ui/BoardPanel.java +++ b/src/dev/kske/chess/ui/BoardPanel.java @@ -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: Chess
@@ -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; } } diff --git a/src/dev/kske/chess/ui/GameModeDialog.java b/src/dev/kske/chess/ui/GameModeDialog.java index fbc8835..0db92ee 100644 --- a/src/dev/kske/chess/ui/GameModeDialog.java +++ b/src/dev/kske/chess/ui/GameModeDialog.java @@ -49,8 +49,7 @@ public class GameModeDialog extends JDialog { Map 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 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 players, BoardPanel boardPanel) { + new Game(players, boardPanel.getBoard(), boardPanel).start(); + dispose(); + } }