I am working on a CLI chess game that only involves 2 human players, I decided to make it follow the MVC architecture to ensure separation of concern and to make the possibility of a GUI/web integration easier to achieve.
Here is the package diagram:
The view only has the View
class that draws the board and the pieces, the controller takes input from the user and communicates with the Game
class inside of the game
package, which is more of a Facade class, which is inside the model.
The player can enter a command (i.e "move a2 a4") and the controller will pass that to the game
object and execute the command using the command design pattern.
ChessGame (controller) class:
package controller;
import model.game.Game;
import view.View;
import java.util.Scanner;
public class ChessGame {
private final Game game;
private final View view;
public ChessGame() {
this.game = new Game();
this.view = new View();
}
public void start() {
Scanner sc = new Scanner(System.in);
view.draw(game.getBoard());
initializePlayers(sc);
while (true) {
try {
System.out.println(game.getCurrentPlayer() + " turn!");
if (game.playerIsInCheck()) {
System.out.println("Check! Protect the king.");
}
System.out.println("Enter a command:");
game.executeCommand(sc.nextLine());
if (game.pawnPromoted()) {
handlePromotion(sc);
}
//If player is still in check after executing the move, undo the move.
if (game.playerIsInCheck()) {
game.undoCommand();
throw new IllegalArgumentException("King is still in check, undoing last move...");
}
view.draw(game.getBoard());
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println("Try again.");
continue;
}
game.setWhitePlayerTurn(!game.isWhitePlayerTurn());
game.setBlackPlayerTurn(!game.isBlackPlayerTurn());
}
}
public void handlePromotion(Scanner sc) {
System.out.println("PROMOTION!");
System.out.println("Choose a piece to promote to:");
System.out.println("1)Queen\n2)Rook\n3)Bishop\n4)Knight");
int choice = sc.nextInt();
game.promote(choice);
}
private void initializePlayers(Scanner sc) {
System.out.println("Enter white player name: ");
String whitePlayerName = sc.nextLine();
System.out.println("Enter black player name: ");
String blackPlayerName = sc.nextLine();
game.setWhitePlayerName(whitePlayerName);
game.setBlackPlayerName(blackPlayerName);
}
}
draw
method inside of View
:
public void draw(Board board) {
int rows = Board.ROWS;
int columns = Board.COLUMNS;
String borderColor = ANSI_RED;
System.out.println(borderColor + " ===================" + ANSI_RESET);
for (int i = 1; i <= columns; i++) {
System.out.print(((columns + 1) - i) + borderColor + " || " + ANSI_RESET);
for (int j = 1; j <= rows; j++) {
Square currentSquare = new Square(j, (columns + 1) - i);
if (board.isSquareOccupied(currentSquare)) {
printPiece(board.getPieceFromSquare(currentSquare));
} else {
System.out.print("- " + ANSI_RESET);
}
}
System.out.println(borderColor + "||" + ANSI_RESET);
}
System.out.println(borderColor + " ===================" + ANSI_RESET);
System.out.println("\t a b c d e f g h");
}
ANSI_*
are constant variables for color.
I am not going to add code from the model since I am concerned about the architecture and the way the classes communicate, not the logic.
Is this overengineered, or still a sensible design choice for a CLI application? Also, since we're using DI in draw
to insert the board, does this make the view
package dependent on model
? Is there a way to use draw
without any DI?