Skip to content

Instantly share code, notes, and snippets.

@n005
Last active March 26, 2023 12:56
Show Gist options
  • Save n005/a5b7507c2d94b713e43bdbd4ad4ed195 to your computer and use it in GitHub Desktop.
Save n005/a5b7507c2d94b713e43bdbd4ad4ed195 to your computer and use it in GitHub Desktop.
TicTacToe in Rust with MinMax algo
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