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.