Command refactor

This commit is contained in:
2025-04-13 12:29:47 +02:00
parent 9f44548843
commit a23c334994
11 changed files with 207 additions and 100 deletions

View File

@@ -28,7 +28,7 @@ public class DumbAI extends GameAdaptator {
} }
@Override @Override
public void playerTurn(Color color) { public void onPlayerTurn(Color color) {
if (color != player) if (color != player)
return; return;
@@ -40,7 +40,7 @@ public class DumbAI extends GameAdaptator {
} }
@Override @Override
public void promotePawn(Coordinate pieceCoords) { public void onPromotePawn(Coordinate pieceCoords) {
Piece pawn = pieceAt(pieceCoords); Piece pawn = pieceAt(pieceCoords);
if (pawn.getColor() != this.player) if (pawn.getColor() != this.player)
return; return;

View File

@@ -6,7 +6,9 @@ import chess.model.Game;
public abstract class Command { public abstract class Command {
public enum CommandResult { public enum CommandResult {
/** The command was successfull. Should update display and switch player turn. */ /**
* The command was successfull. Should update display and switch player turn.
*/
Moved, Moved,
/** The command was successfull. Should not update anything */ /** The command was successfull. Should not update anything */
NotMoved, NotMoved,
@@ -18,5 +20,4 @@ public abstract class Command {
public abstract CommandResult execute(Game game, GameListener outputSystem); public abstract CommandResult execute(Game game, GameListener outputSystem);
public void postExec(Game game, GameListener outputSystem) {}
} }

View File

@@ -34,7 +34,6 @@ public class CommandExecutor {
if (command instanceof PlayerCommand playerCommand && result != CommandResult.NotAllowed) if (command instanceof PlayerCommand playerCommand && result != CommandResult.NotAllowed)
this.game.addAction(playerCommand); this.game.addAction(playerCommand);
command.postExec(game, dispatcher);
return result; return result;
} }
@@ -45,21 +44,23 @@ public class CommandExecutor {
return; return;
case ActionNeeded: case ActionNeeded:
this.dispatcher.updateDisplay(); this.dispatcher.onBoardUpdate();
return; return;
case Moved: case Moved:
if (checkGameStatus()) this.dispatcher.onBoardUpdate();
if (checkGameStatus()) {
this.dispatcher.onGameEnd();
return; return;
}
switchPlayerTurn(); switchPlayerTurn();
this.dispatcher.updateDisplay();
return; return;
} }
} }
private void switchPlayerTurn() { private void switchPlayerTurn() {
this.game.switchPlayerTurn(); this.game.switchPlayerTurn();
this.dispatcher.playerTurn(this.game.getPlayerTurn()); this.dispatcher.onPlayerTurn(this.game.getPlayerTurn());
} }
/** /**
@@ -71,19 +72,19 @@ public class CommandExecutor {
switch (gameStatus) { switch (gameStatus) {
case Check: case Check:
this.dispatcher.kingIsInCheck(); this.dispatcher.onKingInCheck();
return false; return false;
case CheckMate: case CheckMate:
this.dispatcher.kingIsInMat(); this.dispatcher.onKingInMat();
this.dispatcher.winnerIs(this.game.getPlayerTurn()); this.dispatcher.onWin(this.game.getPlayerTurn());
return true; return true;
case OnGoing: case OnGoing:
return false; return false;
case Pat: case Pat:
this.dispatcher.patSituation(); this.dispatcher.onPatSituation();
return true; return true;
} }

View File

@@ -25,6 +25,26 @@ public class MoveCommand extends PlayerCommand {
@Override @Override
public CommandResult execute(Game game, GameListener outputSystem) { public CommandResult execute(Game game, GameListener outputSystem) {
CommandResult result = processMove(game, outputSystem);
switch (result) {
case NotAllowed:
outputSystem.onMoveNotAllowed(this.move);
return result;
case Moved:
outputSystem.onMove(this.move);
return result;
case ActionNeeded:
case NotMoved:
return result;
}
return null;
}
private CommandResult processMove(Game game, GameListener outputSystem) {
final ChessBoard board = game.getBoard(); final ChessBoard board = game.getBoard();
// we must promote the pending pawn before // we must promote the pending pawn before
@@ -50,10 +70,12 @@ public class MoveCommand extends PlayerCommand {
return CommandResult.NotAllowed; return CommandResult.NotAllowed;
} }
if (board.pawnShouldBePromoted()) if (tryPromote(game, outputSystem)) {
return CommandResult.ActionNeeded; return CommandResult.ActionNeeded;
}
board.setLastMove(this.move); board.setLastMove(this.move);
return CommandResult.Moved; return CommandResult.Moved;
} }
@@ -65,18 +87,14 @@ public class MoveCommand extends PlayerCommand {
return CommandResult.Moved; return CommandResult.Moved;
} }
@Override private boolean tryPromote(Game game, GameListener outputSystem) {
public void postExec(Game game, GameListener outputSystem) {
tryPromote(game, outputSystem);
}
private void tryPromote(Game game, GameListener outputSystem) {
Coordinate pawnPos = game.getBoard().pawnPromotePosition(); Coordinate pawnPos = game.getBoard().pawnPromotePosition();
if (pawnPos == null) if (pawnPos == null)
return; return false;
outputSystem.promotePawn(pawnPos); outputSystem.onPromotePawn(pawnPos);
return true;
} }
} }

View File

@@ -50,8 +50,8 @@ public class NewGameCommand extends Command {
game.resetPlayerTurn(); game.resetPlayerTurn();
outputSystem.gameStarted(); outputSystem.onGameStart();
outputSystem.playerTurn(game.getPlayerTurn()); outputSystem.onPlayerTurn(game.getPlayerTurn());
return CommandResult.NotMoved; return CommandResult.NotMoved;
} }

View File

@@ -15,8 +15,8 @@ public class SurrenderCommand extends Command {
@Override @Override
public CommandResult execute(Game game, GameListener outputSystem) { public CommandResult execute(Game game, GameListener outputSystem) {
outputSystem.hasSurrendered(player); outputSystem.onSurrender(player);
outputSystem.winnerIs(Color.getEnemy(player)); outputSystem.onWin(Color.getEnemy(player));
return CommandResult.NotMoved; return CommandResult.NotMoved;
} }

View File

@@ -2,34 +2,44 @@ package chess.controller.event;
import chess.model.Color; import chess.model.Color;
import chess.model.Coordinate; import chess.model.Coordinate;
import chess.model.Move;
public abstract class GameAdaptator implements GameListener { public abstract class GameAdaptator implements GameListener {
@Override @Override
public void playerTurn(Color color) {} public void onPlayerTurn(Color color) {}
@Override @Override
public void winnerIs(Color color) {} public void onWin(Color color) {}
@Override @Override
public void kingIsInCheck() {} public void onKingInCheck() {}
@Override @Override
public void kingIsInMat() {} public void onKingInMat() {}
@Override @Override
public void patSituation() {} public void onPatSituation() {}
@Override @Override
public void hasSurrendered(Color color) {} public void onSurrender(Color color) {}
@Override @Override
public void gameStarted() {} public void onGameStart() {}
@Override @Override
public void promotePawn(Coordinate pieceCoords) {} public void onPromotePawn(Coordinate pieceCoords) {}
@Override @Override
public void updateDisplay() {} public void onBoardUpdate() {}
@Override
public void onGameEnd() {}
@Override
public void onMove(Move move) {}
@Override
public void onMoveNotAllowed(Move move) {}
} }

View File

@@ -8,6 +8,7 @@ import java.util.function.Consumer;
import chess.model.Color; import chess.model.Color;
import chess.model.Coordinate; import chess.model.Coordinate;
import chess.model.Move;
public class GameDispatcher implements GameListener{ public class GameDispatcher implements GameListener{
@@ -28,48 +29,63 @@ public class GameDispatcher implements GameListener{
} }
@Override @Override
public void playerTurn(Color color) { public void onPlayerTurn(Color color) {
asyncForEachCall((l) -> l.playerTurn(color)); asyncForEachCall((l) -> l.onPlayerTurn(color));
} }
@Override @Override
public void winnerIs(Color color) { public void onWin(Color color) {
asyncForEachCall((l) -> l.winnerIs(color)); asyncForEachCall((l) -> l.onWin(color));
} }
@Override @Override
public void kingIsInCheck() { public void onKingInCheck() {
asyncForEachCall((l) -> l.kingIsInCheck()); asyncForEachCall((l) -> l.onKingInCheck());
} }
@Override @Override
public void kingIsInMat() { public void onKingInMat() {
asyncForEachCall((l) -> l.kingIsInMat()); asyncForEachCall((l) -> l.onKingInMat());
} }
@Override @Override
public void patSituation() { public void onPatSituation() {
asyncForEachCall((l) -> l.patSituation()); asyncForEachCall((l) -> l.onPatSituation());
} }
@Override @Override
public void hasSurrendered(Color color) { public void onSurrender(Color color) {
asyncForEachCall((l) -> l.hasSurrendered(color)); asyncForEachCall((l) -> l.onSurrender(color));
} }
@Override @Override
public void gameStarted() { public void onGameStart() {
asyncForEachCall((l) -> l.gameStarted()); asyncForEachCall((l) -> l.onGameStart());
} }
@Override @Override
public void promotePawn(Coordinate pieceCoords) { public void onPromotePawn(Coordinate pieceCoords) {
asyncForEachCall((l) -> l.promotePawn(pieceCoords)); asyncForEachCall((l) -> l.onPromotePawn(pieceCoords));
} }
@Override @Override
public void updateDisplay() { public void onBoardUpdate() {
asyncForEachCall((l) -> l.updateDisplay()); 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));
} }
public void stopService() { public void stopService() {

View File

@@ -2,24 +2,74 @@ package chess.controller.event;
import chess.model.Color; import chess.model.Color;
import chess.model.Coordinate; import chess.model.Coordinate;
import chess.model.Move;
public interface GameListener { public interface GameListener {
void playerTurn(Color color); /**
* Invoked when the display of the board should be updated
*/
void onBoardUpdate();
/**
* Invoked when the game has ended (by a win or a draw)
*/
void onGameEnd();
void winnerIs(Color color); /**
* Invoked when the game has started
*/
void onGameStart();
/**
* Invoked when a king is in check
*/
void onKingInCheck();
void kingIsInCheck(); /**
* Invoked when a checkmate occurs
*/
void onKingInMat();
/**
* Invoked when a valid move on the board occurs
* @param move the move to be processed
*/
void onMove(Move move);
void kingIsInMat(); /**
* Invoked when a sent move is not allowed
* @param move the move to be processed
*/
void onMoveNotAllowed(Move move);
/**
* Invoked when a pat situation occurs
*/
void onPatSituation();
/**
* Invoked when it's the player turn
* @param color the color of the player who should play
*/
void onPlayerTurn(Color color);
/**
* Invoked when a pawn should be promoted
* @param pieceCoords the coordinates of the pawn
*/
void onPromotePawn(Coordinate pieceCoords);
/**
* Invoked when a players surrenders
* @param coward the player who gave up
*/
void onSurrender(Color coward);
/**
* Invoked when a player wins (by checkmate or if the other one surrenders)
* @param winner
*/
void onWin(Color winner);
void patSituation();
void hasSurrendered(Color color);
void gameStarted();
void promotePawn(Coordinate pieceCoords);
void updateDisplay();
} }

