Last active
March 26, 2023 12:56
-
-
Save n005/a5b7507c2d94b713e43bdbd4ad4ed195 to your computer and use it in GitHub Desktop.
TicTacToe in Rust with MinMax algo
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; | |
// Tic Tac Toe game in CLI | |
fn main() { | |
// Draw the board | |
let difficulty = ask_difficulty(); | |
let mut board = Board::new(difficulty); | |
board.print_board(); | |
let player1 = Player::new(1, 'X'); | |
let player2 = Player::new(2, 'O'); | |
let mut player = &player1; | |
let mut move_player: u8; | |
loop { | |
// Print current player | |
println!("Player {}'s turn", player.number); | |
// Ask the player's move (1-9) and return it | |
if player.number == 2 { | |
move_player = find_best_move(&mut board) + 1; | |
//move_player = ask_move(); | |
} else { | |
move_player = ask_move(); | |
} | |
// Clear the screen | |
print!("{esc}[2J{esc}[1;1H", esc = 27 as char); | |
// Print current move | |
println!("Player {}'s move: {}", player.number, move_player); | |
// Check if the move is valid (1-9) | |
if is_valid_move(move_player, &board) { | |
board.update_board(player, move_player); | |
board.print_board(); | |
if evaluate(&board) != 0 { | |
println!("Player {} wins!", player.number); | |
break; | |
} | |
if is_draw(&board) { | |
println!("Draw!"); | |
break; | |
} | |
if player.number == 1 { | |
player = &player2; | |
} else { | |
player = &player1; | |
} | |
} else { | |
board.print_board(); | |
println!("Invalid move!"); | |
} | |
} | |
} | |
// Ask the player's move (1-9) and return it | |
fn ask_move() -> u8 { | |
println!("Enter your move (1-9): "); | |
let mut input = String::new(); | |
io::stdin() | |
.read_line(&mut input) | |
.expect("Failed to read line"); | |
let input: u8 = input.trim().parse().expect("Please type a number!"); | |
input | |
} | |
// Ask the player difficulty (1-9) and return it | |
fn ask_difficulty() -> u8 { | |
println!("Enter difficulty (1-9): "); | |
let mut input = String::new(); | |
io::stdin() | |
.read_line(&mut input) | |
.expect("Failed to read line"); | |
let input: u8 = input.trim().parse().expect("Please type a number!"); | |
input | |
} | |
// Check if the move is valid (1-9) and verify if the vector is empty | |
fn is_valid_move(move_player: u8, board: &Board) -> bool { | |
if move_player > 0 && move_player < 10 && board.board[move_player as usize - 1] == ' ' { | |
return true; | |
} | |
false | |
} | |
struct Player { | |
number: i32, | |
symbol: char, | |
} | |
impl Player { | |
fn new(number: i32, symbol: char) -> Player { | |
Player { number, symbol } | |
} | |
} | |
struct Board { | |
board: Vec<char>, | |
difficulty: u8, | |
} | |
impl Board { | |
fn new(difficulty: u8) -> Board { | |
Board { | |
board: vec![' '; 9], | |
difficulty, | |
} | |
} | |
fn update_board(&mut self, player: &Player, move_player: u8) { | |
self.board[move_player as usize - 1] = player.symbol; | |
} | |
fn print_board(&self) { | |
println!( | |
" {} | {} | {} ", | |
self.board[0], self.board[1], self.board[2] | |
); | |
println!("---+---+---"); | |
println!( | |
" {} | {} | {} ", | |
self.board[3], self.board[4], self.board[5] | |
); | |
println!("---+---+---"); | |
println!( | |
" {} | {} | {} ", | |
self.board[6], self.board[7], self.board[8] | |
); | |
} | |
} | |
// Create a function to check if the game is draw | |
fn is_draw(board: &Board) -> bool { | |
for i in 0..9 { | |
if board.board[i] == ' ' { | |
return false; | |
} | |
} | |
true | |
} | |
// Create a quick evaluation function for the AI player | |
fn evaluate(board: &Board) -> i32 { | |
if board.board[0] != ' ' && board.board[0] == board.board[1] && board.board[1] == board.board[2] | |
{ | |
if board.board[0] == 'O' { | |
return 10; | |
} else { | |
return -10; | |
} | |
} | |
if board.board[3] != ' ' && board.board[3] == board.board[4] && board.board[4] == board.board[5] | |
{ | |
if board.board[3] == 'O' { | |
return 10; | |
} else { | |
return -10; | |
} | |
} | |
if board.board[6] != ' ' && board.board[6] == board.board[7] && board.board[7] == board.board[8] | |
{ | |
if board.board[6] == 'O' { | |
return 10; | |
} else { | |
return -10; | |
} | |
} | |
if board.board[0] != ' ' && board.board[0] == board.board[3] && board.board[3] == board.board[6] | |
{ | |
if board.board[0] == 'O' { | |
return 10; | |
} else { | |
return -10; | |
} | |
} | |
if board.board[1] != ' ' && board.board[1] == board.board[4] && board.board[4] == board.board[7] | |
{ | |
if board.board[1] == 'O' { | |
return 10; | |
} else { | |
return -10; | |
} | |
} | |
if board.board[2] != ' ' && board.board[2] == board.board[5] && board.board[5] == board.board[8] | |
{ | |
if board.board[2] == 'O' { | |
return 10; | |
} else { | |
return -10; | |
} | |
} | |
if board.board[0] != ' ' && board.board[0] == board.board[4] && board.board[4] == board.board[8] | |
{ | |
if board.board[0] == 'O' { | |
return 10; | |
} else { | |
return -10; | |
} | |
} | |
if board.board[2] != ' ' && board.board[2] == board.board[4] && board.board[4] == board.board[6] | |
{ | |
if board.board[2] == 'O' { | |
return 10; | |
} else { | |
return -10; | |
} | |
} | |
0 | |
} | |
// Create a function to find the best move for the AI player using minimax algorithm | |
fn find_best_move(board: &mut Board) -> u8 { | |
let mut best_val = -1000; | |
let mut best_move = 0; | |
for i in 0..9 { | |
if board.board[i] == ' ' { | |
board.board[i] = 'O'; | |
let move_val = minimax(board, 0, false); | |
board.board[i] = ' '; | |
if move_val > best_val { | |
best_move = i; | |
best_val = move_val; | |
} | |
} | |
} | |
best_move as u8 | |
} | |
// Create a function to implement the minimax algorithm | |
fn minimax(board: &mut Board, depth: i32, is_max: bool) -> i32 { | |
let score = evaluate(board); | |
if score == 10 { | |
return score - depth; | |
} | |
if score == -10 { | |
return score + depth; | |
} | |
if is_draw(board) { | |
return 0; | |
} | |
if depth == board.difficulty as i32 { | |
return 0; | |
} | |
if is_max { | |
let mut best = -1000; | |
for i in 0..9 { | |
if board.board[i] == ' ' { | |
board.board[i] = 'O'; | |
best = std::cmp::max(best, minimax(board, depth + 1, !is_max)); | |
board.board[i] = ' '; | |
} | |
} | |
best | |
} else { | |
let mut best = 1000; | |
for i in 0..9 { | |
if board.board[i] == ' ' { | |
board.board[i] = 'X'; | |
best = std::cmp::min(best, minimax(board, depth + 1, !is_max)); | |
board.board[i] = ' '; | |
} | |
} | |
best | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment