From 13b61ad71c85407c4ca75f78bf190b57a300e088 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Sat, 19 Apr 2025 12:56:08 +0200 Subject: [PATCH] big refactor --- app/src/main/java/chess/SwingMain.java | 4 +- app/src/main/java/chess/ai/AlphaBetaAI.java | 131 ++++++++---------- .../main/java/chess/ai/GameSimulation.java | 96 +++++++++++++ app/src/main/java/chess/ai/HungryAI.java | 2 +- app/src/main/java/chess/ai/PieceCost.java | 30 ++-- app/src/main/java/chess/ai/PiecePosCost.java | 131 +++++++++--------- .../chess/controller/CommandExecutor.java | 5 +- .../controller/event/AsyncGameDispatcher.java | 113 +++++++++++++++ .../controller/event/EmptyGameDispatcher.java | 8 ++ .../controller/event/GameDispatcher.java | 110 +-------------- 10 files changed, 366 insertions(+), 264 deletions(-) create mode 100644 app/src/main/java/chess/ai/GameSimulation.java create mode 100644 app/src/main/java/chess/controller/event/AsyncGameDispatcher.java diff --git a/app/src/main/java/chess/SwingMain.java b/app/src/main/java/chess/SwingMain.java index 51d5065..bbaafcc 100644 --- a/app/src/main/java/chess/SwingMain.java +++ b/app/src/main/java/chess/SwingMain.java @@ -20,10 +20,10 @@ public class SwingMain { Window window = new Window(commandExecutor, false); commandExecutor.addListener(window); - AI ai = new AlphaBetaAI(commandExecutor, Color.White, 3); + AI ai = new HungryAI(commandExecutor, Color.White); commandExecutor.addListener(ai); - AI ai2 = new AlphaBetaAI(commandExecutor, Color.Black, 3); + AI ai2 = new AlphaBetaAI(commandExecutor, Color.Black, 1); commandExecutor.addListener(ai2); // Window window2 = new Window(ai2.getSimulation(), false); diff --git a/app/src/main/java/chess/ai/AlphaBetaAI.java b/app/src/main/java/chess/ai/AlphaBetaAI.java index e8ce957..7a9042c 100644 --- a/app/src/main/java/chess/ai/AlphaBetaAI.java +++ b/app/src/main/java/chess/ai/AlphaBetaAI.java @@ -2,20 +2,18 @@ package chess.ai; import java.util.ArrayList; import java.util.List; -import java.util.Random; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import chess.controller.CommandExecutor; -import chess.controller.commands.CastlingCommand; 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; import chess.model.Coordinate; -import chess.model.Game; import chess.model.Move; import chess.model.Piece; @@ -24,28 +22,30 @@ public class AlphaBetaAI extends AI { private final int searchDepth; private final PieceCost pieceCost; private final PiecePosCost piecePosCost; - private final CommandExecutor simulation; - private final Game gameSimulation; + private final GameSimulation simulation; - private final int GREAT_MOVE = -9999; - private final int HORRIBLE_MOVE = -GREAT_MOVE; + private static final float MAX_FLOAT = Float.MAX_VALUE; + private static final float MIN_FLOAT = -MAX_FLOAT; + + private static final int GREAT_MOVE = -9999; + private static final int HORRIBLE_MOVE = -GREAT_MOVE; + + private final ExecutorService threadPool; public AlphaBetaAI(CommandExecutor commandExecutor, Color color, int searchDepth) { super(commandExecutor, color); this.searchDepth = searchDepth; this.pieceCost = new PieceCost(color); this.piecePosCost = new PiecePosCost(color); - this.gameSimulation = new Game(); - this.simulation = new CommandExecutor(this.gameSimulation, new EmptyGameDispatcher()); + this.simulation = new GameSimulation(); + commandExecutor.addListener(simulation); + this.threadPool = Executors.newSingleThreadExecutor(); + System.out.println(Runtime.getRuntime().availableProcessors()); } - public CommandExecutor getSimulation() { - return simulation; - } - - private int getBoardEvaluation() { - final ChessBoard board = this.gameSimulation.getBoard(); - int result = 0; + private float getBoardEvaluation() { + final ChessBoard board = this.simulation.getBoard(); + float result = 0; for (int i = 0; i < Coordinate.VALUE_MAX; i++) { for (int j = 0; j < Coordinate.VALUE_MAX; j++) { Coordinate coordinate = new Coordinate(i, j); @@ -53,48 +53,45 @@ public class AlphaBetaAI extends AI { result += pieceCost.getCost(piece) + piecePosCost.getEvaluation(piece, coordinate); } } - if (this.gameSimulation.getPlayerTurn() != color) + if (this.simulation.getPlayerTurn() != color) return -result; return result; } - private int getEndGameEvaluation() { - Color currentTurn = this.gameSimulation.getPlayerTurn(); + private float getEndGameEvaluation() { + Color currentTurn = this.simulation.getPlayerTurn(); if (currentTurn == this.color) { return HORRIBLE_MOVE; } else { - if (this.gameSimulation.getBoard().isKingInCheck(currentTurn)) + if (this.simulation.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 float getMoveValue(Move move) { + this.simulation.tryMove(move); + float value = -negaMax(this.searchDepth - 1, MIN_FLOAT, MAX_FLOAT); + this.simulation.undoMove(); + return value; } - private void simulateUndo() { - sendCommand(new UndoCommand(), this.simulation); - } - - private int negaMax(int depth, int alpha, int beta) { + private float negaMax(int depth, float alpha, float beta) { if (depth == 0) return getBoardEvaluation(); - int value = Integer.MIN_VALUE; + float value = MIN_FLOAT; - List moves = getAllowedMoves(this.simulation); + List moves = this.simulation.getAllowedMoves(); if (moves.isEmpty()) return getEndGameEvaluation(); for (Move move : moves) { - simulateMove(move); - value = Integer.max(value, -negaMax(depth - 1, -beta, -alpha)); - simulateUndo(); - alpha = Integer.max(alpha, value); + this.simulation.tryMove(move); + value = Float.max(value, -negaMax(depth - 1, -beta, -alpha)); + this.simulation.undoMove(); + alpha = Float.max(alpha, value); if (alpha >= beta) return value; } @@ -103,51 +100,39 @@ public class AlphaBetaAI extends AI { } private Move getBestMove() { - List moves = getAllowedMoves(this.simulation); - int bestMove = Integer.MIN_VALUE; - List bestMoves = new ArrayList<>(20); + List moves = this.simulation.getAllowedMoves(); + List> moveEvaluations = new ArrayList<>(50); + float bestMoveValue = MIN_FLOAT; + Move bestMove = null; for (Move move : moves) { - simulateMove(move); - int value = -negaMax(this.searchDepth - 1, Integer.MIN_VALUE, Integer.MAX_VALUE); - simulateUndo(); - if (value > bestMove) { - bestMove = value; - bestMoves.clear(); - bestMoves.add(move); - } else if (value == bestMove) { - bestMoves.add(move); + moveEvaluations.add(this.threadPool.submit(() -> { + return getMoveValue(move); + })); + } + + for (int i = 0; i < moves.size(); i++) { + Move move = moves.get(i); + float value = MIN_FLOAT; + try { + value = moveEvaluations.get(i).get(); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + if (value > bestMoveValue) { + bestMoveValue = value; + bestMove = move; } } - System.out.println("Best move : " + bestMove + " count : " + bestMoves.size()); + System.out.println("Best move : " + bestMoveValue); - return bestMoves.get(new Random().nextInt(bestMoves.size())); - } - - @Override - public void onGameStart() { - sendCommand(new NewGameCommand(), this.simulation); + return bestMove; } @Override public void onGameEnd() { - this.simulation.close(); - } - - @Override - public void onPawnPromoted(PromoteType promotion) { - sendCommand(new PromoteCommand(promotion), this.simulation); - } - - @Override - public void onCastling(boolean bigCastling) { - sendCommand(new CastlingCommand(bigCastling), this.simulation); - } - - @Override - public void onMove(Move move) { - sendCommand(new MoveCommand(move), this.simulation); + this.threadPool.close(); } @Override diff --git a/app/src/main/java/chess/ai/GameSimulation.java b/app/src/main/java/chess/ai/GameSimulation.java new file mode 100644 index 0000000..c1525b9 --- /dev/null +++ b/app/src/main/java/chess/ai/GameSimulation.java @@ -0,0 +1,96 @@ +package chess.ai; + +import java.util.List; + +import chess.controller.Command; +import chess.controller.Command.CommandResult; +import chess.controller.CommandExecutor; +import chess.controller.commands.CastlingCommand; +import chess.controller.commands.GetPlayerMovesCommand; +import chess.controller.commands.MoveCommand; +import chess.controller.commands.NewGameCommand; +import chess.controller.commands.PromoteCommand; +import chess.controller.commands.UndoCommand; +import chess.controller.commands.PromoteCommand.PromoteType; +import chess.controller.event.EmptyGameDispatcher; +import chess.controller.event.GameAdaptator; +import chess.model.ChessBoard; +import chess.model.Color; +import chess.model.Game; +import chess.model.Move; + +public class GameSimulation extends GameAdaptator { + + private final CommandExecutor simulation; + private final Game gameSimulation; + + public GameSimulation() { + this.gameSimulation = new Game(); + this.simulation = new CommandExecutor(gameSimulation, new EmptyGameDispatcher()); + } + + protected CommandResult sendCommand(Command command) { + CommandResult result = this.simulation.executeCommand(command); + if (result == CommandResult.NotAllowed) { + System.out.println("eeeeee"); + } + return result; + } + + public void tryMove(Move move) { + sendCommand(new MoveCommand(move)); + if (this.gameSimulation.getBoard().pawnShouldBePromoted()) + sendCommand(new PromoteCommand(PromoteType.Queen)); + } + + public void undoMove() { + sendCommand(new UndoCommand()); + } + + @Override + public void onPawnPromoted(PromoteType promotion) { + sendCommand(new PromoteCommand(promotion)); + } + + @Override + public void onCastling(boolean bigCastling) { + sendCommand(new CastlingCommand(bigCastling)); + } + + @Override + public void onMove(Move move) { + sendCommand(new MoveCommand(move)); + } + + @Override + public void onGameStart() { + sendCommand(new NewGameCommand()); + } + + public CommandExecutor getCommandExecutor() { + return simulation; + } + + public Game getGame() { + return gameSimulation; + } + + public ChessBoard getBoard() { + return this.gameSimulation.getBoard(); + } + + public Color getPlayerTurn() { + return this.gameSimulation.getPlayerTurn(); + } + + public List getAllowedMoves() { + GetPlayerMovesCommand cmd = new GetPlayerMovesCommand(); + sendCommand(cmd); + return cmd.getMoves(); + } + + public void close() { + this.simulation.close(); + } + +} diff --git a/app/src/main/java/chess/ai/HungryAI.java b/app/src/main/java/chess/ai/HungryAI.java index ed2303c..435c465 100644 --- a/app/src/main/java/chess/ai/HungryAI.java +++ b/app/src/main/java/chess/ai/HungryAI.java @@ -26,7 +26,7 @@ public class HungryAI extends AI { private int getMoveCost(Move move) { Piece piece = pieceAt(move.getDeadPieceCoords()); - return -pieceCost.getCost(piece); + return - (int) pieceCost.getCost(piece); } private List getBestMoves() { diff --git a/app/src/main/java/chess/ai/PieceCost.java b/app/src/main/java/chess/ai/PieceCost.java index 385be77..8654688 100644 --- a/app/src/main/java/chess/ai/PieceCost.java +++ b/app/src/main/java/chess/ai/PieceCost.java @@ -10,57 +10,57 @@ import chess.model.pieces.Pawn; import chess.model.pieces.Queen; import chess.model.pieces.Rook; -public class PieceCost implements PieceVisitor { +public class PieceCost implements PieceVisitor { private final Color player; - public static final int BISHOP = 30; - public static final int KING = 900; - public static final int KNIGHT = 30; - public static final int PAWN = 10; - public static final int QUEEN = 90; - public static final int ROOK = 50; + public static final float BISHOP = 30; + public static final float KING = 900; + public static final float KNIGHT = 30; + public static final float PAWN = 10; + public static final float QUEEN = 90; + public static final float ROOK = 50; public PieceCost(Color color) { this.player = color; } - public int getCost(Piece piece) { + public float getCost(Piece piece) { if (piece == null) return 0; - int cost = visit(piece); + float cost = visit(piece); if (piece.getColor() != player) cost = -cost; return cost; } @Override - public Integer visitPiece(Bishop bishop) { + public Float visitPiece(Bishop bishop) { return BISHOP; } @Override - public Integer visitPiece(King king) { + public Float visitPiece(King king) { return KING; } @Override - public Integer visitPiece(Knight knight) { + public Float visitPiece(Knight knight) { return KNIGHT; } @Override - public Integer visitPiece(Pawn pawn) { + public Float visitPiece(Pawn pawn) { return PAWN; } @Override - public Integer visitPiece(Queen queen) { + public Float visitPiece(Queen queen) { return QUEEN; } @Override - public Integer visitPiece(Rook rook) { + public Float visitPiece(Rook rook) { return ROOK; } diff --git a/app/src/main/java/chess/ai/PiecePosCost.java b/app/src/main/java/chess/ai/PiecePosCost.java index be1478e..68af25e 100644 --- a/app/src/main/java/chess/ai/PiecePosCost.java +++ b/app/src/main/java/chess/ai/PiecePosCost.java @@ -14,114 +14,115 @@ import chess.model.pieces.Pawn; import chess.model.pieces.Queen; import chess.model.pieces.Rook; -public class PiecePosCost implements PieceVisitor> { +public class PiecePosCost implements PieceVisitor> { private final Color color; - private static final List BISHOP = Arrays.asList( - -2, -1, -1, -1, -1, -1, -1, -2, -1, - 0, 0, 0, 0, 0, 0, -1, - -1, 0, 0, 1, 1, 0, 0, -1, - -1, 0, 0, 1, 1, 0, 0, -1, - -1, 0, 0, 1, 1, 0, 0, -1, - -1, 1, 1, 1, 1, 1, 1, -1, - -1, 0, 0, 0, 0, 0, 0, -1, - -2, -1, -1, -1, -1, -1, -1, -2); + private static final List BISHOP = Arrays.asList( + -2.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -2.0f, + -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, + -1.0f, 0.0f, 0.5f, 1.0f, 1.0f, 0.5f, 0.0f, -1.0f, + -1.0f, 0.5f, 0.5f, 1.0f, 1.0f, 0.5f, 0.5f, -1.0f, + -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, -1.0f, + -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, + -1.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, -1.0f, + -2.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -2.0f); - private static final List KING = Arrays.asList( - -3, -4, -4, -5, -5, -4, -4, -3, - -3, -4, -4, -5, -5, -4, -4, -3, - -3, -4, -4, -5, -5, -4, -4, -3, - -3, -4, -4, -5, -5, -4, -4, -3, - -2, -3, -3, -4, -4, -3, -3, -2, - -1, -2, -2, -2, -2, -2, -2, -1, - 2, 2, 0, 0, 0, 0, 2, 2, - 2, 3, 1, 0, 0, 1, 3, 2); + private static final List KING = Arrays.asList( + -3.0f, -4.0f, -4.0f, -5.0f, -5.0f, -4.0f, -4.0f, -3.0f, + -3.0f, -4.0f, -4.0f, -5.0f, -5.0f, -4.0f, -4.0f, -3.0f, + -3.0f, -4.0f, -4.0f, -5.0f, -5.0f, -4.0f, -4.0f, -3.0f, + -3.0f, -4.0f, -4.0f, -5.0f, -5.0f, -4.0f, -4.0f, -3.0f, + -2.0f, -3.0f, -3.0f, -4.0f, -4.0f, -3.0f, -3.0f, -2.0f, + -1.0f, -2.0f, -2.0f, -2.0f, -2.0f, -2.0f, -2.0f, -1.0f, + 2.0f, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, 2.0f, + 2.0f, 3.0f, 1.0f, 0.0f, 0.0f, 1.0f, 3.0f, 2.0f); - private static final List KNIGHT = Arrays.asList( - -5, -4, -3, -3, -3, -3, -4, -5, -4, - -2, 0, 0, 0, 0, -2, -4, - -3, 0, 1, 1, 1, 1, 0, -3, - -3, 0, 1, 2, 2, 1, 0, -3, - -3, 0, 1, 2, 2, 1, 0, -3, - -3, 0, 1, 1, 1, 1, 0, -3, - -4, -2, 0, 0, 0, 0, -2, -4, - -5, -4, -3, -3, -3, -3, -4, -5); + private static final List KNIGHT = Arrays.asList( + -5.0f, -4.0f, -3.0f, -3.0f, -3.0f, -3.0f, -4.0f, -5.0f, + -4.0f, -2.0f, 0.0f, 0.0f, 0.0f, 0.0f, -2.0f, -4.0f, + -3.0f, 0.0f, 1.0f, 1.5f, 1.5f, 1.0f, 0.0f, -3.0f, + -3.0f, 0.5f, 1.5f, 2.0f, 2.0f, 1.5f, 0.5f, -3.0f, + -3.0f, 0.0f, 1.5f, 2.0f, 2.0f, 1.5f, 0.0f, -3.0f, + -3.0f, 0.5f, 1.0f, 1.5f, 1.5f, 1.0f, 0.5f, -3.0f, + -4.0f, -2.0f, 0.0f, 0.5f, 0.5f, 0.0f, -2.0f, -4.0f, + -5.0f, -4.0f, -3.0f, -3.0f, -3.0f, -3.0f, -4.0f, -5.0f); - private static final List PAWN = Arrays.asList( - 0, 0, 0, 0, 0, 0, 0, 0, - 5, 5, 5, 5, 5, 5, 5, 5, - 1, 1, 2, 3, 3, 2, 1, 1, - 0, 0, 1, 2, 2, 1, 0, 0, - 0, 0, 1, 2, 2, 1, 0, 0, - 0, 0, -1, 0, 0, -1, 0, 0, - 0, 1, 1, -2, -2, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0); + private static final List PAWN = Arrays.asList( + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, + 1.0f, 1.0f, 2.0f, 3.0f, 3.0f, 2.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 1.0f, 2.5f, 2.5f, 1.0f, 0.5f, 0.5f, + 0.0f, 0.0f, 1.0f, 2.0f, 2.0f, 1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, -1.0f, -0.5f, 0.5f, + 0.5f, 1.0f, 1.0f, -2.0f, -2.0f, 1.0f, 1.0f, 0.5f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); - private static final List QUEEN = Arrays.asList( - -2, -1, -1, 0, 0, -1, -1, -2, - -1, 0, 0, 0, 0, 0, 0, -1, - -1, 0, 0, 0, 0, 0, 0, -1, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - -1, 0, 0, 0, 0, 0, 0, -1, - -1, 0, 0, 0, 0, 0, 0, -1, - -2, -1, -1, 0, 0, -1, -1, -2); + private static final List QUEEN = Arrays.asList( + -2.0f, -1.0f, -1.0f, -0.5f, -0.5f, -1.0f, -1.0f, -2.0f, + -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, + -1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 0.5f, 0.0f, -1.0f, + -0.5f, 0.0f, 0.5f, 0.5f, 0.5f, 0.5f, 0.0f, -0.5f, + 0.0f, 0.0f, 0.5f, 0.5f, 0.5f, 0.5f, 0.0f, -0.5f, + -1.0f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.0f, -1.0f, + -1.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, + -2.0f, -1.0f, -1.0f, -0.5f, -0.5f, -1.0f, -1.0f, -2.0f); - private static final List ROOK = Arrays.asList( - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, - -1, 0, 0, 0, 0, 0, 0, 0, -1, - -1, 0, 0, 0, 0, 0, 0, 0, -1, - -1, 0, 0, 0, 0, 0, 0, 0, -1, - -1, 0, 0, 0, 0, 0, 0, 0, -1, - -1, 0, 0, 0, 0, 0, 0, 0, -1, - 0, 0, 0, 0, 1, 1, 0, 0, 0); + private static final List ROOK = Arrays.asList( + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.5f, + -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.5f, + -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.5f, + -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.5f, + -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.5f, + -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.5f, + 0.0f, 0.0f, 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f); public PiecePosCost(Color color) { this.color = color; } - public int getEvaluation(Piece piece, Coordinate coordinate) { + public float getEvaluation(Piece piece, Coordinate coordinate) { if (piece == null) return 0; - List positions = visit(piece); + List positions = visit(piece); + int x = piece.getColor() == Color.Black ? (Coordinate.VALUE_MAX - 1 - coordinate.getX()) : coordinate.getX(); int y = piece.getColor() == Color.Black ? (Coordinate.VALUE_MAX - 1 - coordinate.getY()) : coordinate.getY(); - Coordinate newCoords = new Coordinate(coordinate.getX(), y); + Coordinate newCoords = new Coordinate(x, y); assert newCoords.isValid(); - int result = positions.get(newCoords.toIndex()); + float result = positions.get(newCoords.toIndex()); if (piece.getColor() != color) return -result; return result; } @Override - public List visitPiece(Bishop bishop) { + public List visitPiece(Bishop bishop) { return BISHOP; } @Override - public List visitPiece(King king) { + public List visitPiece(King king) { return KING; } @Override - public List visitPiece(Knight knight) { + public List visitPiece(Knight knight) { return KNIGHT; } @Override - public List visitPiece(Pawn pawn) { + public List visitPiece(Pawn pawn) { return PAWN; } @Override - public List visitPiece(Queen queen) { + public List visitPiece(Queen queen) { return QUEEN; } @Override - public List visitPiece(Rook rook) { + public List visitPiece(Rook rook) { return ROOK; } diff --git a/app/src/main/java/chess/controller/CommandExecutor.java b/app/src/main/java/chess/controller/CommandExecutor.java index 5724f26..f33040f 100644 --- a/app/src/main/java/chess/controller/CommandExecutor.java +++ b/app/src/main/java/chess/controller/CommandExecutor.java @@ -2,6 +2,7 @@ package chess.controller; import chess.controller.Command.CommandResult; import chess.controller.commands.UndoCommand; +import chess.controller.event.AsyncGameDispatcher; import chess.controller.event.GameDispatcher; import chess.controller.event.GameListener; import chess.model.Game; @@ -17,7 +18,7 @@ public class CommandExecutor { } public CommandExecutor(Game game) { - this(game, new GameDispatcher()); + this(game, new AsyncGameDispatcher()); } public CommandExecutor(Game game, GameDispatcher dispatcher) { @@ -109,6 +110,6 @@ public class CommandExecutor { } public void close() { - this.dispatcher.stopService(); + this.dispatcher.close(); } } diff --git a/app/src/main/java/chess/controller/event/AsyncGameDispatcher.java b/app/src/main/java/chess/controller/event/AsyncGameDispatcher.java new file mode 100644 index 0000000..49c9343 --- /dev/null +++ b/app/src/main/java/chess/controller/event/AsyncGameDispatcher.java @@ -0,0 +1,113 @@ +package chess.controller.event; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Consumer; + +import chess.controller.commands.PromoteCommand.PromoteType; +import chess.model.Color; +import chess.model.Coordinate; +import chess.model.Move; + +public class AsyncGameDispatcher extends GameDispatcher { + + private final List listeners; + private final ExecutorService executor; + + public AsyncGameDispatcher() { + this.listeners = new ArrayList<>(); + this.executor = Executors.newSingleThreadExecutor(); + } + + @Override + public void addListener(GameListener listener) { + this.listeners.add(listener); + } + + private void asyncForEachCall(Consumer func) { + this.executor.execute(() -> this.listeners.forEach(func)); + } + + @Override + public void onPlayerTurn(Color color, boolean undone) { + asyncForEachCall((l) -> l.onPlayerTurn(color, undone)); + } + + @Override + public void onWin(Color color) { + asyncForEachCall((l) -> l.onWin(color)); + } + + @Override + public void onKingInCheck() { + asyncForEachCall((l) -> l.onKingInCheck()); + } + + @Override + public void onKingInMat() { + asyncForEachCall((l) -> l.onKingInMat()); + } + + @Override + public void onPatSituation() { + asyncForEachCall((l) -> l.onPatSituation()); + } + + @Override + public void onSurrender(Color color) { + asyncForEachCall((l) -> l.onSurrender(color)); + } + + @Override + public void onGameStart() { + asyncForEachCall((l) -> l.onGameStart()); + } + + @Override + public void onPromotePawn(Coordinate pieceCoords) { + asyncForEachCall((l) -> l.onPromotePawn(pieceCoords)); + } + + @Override + public void onBoardUpdate() { + asyncForEachCall((l) -> l.onBoardUpdate()); + } + + @Override + public void onGameEnd() { + asyncForEachCall((l) -> l.onGameEnd()); + } + + @Override + public void onMove(Move move) { + asyncForEachCall((l) -> l.onMove(move)); + } + + @Override + public void onMoveNotAllowed(Move move) { + asyncForEachCall((l) -> l.onMoveNotAllowed(move)); + } + + @Override + public void onDraw() { + asyncForEachCall((l) -> l.onDraw()); + } + + @Override + public void onCastling(boolean bigCastling) { + asyncForEachCall((l) -> l.onCastling(bigCastling)); + } + + @Override + public void onPawnPromoted(PromoteType promotion) { + asyncForEachCall((l) -> l.onPawnPromoted(promotion)); + } + + @Override + public void close() { + this.executor.shutdown(); + } + +} diff --git a/app/src/main/java/chess/controller/event/EmptyGameDispatcher.java b/app/src/main/java/chess/controller/event/EmptyGameDispatcher.java index 5e1b9ab..8ad5084 100644 --- a/app/src/main/java/chess/controller/event/EmptyGameDispatcher.java +++ b/app/src/main/java/chess/controller/event/EmptyGameDispatcher.java @@ -67,4 +67,12 @@ public class EmptyGameDispatcher extends GameDispatcher { public void onPawnPromoted(PromoteType promotion) { } + @Override + public void addListener(GameListener listener) { + } + + @Override + public void close() { + } + } diff --git a/app/src/main/java/chess/controller/event/GameDispatcher.java b/app/src/main/java/chess/controller/event/GameDispatcher.java index 06023fe..87c3f4d 100644 --- a/app/src/main/java/chess/controller/event/GameDispatcher.java +++ b/app/src/main/java/chess/controller/event/GameDispatcher.java @@ -1,111 +1,9 @@ package chess.controller.event; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.function.Consumer; +public abstract class GameDispatcher extends GameAdaptator { -import chess.controller.commands.PromoteCommand.PromoteType; -import chess.model.Color; -import chess.model.Coordinate; -import chess.model.Move; - -public class GameDispatcher implements GameListener { - - private final List listeners; - private final ExecutorService executor; - - public GameDispatcher() { - this.listeners = new ArrayList<>(); - this.executor = Executors.newSingleThreadExecutor(); - } - - public void addListener(GameListener listener) { - this.listeners.add(listener); - } - - private void asyncForEachCall(Consumer func) { - this.executor.execute(() -> this.listeners.forEach(func)); - } - - @Override - public void onPlayerTurn(Color color, boolean undone) { - asyncForEachCall((l) -> l.onPlayerTurn(color, undone)); - } - - @Override - public void onWin(Color color) { - asyncForEachCall((l) -> l.onWin(color)); - } - - @Override - public void onKingInCheck() { - asyncForEachCall((l) -> l.onKingInCheck()); - } - - @Override - public void onKingInMat() { - asyncForEachCall((l) -> l.onKingInMat()); - } - - @Override - public void onPatSituation() { - asyncForEachCall((l) -> l.onPatSituation()); - } - - @Override - public void onSurrender(Color color) { - asyncForEachCall((l) -> l.onSurrender(color)); - } - - @Override - public void onGameStart() { - asyncForEachCall((l) -> l.onGameStart()); - } - - @Override - public void onPromotePawn(Coordinate pieceCoords) { - asyncForEachCall((l) -> l.onPromotePawn(pieceCoords)); - } - - @Override - public void onBoardUpdate() { - asyncForEachCall((l) -> l.onBoardUpdate()); - } - - @Override - public void onGameEnd() { - asyncForEachCall((l) -> l.onGameEnd()); - } - - @Override - public void onMove(Move move) { - asyncForEachCall((l) -> l.onMove(move)); - } - - @Override - public void onMoveNotAllowed(Move move) { - asyncForEachCall((l) -> l.onMoveNotAllowed(move)); - } - - @Override - public void onDraw() { - asyncForEachCall((l) -> l.onDraw()); - } - - @Override - public void onCastling(boolean bigCastling) { - asyncForEachCall((l) -> l.onCastling(bigCastling)); - } - - @Override - public void onPawnPromoted(PromoteType promotion) { - asyncForEachCall((l) -> l.onPawnPromoted(promotion)); - } - - public void stopService() { - this.executor.shutdown(); - } + public abstract void addListener(GameListener listener); + public abstract void close(); + }