Validate Chess Moves: Bishop Diagonals, Blocked Paths and Shared Sliding Rules
Company: Glean
Role: Software Engineer
Category: Software Engineering Fundamentals
Difficulty: medium
Interview Round: Technical Screen
You are building the backend of a two-player chess game, and the backend must accept only legal moves. Rather than the full game framework, the interviewer narrows the task to move validation and builds it up in steps.
A square is a `Position` with integer fields `x` and `y`; assume `x` is the column and `y` the row, both from 0 to 7. For readers new to chess: a bishop moves any number of squares diagonally, a rook any number of squares along a row or a column, and a queen in either way; none of the three may jump over another piece. A move may end on an opponent's piece, which captures it, but never on one of the mover's own pieces.
```python
from dataclasses import dataclass
from typing import Protocol
@dataclass(frozen=True)
class Position:
x: int # column, 0-7
y: int # row, 0-7
@dataclass(frozen=True)
class Piece:
kind: str # "bishop", "rook", "queen", "knight", "king" or "pawn"
color: str # "white" or "black"
class Board(Protocol):
def piece_at(self, pos: Position) -> Piece | None: ...
```
The interviewer specified only `Position`; `Piece` and `Board` are assumed interfaces for Parts 2 and 3.
### Clarifying Questions
- Is `x` the column and `y` the row, and do both run from 0 to 7?
- How is the board stored, and what does it return for an empty square?
- Must a move also not leave the mover's own king in check, or does validation stop at how each piece moves?
- Should these functions check that it is the mover's turn, or is that the caller's job?
### Part 1 — Validate a bishop's move
Write `is_valid_bishop_move(a: Position, b: Position) -> bool`, which returns whether a bishop standing on `a` may move to `b` on an otherwise empty board.
```hint Compare the two displacements
Look at how far the move goes horizontally and vertically, and at which pair of positions is not a move at all.
```
#### What This Part Should Cover
- The diagonal condition, expressed through the column and row differences, for all four directions
- Rejecting a "move" that stays on the same square
- Rejecting positions outside the board
### Part 2 — Pieces in the way
A bishop cannot jump over other pieces. Write `is_legal_bishop_move(board: Board, a: Position, b: Position) -> bool` for a bishop standing on `a`, taking the other pieces into account: any piece on a square between `a` and `b` blocks the move.
```hint Walk the squares
Work out the one-square step that leads from `a` toward `b`, and decide exactly which squares along the way must be checked, including whether the two endpoints are among them.
```
#### What This Part Should Cover
- Deriving the step direction from the signs of the differences
- Checking exactly the squares strictly between `a` and `b`
- The rule for the destination square: an opponent's piece against one of the mover's own
- Why the geometry check must come before the walk, and the cost of the walk
### Part 3 — Generalize to the other pieces
Generalize the validation with `is_legal_move(board: Board, a: Position, b: Position) -> bool` for whatever piece stands on `a`. The bishop, the rook and the queen should share one path check, while each kind of piece keeps its own movement constraints. Describe the structure and write its key parts. For reference: the knight jumps in an L shape (two squares in one direction and one at a right angle) and may pass over pieces; the king moves one square in any direction; a pawn moves one square forward, or two from its starting row, and captures one square diagonally forward.
```hint Split the rule in two
Separate "can this kind of piece make this displacement at all" from "is the way clear", and ask which pieces need the second check.
```
#### Clarifying Questions for this Part
- Should castling, en passant and promotion be handled here, or by a game layer that knows the history of the game?
#### What This Part Should Cover
- One shared path check reused by the bishop, the rook and the queen
- Per-piece rules for the knight, the king and the pawn
- A structure (a rule table or a class per piece) in which adding or changing a rule stays local
- Which rules need more than the board: turn order, check, castling, en passant
### What a Strong Answer Covers
- Correct geometry with bounds and null-move checks
- An exact walk over the intermediate squares, with the endpoints handled deliberately
- A shared path helper and per-piece rules without duplicated loops
- Clear interfaces for `Position`, `Piece`, `Board` and the validation entry point
- Tests for every direction, adjacent moves, blockers on the first and last intermediate squares, and captures of each colour
- Awareness of which legality rules depend on game state rather than on piece geometry
### Follow-up Questions
- How would you reject a move that leaves the mover's own king in check, and what does that cost per move?
- Where do castling and en passant fit, given that they depend on the history of the game?
- Two players' clients send moves at nearly the same time, or a client sends a move out of turn. How does the backend stay consistent?
- How would you list every legal move of a piece, for example to highlight the target squares in the interface?
Overview: Validate moves for a two-player chess backend: confirm a bishop's move is diagonal and not in place, reject moves blocked by pieces along the path, then generalize so the bishop, rook and queen share one path check while every piece keeps its own movement rules. Tests board geometry, exact iteration and extensible design.
Read the full Glean Software Engineer interview experience this question came from