Created
October 12, 2025 21:08
-
-
Save phoemur/32d59426259fcf89fa5f9c439b4be2a2 to your computer and use it in GitHub Desktop.
tictactoe
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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