A Tic Tac Toe Board Validator

Asked to build a Tic Tac Toe board validator in Rust. Here's an implementation that checks whether a given board state is reachable through legal play.

use std::fmt;

// Represents a single cell on the Tic Tac Toe board
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Cell {
    Empty,
    X,
    O,
}

#[derive(Debug)]
enum ValidationError {
    InvalidCounts,
    BothPlayersWon,
    WinnerCountMismatch,
}

impl fmt::Display for ValidationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

trait BoardValidator {
    fn validate(&self) -> Result<(), ValidationError>;
}

struct Board {
    cells: [[Cell; 3]; 3],
}

impl Board {
    // Construct a new board from a 2D array of cells
    fn new(cells: [[Cell; 3]; 3]) -> Self {
        Board { cells }
    }

    fn count(&self, target: Cell) -> usize {
        self.cells.iter().flatten().filter(|&&c| c == target).count()
    }

    fn has_winner(&self, player: Cell) -> bool {
        let b = &self.cells;
        for i in 0..3 {
            // Check row i
            if b[i].iter().all(|&c| c == player) { return true; }
            // Check column i
            if (0..3).all(|j| b[j][i] == player) { return true; }
        }
        // Check diagonals
        if (0..3).all(|i| b[i][i] == player) { return true; }
        if (0..3).all(|i| b[i][2 - i] == player) { return true; }
        false
    }
}

impl BoardValidator for Board {
    fn validate(&self) -> Result<(), ValidationError> {
        let x_count = self.count(Cell::X);
        let o_count = self.count(Cell::O);

        // X goes first, so X count must equal O count or be exactly one more
        if x_count != o_count && x_count != o_count + 1 {
            return Err(ValidationError::InvalidCounts);
        }

        let x_wins = self.has_winner(Cell::X);
        let o_wins = self.has_winner(Cell::O);

        if x_wins && o_wins {
            return Err(ValidationError::BothPlayersWon);
        }
        if x_wins && x_count != o_count + 1 {
            return Err(ValidationError::WinnerCountMismatch);
        }
        if o_wins && x_count != o_count {
            return Err(ValidationError::WinnerCountMismatch);
        }
        Ok(())
    }
}

fn main() {
    let board = Board::new([
        [Cell::X, Cell::O, Cell::X],
        [Cell::O, Cell::X, Cell::O],
        [Cell::Empty, Cell::Empty, Cell::X],
    ]);
    match board.validate() {
        Ok(()) => println!("Board is valid"),
        Err(e) => println!("Invalid: {}", e),
    }
}

Code Review

1. Lines 11-15. Three error variants for a tic tac toe validator. Not awful, but InvalidCounts and WinnerCountMismatch overlap conceptually and you'll regret this distinction the first time you write tests.

2. Lines 17-21. Implementing Display just to call write!(f, "{:?}", self) means you've reinvented Debug with extra steps. Either write real messages or derive what you need.

3. Lines 23-25. A BoardValidator trait with one impl on one struct. This is the textbook example of an interface that exists because the author felt guilty not having one.

4. Lines 32-34. The comment 'Construct a new board from a 2D array of cells' tells me exactly what the function signature already tells me. Thanks.

5. Lines 40-50. has_winner is fine but mixes early returns inside a for loop with trailing diagonal checks. Pulling all four line generators into one iterator chain would actually be shorter and more idiomatic.

6. Lines 58-61. This comment about X going first is the only comment in the file that actually earns its keep. Promote this one, delete the others.

7. Lines 68-74. The WinnerCountMismatch branch silently subsumes the case where someone wins with an illegal count, but InvalidCounts already caught most of that above. Worth a unit test to confirm the order of checks actually matters.

8. Lines 79-88. main does exactly one hardcoded validation and prints a result. No input parsing, no tests, no CLI. For a 'validator' this is more of a demo than a tool.