Skip to Content
Course content

231: Building a Basic Chess Game Engine

Click on the "Edit" button in the top corner of the screen to edit your slide content.

When I first started building game engines, I fell into a trap that I see almost every junior developer hit when they tackle chess: the "Stringly-Typed" board. You'll be tempted to represent your board as a String[][] or an int[][], where "WP" stands for White Pawn and "BK" stands for Black King. It feels fast. You can print it to the console easily, and it seems intuitive.

Here is why that's a disaster. The moment you need to implement a move, you end up with a massive, nested switch statement or a chain of if-else blocks that looks like this:

if (board[row][col].equals("WP")) {
    // 50 lines of pawn movement logic
} else if (board[row][col].equals("WR")) {
    // 50 lines of rook movement logic
} // ... and so on for every piece


This is brittle. One typo—like typing "Wp" instead of "WP"—and your game engine doesn't crash; it just silently fails to move the piece, leaving you hunting through logs for an hour. It's a maintenance nightmare because adding a new rule (like castling) requires hunting through a monolithic block of logic.

Forget Strings, Use Polymorphism

The correct way to handle this in Java is to let the pieces tell the board how they move, not the other way around. We want a polymorphic structure. I always start by defining an abstract Piece class. This class doesn't know how to move—that's for the subclasses—but it defines the contract that every piece must follow.

public abstract class Piece {
    protected Color color;

    public Piece(Color color) {
        this.color = color;
    }

    public Color getColor() {
        return color;
    }

    // Every piece must implement its own rule set
    public abstract boolean isValidMove(Board board, Position start, Position end);
}


By doing this, the Board class doesn't need to know if it's dealing with a Bishop or a Knight. It just calls piece.isValidMove(...). I love this approach because it encapsulates the complexity. If the Knight's movement logic is buggy, I know exactly which file to open: Knight.java.

The Board as a Manager, Not a Rulebook

Another common mistake is putting the movement validation logic inside the Board class. The board should be a coordinator. It should manage the 2D array of Piece objects and handle the physical swapping of positions, but it shouldn't be the one deciding if a diagonal move is legal for a Bishop.

Think of the Board as the physical wooden board and the Piece as the intelligence. Here is a streamlined way to handle the move request:

public class Board {
    private Piece[][] grid = new Piece[8][8];

    public boolean movePiece(Position start, Position end) {
        Piece piece = grid[start.getRow()][start.getCol()];
        
        if (piece == null) {
            return false; // No piece to move
        }

        if (piece.isValidMove(this, start, end)) {
            grid[end.getRow()][end.getCol()] = piece;
            grid[start.getRow()][start.getCol()] = null;
            return true;
        }
        
        return false; // Illegal move
    }
}


Notice how movePiece is clean? It asks the piece if the move is valid, and if the piece says "yes," the board updates the state. This separation of concerns is what differentiates a "script" from an "engine."

Handling the "Jump" Logic

Now, you might be wondering: "Wait, if the piece handles the logic, how does it know if another piece is blocking the path?" This is why we pass the Board instance into the isValidMove method.

For a Rook, the isValidMove implementation will loop through the coordinates between the start and end points. If it hits any non-null Piece in the board.grid before reaching the destination, it returns false. The Knight, of course, ignores this because it can jump. By passing the board reference, the piece has a "window" into the world around it without owning the world itself.




📋 Practical Task

Implementing the Knight's L-Shape Movement Logic

You have been provided with the Piece abstract class and the Board class. Your task is to create the Knight class that extends Piece.

Implement the isValidMove(Board board, Position start, Position end) method specifically for the Knight. Remember:

  • The Knight moves in an "L" shape: two squares in one cardinal direction and then one square perpendicularly.
  • Unlike the Rook or Bishop, the Knight can jump over other pieces; you do not need to check for blocking pieces between the start and end positions.
  • The move is invalid if the destination square is occupied by a piece of the same color.
  • The move is invalid if the destination is outside the 8x8 board boundaries.

Requirements: 1. Create the Knight class. 2. Use absolute difference calculations (Math.abs) to verify the 2-and-1 movement pattern. 3. Ensure you check the piece color at the destination square using the Board reference.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.