Also known as Noughts and Crosses.
A Rust program to generate all terminal positions of the game of Tic-Tac-Toe. Rotations, transpositions, games-still-in-progress and impossible board states are not shown.
use std::collections::HashMap; const EMPTY: char = ' '; const X: char = 'X'; const O: char = 'O'; type Boards = HashMap; type Board = [[char; 3]; 3]; type Representations = [String; 8]; trait BoardMethods { fn display(&self) -> String; fn is_terminal(&self) -> bool; fn representation(&self) -> String; fn all_representations(&self) -> Representations; fn rotate(&self) -> Board; fn transpose(&self) -> Board; } trait BoardsMethods { fn contains(&self, b: Board) -> bool; fn insert_board(&mut self, b: Board); } fn main() { test(); let empty_board: Board = [[EMPTY; 3]; 3]; let mut final_boards: Boards = HashMap::new(); let boards: Vec = all_boards(empty_board, X); for board in boards { if board.is_terminal() && !final_boards.contains(board) { final_boards.insert_board(board); } } println!("{} unique terminal boards found\n", final_boards.len()); for (_, board) in final_boards { println!("{}", board.display()); } } fn all_boards(board: Board, to_play: char) -> Vec { let to_play_next: char = if to_play == X { O } else { X }; let mut boards: Vec = Vec::new(); for x in 0..board.len() { let row = board[x]; for y in 0..row.len() { if board[x][y] == EMPTY { let mut b: Board = board.clone(); b[x][y] = to_play; boards.push(b); if !b.is_terminal() { boards.append(&mut all_boards(b, to_play_next)); } } } } return boards; } impl BoardMethods for Board { fn display(&self) -> String { let mut s = String::new(); for row in self { for square in row { s.push('|'); s.push(*square); } s.push_str("|\n"); } return s; } fn is_terminal(&self) -> bool { for i in 0..3 { if self[i][0] != EMPTY && self[i][0] == self[i][1] && self[i][1] == self[i][2] { return true; } if self[0][i] != EMPTY && self[0][i] == self[1][i] && self[1][i] == self[2][i] { return true; } } if self[0][0] != EMPTY && self[0][0] == self[1][1] && self[1][1] == self[2][2] { return true; } if self[0][2] != EMPTY && self[0][2] == self[1][1] && self[1][1] == self[2][0] { return true; } for row in self { for square in row { if *square == EMPTY { return false; } } } return true; } fn all_representations(&self) -> Representations { let mut r: Representations = Default::default(); r[0] = self.representation(); r[1] = self.rotate().representation(); r[2] = self.rotate().rotate().representation(); r[3] = self.rotate().rotate().rotate().representation(); r[4] = self.transpose().representation(); r[5] = self.rotate().transpose().representation(); r[6] = self.rotate().rotate().transpose().representation(); r[7] = self.rotate().rotate().rotate().transpose().representation(); return r; } fn representation(&self) -> String { let mut s = String::new(); for row in self { for square in row { s.push(*square); } } return s; } fn rotate(&self) -> Board { let mut b: Board = [[EMPTY, EMPTY, EMPTY]; 3]; for x in 0..self.len() { let row = self[x]; for y in 0..row.len() { b[x][y] = self[row.len() - y - 1][x]; } } return b } fn transpose(&self) -> Board { let mut b: Board = [[EMPTY, EMPTY, EMPTY]; 3]; for x in 0..self.len() { let row = self[x]; for y in 0..row.len() { b[y][x] = self[x][y]; } } return b; } } impl BoardsMethods for Boards { fn contains(&self, board: Board) -> bool { for repr in board.all_representations() { if self.contains_key(&repr) { return true; } } return false; } fn insert_board(&mut self, b: Board) { self.insert(b.representation(), b); } } fn test() { test_rotate(); test_transpose(); } fn test_rotate() { // XXX _OX // OOO => _OX // ___ _OX let b: Board = [[X, X, X], [O, O, O], [EMPTY, EMPTY, EMPTY]]; let rotated: Board = b.rotate(); assert!(rotated[0] == [EMPTY, O, X]); assert!(rotated[1] == [EMPTY, O, X]); assert!(rotated[2] == [EMPTY, O, X]); } fn test_transpose() { // X_O XO_ // OX_ => _X_ // __X O_X let b: Board = [[X, EMPTY, O], [O, X, EMPTY], [EMPTY, EMPTY, X]]; let transposed: Board = b.transpose(); assert!(transposed == [[X, O, EMPTY], [EMPTY, X, EMPTY], [O, EMPTY, X]]); }