View File

@@ -55,7 +55,7 @@ public class Console implements GameListener {
} }
@Override @Override
public void playerTurn(Color color) { public void onPlayerTurn(Color color) {
System.out.println(Colors.RED + "Player turn: " + color + Colors.RESET); System.out.println(Colors.RED + "Player turn: " + color + Colors.RESET);
boolean endTurn; boolean endTurn;
do { do {
@@ -73,7 +73,7 @@ public class Console implements GameListener {
}; };
} while (!endTurn); } while (!endTurn);
System.out.println(Colors.RED + "Turn ended." + Colors.RESET); System.out.println(Colors.RED + "Turn ended." + Colors.RESET);
updateDisplay(); onBoardUpdate();
} }
private boolean playerPickedSurrender(Color color) { private boolean playerPickedSurrender(Color color) {
@@ -89,10 +89,9 @@ public class Console implements GameListener {
Coordinate end = stringToCoordinate(scanner.nextLine()); Coordinate end = stringToCoordinate(scanner.nextLine());
Command.CommandResult result = sendCommand(new MoveCommand(new Move(start, end))); Command.CommandResult result = sendCommand(new MoveCommand(new Move(start, end)));
switch (Objects.requireNonNull(result)) { switch (Objects.requireNonNull(result)) {
case Command.CommandResult.Moved: return true; case Command.CommandResult.Moved:
case Command.CommandResult.ActionNeeded: updateDisplay(); promotePawn(end); return true; case Command.CommandResult.ActionNeeded: return true;
default: System.out.println(result); default:
System.out.println("Move not allowed.");
return false; return false;
} }
@@ -125,46 +124,45 @@ public class Console implements GameListener {
} }
@Override @Override
public void winnerIs(Color color) { public void onWin(Color color) {
System.out.println(Colors.RED + "Victory of player " + color + Colors.RESET); System.out.println(Colors.RED + "Victory of player " + color + Colors.RESET);
gameEnded();
} }
@Override @Override
public void kingIsInCheck() { public void onKingInCheck() {
System.out.println(Colors.RED + "Check!" + Colors.RESET); System.out.println(Colors.RED + "Check!" + Colors.RESET);
// todo // todo
} }
@Override @Override
public void kingIsInMat() { public void onKingInMat() {
System.out.println(Colors.RED + "Checkmate!" + Colors.RESET); System.out.println(Colors.RED + "Checkmate!" + Colors.RESET);
} }
@Override @Override
public void patSituation() { public void onPatSituation() {
// todo // todo
gameEnded();
} }
@Override @Override
public void hasSurrendered(Color color) { public void onSurrender(Color color) {
System.out.println("The " + color + " player has surrendered!"); System.out.println("The " + color + " player has surrendered!");
} }
@Override @Override
public void gameStarted() { public void onGameStart() {
System.out.println("Game start!"); System.out.println("Game start!");
updateDisplay(); onBoardUpdate();
} }
public void gameEnded(){ @Override
public void onGameEnd() {
System.out.println("Thank you for playing!"); System.out.println("Thank you for playing!");
this.commandExecutor.close(); this.commandExecutor.close();
} }
@Override @Override
public void promotePawn(Coordinate pieceCoords) { public void onPromotePawn(Coordinate pieceCoords) {
System.out.println("The pawn on the " + pieceCoords + " coordinates needs to be promoted."); System.out.println("The pawn on the " + pieceCoords + " coordinates needs to be promoted.");
System.out.println("Enter 'B' to promote it into a Bishop, 'N' for a Knight, 'Q' for a Queen, 'R' for a Rook."); System.out.println("Enter 'B' to promote it into a Bishop, 'N' for a Knight, 'Q' for a Queen, 'R' for a Rook.");
boolean valid = false; boolean valid = false;
@@ -189,7 +187,7 @@ public class Console implements GameListener {
} }
@Override @Override
public void updateDisplay() { public void onBoardUpdate() {
StringBuilder string = new StringBuilder(); StringBuilder string = new StringBuilder();
string.append(" a b c d e f g h \n"); string.append(" a b c d e f g h \n");
for (int i = 0; i < Coordinate.VALUE_MAX; i++) { for (int i = 0; i < Coordinate.VALUE_MAX; i++) {
@@ -246,4 +244,12 @@ public class Console implements GameListener {
} }
System.out.println(string); System.out.println(string);
} }
@Override
public void onMove(Move move) {}
@Override
public void onMoveNotAllowed(Move move) {
System.out.println("Move not allowed.");
}
} }

View File

@@ -188,29 +188,25 @@ public class Window extends JFrame implements GameListener {
} }
if (!this.lastClick.equals(new Coordinate(x, y))) { if (!this.lastClick.equals(new Coordinate(x, y))) {
Move move = new Move(lastClick, new Coordinate(x, y)); Move move = new Move(lastClick, new Coordinate(x, y));
sendCommand(new MoveCommand(move));
if (sendCommand(new MoveCommand(move)) == CommandResult.NotAllowed) {
drawInvalid(move);
}
} }
this.lastClick = null; this.lastClick = null;
} }
@Override @Override
public void playerTurn(chess.model.Color color) { public void onPlayerTurn(chess.model.Color color) {
this.displayText.setText("Current turn: " + color); this.displayText.setText("Current turn: " + color);
} }
@Override @Override
public void winnerIs(chess.model.Color color) { public void onWin(chess.model.Color color) {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
JOptionPane.showMessageDialog(this, "Victory of " + color); JOptionPane.showMessageDialog(this, "Victory of " + color);
onGameEnd();
}); });
} }
@Override @Override
public void kingIsInCheck() { public void onKingInCheck() {
if (!showPopups) if (!showPopups)
return; return;
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
@@ -219,39 +215,40 @@ public class Window extends JFrame implements GameListener {
} }
@Override @Override
public void kingIsInMat() { public void onKingInMat() {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
JOptionPane.showMessageDialog(this, "Checkmate!"); JOptionPane.showMessageDialog(this, "Checkmate!");
}); });
} }
@Override @Override
public void patSituation() { public void onPatSituation() {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
JOptionPane.showMessageDialog(this, "Pat. It's a draw!"); JOptionPane.showMessageDialog(this, "Pat. It's a draw!");
onGameEnd();
}); });
} }
@Override @Override
public void hasSurrendered(chess.model.Color color) { public void onSurrender(chess.model.Color color) {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
JOptionPane.showMessageDialog(this, color + " has surrendered."); JOptionPane.showMessageDialog(this, color + " has surrendered.");
}); });
} }
private void onGameEnd() { @Override
public void onGameEnd() {
JOptionPane.showMessageDialog(this, "End of the game");
this.dispose(); this.dispose();
this.commandExecutor.close(); this.commandExecutor.close();
} }
@Override @Override
public void gameStarted() { public void onGameStart() {
buildBoard(); buildBoard();
} }
@Override @Override
public void promotePawn(Coordinate pieceCoords) { public void onPromotePawn(Coordinate pieceCoords) {
if (!showPopups) if (!showPopups)
return; return;
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
@@ -290,8 +287,16 @@ public class Window extends JFrame implements GameListener {
} }
@Override @Override
public void updateDisplay() { public void onBoardUpdate() {
updateBoard(); updateBoard();
} }
@Override
public void onMove(Move move) {}
@Override
public void onMoveNotAllowed(Move move) {
drawInvalid(move);
}
} }