diff --git a/app/src/main/java/chess/ai/AlphaBetaAI.java b/app/src/main/java/chess/ai/AlphaBetaAI.java index 6b9313d..37e8022 100644 --- a/app/src/main/java/chess/ai/AlphaBetaAI.java +++ b/app/src/main/java/chess/ai/AlphaBetaAI.java @@ -10,6 +10,7 @@ import chess.controller.commands.MoveCommand; import chess.controller.commands.NewGameCommand; import chess.controller.commands.PromoteCommand; import chess.controller.commands.PromoteCommand.PromoteType; +import chess.controller.event.EmptyGameDispatcher; import chess.controller.commands.UndoCommand; import chess.model.ChessBoard; import chess.model.Color; @@ -24,12 +25,15 @@ public class AlphaBetaAI extends AI { private final CommandExecutor simulation; private final Game gameSimulation; + private final int GREAT_MOVE = -9999; + private final int HORRIBLE_MOVE = -GREAT_MOVE; + public AlphaBetaAI(CommandExecutor commandExecutor, Color color, int searchDepth) { super(commandExecutor, color); - this.searchDepth = 3; + this.searchDepth = searchDepth; this.pieceCost = new PieceCost(color); this.gameSimulation = new Game(); - this.simulation = new CommandExecutor(this.gameSimulation); + this.simulation = new CommandExecutor(this.gameSimulation, new EmptyGameDispatcher()); } public CommandExecutor getSimulation() { @@ -44,9 +48,32 @@ public class AlphaBetaAI extends AI { result += pieceCost.getCost(board.pieceAt(new Coordinate(i, j))); } } + if (this.gameSimulation.getPlayerTurn() != color) + return -result; return result; } + private int getEndGameEvaluation() { + Color currentTurn = this.gameSimulation.getPlayerTurn(); + if (currentTurn == this.color) { + return HORRIBLE_MOVE; + } else { + if (this.gameSimulation.getBoard().isKingInCheck(currentTurn)) + return GREAT_MOVE; + return getBoardEvaluation() - PieceCost.PAWN; + } + } + + private void simulateMove(Move move) { + sendCommand(new MoveCommand(move), this.simulation); + if (this.gameSimulation.getBoard().pawnShouldBePromoted()) + sendCommand(new PromoteCommand(PromoteType.Queen), this.simulation); + } + + private void simulateUndo() { + sendCommand(new UndoCommand(), this.simulation); + } + private int negaMax(int depth, int alpha, int beta) { if (depth == 0) return getBoardEvaluation(); @@ -55,20 +82,13 @@ public class AlphaBetaAI extends AI { List moves = getAllowedMoves(this.simulation); - if (moves.isEmpty()) { - System.out.println("oaaaaaaaaa"); - int board = getBoardEvaluation(); - int result = board + (this.gameSimulation.getPlayerTurn() == this.color ? -100 : 100); - return result; - } + if (moves.isEmpty()) + return getEndGameEvaluation(); for (Move move : moves) { - sendCommand(new MoveCommand(move), this.simulation); - if (this.gameSimulation.getBoard().pawnShouldBePromoted()) - sendCommand(new PromoteCommand(PromoteType.Queen), this.simulation); - int leaveValue = -negaMax(depth - 1, -beta, -alpha); - value = Integer.max(value, leaveValue); - sendCommand(new UndoCommand(), this.simulation); + simulateMove(move); + value = Integer.max(value, -negaMax(depth - 1, -beta, -alpha)); + simulateUndo(); alpha = Integer.max(alpha, value); if (alpha >= beta) return value; @@ -83,11 +103,9 @@ public class AlphaBetaAI extends AI { List bestMoves = new ArrayList<>(20); for (Move move : moves) { - sendCommand(new MoveCommand(move), this.simulation); - if (this.gameSimulation.getBoard().pawnShouldBePromoted()) - sendCommand(new PromoteCommand(PromoteType.Queen), this.simulation); + simulateMove(move); int value = -negaMax(this.searchDepth - 1, Integer.MIN_VALUE, Integer.MAX_VALUE); - sendCommand(new UndoCommand(), this.simulation); + simulateUndo(); if (value > bestMove) { bestMove = value; bestMoves.clear(); @@ -108,9 +126,12 @@ public class AlphaBetaAI extends AI { } @Override - public void onPawnPromoted(PromoteType promotion, Color player) { - // if (player == this.color) - // return; + public void onGameEnd() { + this.simulation.close(); + } + + @Override + public void onPawnPromoted(PromoteType promotion) { sendCommand(new PromoteCommand(promotion), this.simulation); } diff --git a/app/src/main/java/chess/ai/PieceCost.java b/app/src/main/java/chess/ai/PieceCost.java index 61a9c4f..d94e4e0 100644 --- a/app/src/main/java/chess/ai/PieceCost.java +++ b/app/src/main/java/chess/ai/PieceCost.java @@ -14,6 +14,13 @@ public class PieceCost implements PieceVisitor { private final Color player; + public static final int BISHOP = 3; + public static final int KING = 90; + public static final int KNIGHT = 3; + public static final int PAWN = 1; + public static final int QUEEN = 9; + public static final int ROOK = 5; + public PieceCost(Color color) { this.player = color; } @@ -22,39 +29,39 @@ public class PieceCost implements PieceVisitor { if (piece == null) return 0; int cost = visit(piece); - if (piece.getColor() == player) + if (piece.getColor() != player) cost = -cost; return cost; } @Override public Integer visitPiece(Bishop bishop) { - return 3; + return BISHOP; } @Override public Integer visitPiece(King king) { - return 90; + return KING; } @Override public Integer visitPiece(Knight knight) { - return 3; + return KNIGHT; } @Override public Integer visitPiece(Pawn pawn) { - return 1; + return PAWN; } @Override public Integer visitPiece(Queen queen) { - return 9; + return QUEEN; } @Override public Integer visitPiece(Rook rook) { - return 5; + return ROOK; } } diff --git a/app/src/main/java/chess/controller/CommandExecutor.java b/app/src/main/java/chess/controller/CommandExecutor.java index 607a5ca..5724f26 100644 --- a/app/src/main/java/chess/controller/CommandExecutor.java +++ b/app/src/main/java/chess/controller/CommandExecutor.java @@ -19,7 +19,7 @@ public class CommandExecutor { public CommandExecutor(Game game) { this(game, new GameDispatcher()); } - + public CommandExecutor(Game game, GameDispatcher dispatcher) { this.game = game; this.dispatcher = dispatcher; @@ -52,18 +52,21 @@ public class CommandExecutor { return; case Moved: + boolean notifyPlayerTurn = true; this.dispatcher.onBoardUpdate(); if (checkGameStatus()) { this.dispatcher.onGameEnd(); + notifyPlayerTurn = false; } - switchPlayerTurn(command instanceof UndoCommand); + switchPlayerTurn(command instanceof UndoCommand, notifyPlayerTurn); return; } } - private void switchPlayerTurn(boolean undone) { + private void switchPlayerTurn(boolean undone, boolean notifyPlayerTurn) { this.game.switchPlayerTurn(); - this.dispatcher.onPlayerTurn(this.game.getPlayerTurn(), undone); + if (notifyPlayerTurn) + this.dispatcher.onPlayerTurn(this.game.getPlayerTurn(), undone); } /** diff --git a/app/src/main/java/chess/controller/commands/PromoteCommand.java b/app/src/main/java/chess/controller/commands/PromoteCommand.java index a735ab8..3adb4c4 100644 --- a/app/src/main/java/chess/controller/commands/PromoteCommand.java +++ b/app/src/main/java/chess/controller/commands/PromoteCommand.java @@ -55,7 +55,7 @@ public class PromoteCommand extends PlayerCommand { this.oldPawn = pawn; board.pieceComes(createPiece(this.promoteType, pawn.getColor()), this.pieceCoords); - outputSystem.onPawnPromoted(this.promoteType, game.getPlayerTurn()); + outputSystem.onPawnPromoted(this.promoteType); return CommandResult.Moved; } diff --git a/app/src/main/java/chess/controller/event/EmptyGameDispatcher.java b/app/src/main/java/chess/controller/event/EmptyGameDispatcher.java index 1e1ac2e..5e1b9ab 100644 --- a/app/src/main/java/chess/controller/event/EmptyGameDispatcher.java +++ b/app/src/main/java/chess/controller/event/EmptyGameDispatcher.java @@ -1,5 +1,6 @@ package chess.controller.event; +import chess.controller.commands.PromoteCommand.PromoteType; import chess.model.Color; import chess.model.Coordinate; import chess.model.Move; @@ -57,5 +58,13 @@ public class EmptyGameDispatcher extends GameDispatcher { @Override public void onWin(Color winner) { } - + + @Override + public void onCastling(boolean bigCastling) { + } + + @Override + public void onPawnPromoted(PromoteType promotion) { + } + } diff --git a/app/src/main/java/chess/controller/event/GameAdaptator.java b/app/src/main/java/chess/controller/event/GameAdaptator.java index 33ed535..03a3957 100644 --- a/app/src/main/java/chess/controller/event/GameAdaptator.java +++ b/app/src/main/java/chess/controller/event/GameAdaptator.java @@ -50,6 +50,6 @@ public abstract class GameAdaptator implements GameListener { public void onCastling(boolean bigCastling) {} @Override - public void onPawnPromoted(PromoteType promotion, Color player) {} + public void onPawnPromoted(PromoteType promotion) {} } diff --git a/app/src/main/java/chess/controller/event/GameDispatcher.java b/app/src/main/java/chess/controller/event/GameDispatcher.java index c311f9f..06023fe 100644 --- a/app/src/main/java/chess/controller/event/GameDispatcher.java +++ b/app/src/main/java/chess/controller/event/GameDispatcher.java @@ -100,8 +100,8 @@ public class GameDispatcher implements GameListener { } @Override - public void onPawnPromoted(PromoteType promotion, Color player) { - asyncForEachCall((l) -> l.onPawnPromoted(promotion, player)); + public void onPawnPromoted(PromoteType promotion) { + asyncForEachCall((l) -> l.onPawnPromoted(promotion)); } public void stopService() { diff --git a/app/src/main/java/chess/controller/event/GameListener.java b/app/src/main/java/chess/controller/event/GameListener.java index 74e4cfe..7086e93 100644 --- a/app/src/main/java/chess/controller/event/GameListener.java +++ b/app/src/main/java/chess/controller/event/GameListener.java @@ -98,6 +98,6 @@ public interface GameListener { * @param promotion the type of promotion * @param player the player who promoted the pawns */ - void onPawnPromoted(PromoteType promotion, Color player); + void onPawnPromoted(PromoteType promotion); } diff --git a/app/src/main/java/chess/model/Game.java b/app/src/main/java/chess/model/Game.java index 8982632..4b7be53 100644 --- a/app/src/main/java/chess/model/Game.java +++ b/app/src/main/java/chess/model/Game.java @@ -70,24 +70,26 @@ public class Game { playerTurn = Color.getEnemy(playerTurn); } - public GameStatus checkGameStatus() { - final Color enemy = Color.getEnemy(getPlayerTurn()); - + public GameStatus checkGameStatus(Color color) { if (checkDraw()) return GameStatus.Draw; - if (this.board.isKingInCheck(enemy)) - if (this.board.hasAllowedMoves(enemy)) + if (this.board.isKingInCheck(color)) + if (this.board.hasAllowedMoves(color)) return GameStatus.Check; else return GameStatus.CheckMate; - if (!board.hasAllowedMoves(enemy)) + if (!board.hasAllowedMoves(color)) return GameStatus.Pat; return GameStatus.OnGoing; } + public GameStatus checkGameStatus() { + return checkGameStatus(Color.getEnemy(getPlayerTurn())); + } + public void addAction(PlayerCommand command) { this.movesHistory.add(command); } diff --git a/app/src/main/java/chess/pgn/PgnExport.java b/app/src/main/java/chess/pgn/PgnExport.java index 3ad34f5..22e0cf7 100644 --- a/app/src/main/java/chess/pgn/PgnExport.java +++ b/app/src/main/java/chess/pgn/PgnExport.java @@ -137,7 +137,7 @@ public class PgnExport { } private static String checkCheckMate(Game game) { - switch (game.checkGameStatus()) { + switch (game.checkGameStatus(game.getPlayerTurn())) { case CheckMate: return "#"; diff --git a/app/src/main/java/chess/view/consolerender/Console.java b/app/src/main/java/chess/view/consolerender/Console.java index 1479d1e..559ff54 100644 --- a/app/src/main/java/chess/view/consolerender/Console.java +++ b/app/src/main/java/chess/view/consolerender/Console.java @@ -337,7 +337,7 @@ public class Console implements GameListener { public void onCastling(boolean bigCastling) {} @Override - public void onPawnPromoted(PromoteType promotion, Color player) {} + public void onPawnPromoted(PromoteType promotion) {} } diff --git a/app/src/main/java/chess/view/simplerender/Window.java b/app/src/main/java/chess/view/simplerender/Window.java index 275dd8a..10f6fab 100644 --- a/app/src/main/java/chess/view/simplerender/Window.java +++ b/app/src/main/java/chess/view/simplerender/Window.java @@ -322,6 +322,6 @@ public class Window extends JFrame implements GameListener { public void onCastling(boolean bigCastling) {} @Override - public void onPawnPromoted(PromoteType promotion, chess.model.Color player) {} + public void onPawnPromoted(PromoteType promotion) {} }