Skip to content

Instantly share code, notes, and snippets.

@phoemur
Created October 12, 2025 21:08
Show Gist options
  • Save phoemur/32d59426259fcf89fa5f9c439b4be2a2 to your computer and use it in GitHub Desktop.
Save phoemur/32d59426259fcf89fa5f9c439b4be2a2 to your computer and use it in GitHub Desktop.
tictactoe
use std::io;
use itertools::interleave;
type Board = [[char; 3]; 3];
fn main() {
let mut array: Board = [[' '; 3]; 3];
instructions();
println!("--------------------\n");
loop {
// Get input
print_board(&array);
println!("Enter your move (or exit): ");
let mut st = String::new();
let _ = io::stdin().read_line(&mut st);
let st2 = st.trim();
if st2 == "exit" {
std::process::exit(0);
}
let (x, y) = match get_coordinates(&st2) {
Some((x, y)) => (x, y),
None => {
println!("Invalid Input");
continue;
}
};
// println!("Coords:x={x} y={y}");
// Human move
if array[x][y] == ' ' {
array[x][y] = 'o';
} else {
println!("Invalid Input");
continue;
}
let mut score = evaluate(&array);
if score == -10 {
print_board(&array);
println!("YOU WIN!!\n");
std::process::exit(0);
} else if score == 10 {
print_board(&array);
println!("YOU LOOSE!!\n");
std::process::exit(0);
}
// Computer move
if let Some(best) = find_best_move(&mut array) {
if array[best.0][best.1] == ' ' {
array[best.0][best.1] = 'x';
score = evaluate(&array);
if score == -10 {
print_board(&array);
println!("YOU WIN!!\n");
std::process::exit(0);
} else if score == 10 {
print_board(&array);
println!("YOU LOOSE!!\n");
std::process::exit(0);
}
}
}
if !has_moves_left(&array) {
break;
}
}
print_board(&array);
println!("\nDRAW!!!\n\n")
}
// Evaluation function
fn evaluate(b: &Board) -> i64 {
// Checking for Rows for X or O victory.
for row in 0..3 {
if b[row][0] == b[row][1] && b[row][1] == b[row][2] {
if b[row][0] == 'x' {
return 10;
} else if b[row][0] == 'o' {
return -10;
}
}
}
// Checking for Columns for X or O victory.
for col in 0..3 {
if b[0][col] == b[1][col] && b[1][col] == b[2][col] {
if b[0][col] == 'x' {
return 10;
} else if b[0][col] == 'o' {
return -10;
}
}
}
// Checking for Diagonals for X or O victory.
if b[0][0] == b[1][1] && b[1][1] == b[2][2] {
if b[0][0] == 'x' {
return 10;
} else if b[0][0] == 'o' {
return -10;
}
}
if b[0][2] == b[1][1] && b[1][1] == b[2][0] {
if b[0][2] == 'x' {
return 10;
} else if b[0][2] == 'o' {
return -10;
}
}
// Else if none of them have won then return 0
return 0;
}
// This is the minimax function. It considers all
// the possible ways the game can go and returns
// the value of the board
fn minimax(board: &mut Board, depth: i64, is_max: bool) -> i64 {
let score = evaluate(&board);
// If Maximizer has won the game return his/her
// evaluated score minus depth (to make it smarter)
if score == 10 {
return score - depth;
}
// If Minimizer has won the game return his/her
// evaluated score plus depth (to make it smarter)
if score == -10 {
return score + depth;
}
// If there are no more moves and no winner then
// it is a tie
if !has_moves_left(&board) {
return 0;
}
// If this maximizer's move
if is_max == true {
let mut best = -1000;
// Traverse all cells
for i in 0..3 {
for j in 0..3 {
// Check if cell is empty
if board[i][j] == ' ' {
// Make the move
board[i][j] = 'x';
// Call minimax recursively and choose
// the maximum value
best = std::cmp::max(best, minimax(board, depth + 1, !is_max));
// Undo the move
board[i][j] = ' ';
}
}
}
return best;
}
// If this minimizer's move
else {
let mut best = 1000;
// Traverse all cells
for i in 0..3 {
for j in 0..3 {
// Check if cell is empty
if board[i][j] == ' ' {
// Make the move
board[i][j] = 'o';
// Call minimax recursively and choose
// the minimum value
best = std::cmp::min(best, minimax(board, depth + 1, !is_max));
// Undo the move
board[i][j] = ' ';
}
}
}
return best;
}
}
fn find_best_move(board: &mut Board) -> Option<(usize, usize)> {
let mut best_val = -1000;
let mut row: i64 = -1;
let mut col: i64 = -1;
// Traverse all cells, evalutae minimax function for
// all empty cells. And return the cell with optimal
// value.
for i in 0..3 {
for j in 0..3 {
// Check if celll is empty
if board[i][j] == ' ' {
// Make the move
board[i][j] = 'x';
// compute evaluation function for this
// move.
let move_val = minimax(board, 0, false);
// Undo the move
board[i][j] = ' ';
// If the value of the current move is
// more than the best value, then update
// best/
if move_val > best_val {
row = i as i64;
col = j as i64;
best_val = move_val;
}
}
}
}
if row < 0 || col < 0 {
return None;
}
Some((row as usize, col as usize))
}
fn get_coordinates(s: &str) -> Option<(usize, usize)> {
let i = match s.parse::<usize>() {
Err(_) => {
return None;
}
Ok(n) => {
if n < 10 {
n
} else {
return None;
}
}
};
Some(((i - 1) / 3, (i - 1) % 3))
}
fn instructions() {
println!("Choose a cell numbered from 1 to 9 as below\n and play\n");
let a: Board = [['1', '2', '3'], ['4', '5', '6'], ['7', '8', '9']];
print_board(&a);
}
fn print_board(arr: &Board) {
let mut count : usize = 0;
for line in arr.iter() {
for elem in interleave(line, &['|', '|']) {
print!("{elem} ");
}
if count < 2 {
print!("\n-----------\n");
}
count += 1;
}
print!("\n\n")
}
fn has_moves_left(arr: &Board) -> bool {
for line in arr.iter() {
for elem in line.iter() {
if *elem == ' ' {
return true;
}
}
}
false
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment