diff --git a/src/main.rs b/src/main.rs index 0a718a7..e975410 100644 --- a/src/main.rs +++ b/src/main.rs @@ -46,6 +46,7 @@ trait Displayer { } /// Represents the **Tic-Tac-Toe** board game +#[derive(Copy, Clone)] struct Board { // board array is reprensented as follow: // [ 0, 1, 2 ] @@ -224,6 +225,20 @@ impl Player { fn new(item: Item, is_bot: bool) -> Self { Player { item, is_bot } } + + fn as_bot() -> Self { + Player { + item: Item::Empty, + is_bot: true, + } + } + + fn as_human() -> Self { + Player { + item: Item::Empty, + is_bot: false, + } + } } impl Default for Player { @@ -239,10 +254,7 @@ impl Player { /// init players with the game type fn from_game_type(game_type: GameType) -> Vec { match game_type { - GameType::TwoPlayers => vec![ - Player::new(Item::Empty, false), - Player::new(Item::Empty, false), - ], + GameType::TwoPlayers => vec![Player::new(Item::X, false), Player::new(Item::O, false)], GameType::Bot => vec![ Player::new(Item::Empty, false), Player::new(Item::Empty, true), @@ -255,7 +267,7 @@ impl Player { /// Two `GameType` availables: /// * TwoPlayers: no bot /// * Bot: player against the bot -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq)] enum GameType { TwoPlayers, Bot, @@ -306,15 +318,15 @@ impl TicTacToe { self.game_type = game_type; } - fn set_players_item(&mut self, first_item: Item) -> Result<(), String> { + fn set_players_item(&mut self) -> Result<(), String> { if self.players.len() != 2 { return Err( "unable to initialize players item, 2 players must be initialized".to_string(), ); } - self.players[0].item = first_item; - self.players[1].item = first_item.next(); + self.players[0].item = Item::X; + self.players[1].item = Item::O; Ok(()) } @@ -334,8 +346,17 @@ impl TicTacToe { Ok(self.players[0]) } - /// set the item for the first player (the one that starts) - fn select_first_player_item(&mut self) { + /// if `GameType` is againt bot, you can select who starts + fn select_first_player(&mut self) { + if self.players.len() != 2 { + fatal("unable to initialize the game, 2 players must be set".to_string()); + } + + // the first player always starts whith 'X' + if self.game_type == GameType::TwoPlayers { + return; + } + let mut attempt = 0; loop { if attempt == 2 { @@ -343,19 +364,29 @@ impl TicTacToe { std::process::exit(1); } - let input = TicTacToe::get_user_input("first player, choose your item: ('X' or 'O')"); - let item_start = Item::try_from(input); - match item_start { - Ok(item) => { - match self.set_players_item(item) { - Ok(()) => break, - Err(e) => fatal(e), + let input = TicTacToe::get_user_input("who starts: ('B': bot, 'H': you as human)"); + let is_bot = self.players[0].is_bot; + + match &input[..] { + "B" | "b" => { + if !is_bot { + self.players.swap(0, 1) } - break; } - Err(e) => error(e.to_string()), + "H" | "h" => { + if is_bot { + self.players.swap(0, 1) + } + } + _ => { + error("bad input, please retry!".to_string()); + attempt += 1; + continue; + } } - attempt += 1; + + self.set_players_item(); + break; } } @@ -410,9 +441,22 @@ impl TicTacToe { Ok(available_indexes[0] + 1) } + /// minimax algorithm used by the bot player to find the best move (minimizing the loses) + fn minimax(&self, board: Option, player: Player, depth: usize) -> usize { + let mut board = match board { + Some(b) => b, + None => self.board.clone(), + }; + let availables_indexes = board.get_available_indexes(); + for index in availables_indexes { + board.set_item(player.item, index); + } + 0 + } + fn run(&mut self) { self.select_game_type(); - self.select_first_player_item(); + self.select_first_player(); loop { if self.board.is_full() {