From 78055da14cea0dcb00bdd5faf9634d75a7b82a50 Mon Sep 17 00:00:00 2001 From: Bananasmoothii <45853225+bananasmoothii@users.noreply.github.com> Date: Wed, 18 Oct 2023 15:22:11 +0200 Subject: [PATCH] game is now always present --- src/bot.rs | 10 +++----- src/main.rs | 2 +- src/min_max.rs | 56 +++++++++++++++++++-------------------------- src/min_max/node.rs | 48 +++++++++----------------------------- 4 files changed, 38 insertions(+), 78 deletions(-) diff --git a/src/bot.rs b/src/bot.rs index 7fe2e3d..d7cf4aa 100644 --- a/src/bot.rs +++ b/src/bot.rs @@ -35,10 +35,7 @@ impl Bot { .try_into_child(play); if is_known_move { self.game_tree = Some(new_game_tree); - debug_assert!(self - .game_tree - .as_ref() - .is_some_and(|tree| tree.game().is_some())); + debug_assert!(self.game_tree.as_ref().is_some()); } else { // Here, new_game_tree is actually game_tree, the ownership was given back to us let result = new_game_tree @@ -52,7 +49,7 @@ impl Bot { println!("Unexpected move... Maybe you are a pure genius, or a pure idiot."); } let depth = new_game_tree.depth() + 1; - let game = new_game_tree.into_expect_game(); + let game = new_game_tree.into_game(); self.game_tree = Some(GameNode::new_root(game, self.player, depth)); } self.play_count += 1; @@ -86,7 +83,6 @@ impl Bot { println!("Ok I'm basically dead..."); } - debug_assert!(game_tree.game().is_some()); game_tree.game_state.get_last_play().1.unwrap() } @@ -95,6 +91,6 @@ impl Bot { } pub fn expect_game(&self) -> &G { - self.game_tree.as_ref().unwrap().expect_game() + self.game_tree.as_ref().unwrap().game() } } diff --git a/src/main.rs b/src/main.rs index 3ca423c..7df1a35 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ mod min_max; mod scalar; fn main() { - let max_depth = 10; + let max_depth = 11; let bot_vs_bot = false; let worst_case: u64 = (1..=max_depth).into_iter().map(|n| 7u64.pow(n)).sum(); diff --git a/src/min_max.rs b/src/min_max.rs index 3fc46d4..4627bee 100644 --- a/src/min_max.rs +++ b/src/min_max.rs @@ -113,7 +113,7 @@ impl GameNode { // WARNING: (maybe) destroying game here, for memory efficiency - let check_children = self.fill_children_and_destroy_game(now_playing, real_plays); + let check_children = self.fill_children_and_destroy_game(now_playing); let auto_destroy = AtomicBool::new(false); @@ -182,38 +182,30 @@ impl GameNode { } /// Returns true if childrens should be checked for win or draw, false if they were already checked - fn fill_children_and_destroy_game( - &mut self, - now_playing: ::Player, - real_plays: u32, - ) -> bool { - let game = self.game.as_ref().expect("game was removed too early"); - + fn fill_children_and_destroy_game(&mut self, now_playing: ::Player) -> bool { if self.children.is_empty() { + let game = &self.game; // if we take the game from us, we don't have to clone it, but we can do this only once - let take_game = self.depth() != real_plays; let possible_plays = game.possible_plays(); let possibilities = possible_plays.len(); let mut vec = Vec::with_capacity(possibilities); - if possibilities >= 2 || !take_game { - for i in 0..(possibilities - 1) { - let input_coord = possible_plays[i]; - let mut game = game.clone(); - game.play(now_playing, input_coord).unwrap(); // should not panic as input_coord is a possible play - vec.push(( - input_coord, - GameNode::new( - Some(game), - self.depth() + 1, - None, - PlayersTurn(now_playing.other(), Some(input_coord)), - ), - )); - } + for i in 0..(possibilities) { + let input_coord = possible_plays[i]; + let mut game = game.clone(); + game.play(now_playing, input_coord).unwrap(); // should not panic as input_coord is a possible play + vec.push(( + input_coord, + GameNode::new( + game, + self.depth() + 1, + None, + PlayersTurn(now_playing.other(), Some(input_coord)), + ), + )); } - if possibilities >= 1 { + /*if possibilities >= 1 { let input_coord = possible_plays[possibilities - 1]; let mut game = if take_game { // we don't want to destroy the game in the root node @@ -232,12 +224,11 @@ impl GameNode { PlayersTurn(now_playing.other(), Some(input_coord)), ), )); - } - + }*/ self.children = vec; true } else { - self.regenerate_children_games(); + //self.regenerate_children_games(); false } } @@ -251,7 +242,7 @@ impl GameNode { self.set_weight(Some(half_loose)); return true; } - if self.game.as_ref().unwrap().is_full() { + if self.game.is_full() { self.set_weight(Some(half_loose)); self.game_state = self.game_state.to_draw(); return true; @@ -264,7 +255,7 @@ impl GameNode { self.set_weight(Self::win_weight(winner, bot_player)); return true; } - let winner = self.game.as_ref().unwrap().get_winner(); + let winner = self.game.get_winner(); if let Some(winner) = winner { self.set_weight(Self::win_weight(winner, bot_player)); self.game_state = self.game_state.to_win(); @@ -291,8 +282,8 @@ impl GameNode { max_depth: u32, real_plays: u32, ) -> bool { - let game = self.game.as_ref().expect("game was removed too early"); if self.depth() >= max_depth + real_plays { + let game = &self.game; let weight = Some( if Self::USE_GAME_SCORE { // I know this is a constant, but this allows me to change it easily @@ -332,8 +323,7 @@ impl GameNode { break; } } - let mut best_child = best_child.expect("No children found"); - best_child.fill_play(self.game.unwrap()); + let best_child = best_child.expect("No children found"); best_child /* let mut best = G::Score::MIN(); diff --git a/src/min_max/node.rs b/src/min_max/node.rs index 4e19372..4fc0a57 100644 --- a/src/min_max/node.rs +++ b/src/min_max/node.rs @@ -9,17 +9,12 @@ pub struct GameNode { depth: u32, weight: Option, pub(super) children: Vec<(G::InputCoordinate, Self)>, - pub(super) game: Option, + pub(super) game: G, pub game_state: GameState, } impl GameNode { - pub fn new( - game: Option, - depth: u32, - weight: Option, - game_state: GameState, - ) -> Self { + pub fn new(game: G, depth: u32, weight: Option, game_state: GameState) -> Self { Self { depth, weight, @@ -30,23 +25,7 @@ impl GameNode { } pub fn new_root(game: G, starting_player: G::Player, depth: u32) -> Self { - GameNode::new(Some(game), depth, None, PlayersTurn(starting_player, None)) - } - - pub fn fill_play(&mut self, mut previous_game: G) { - if self.game.is_some() { - return; - } - let (last_player, last_play) = self.game_state.get_last_play(); - previous_game.play(last_player, last_play.unwrap()).unwrap(); - self.game = Some(previous_game); - } - - pub fn regenerate_children_games(&mut self) { - let game = self.game.as_ref().unwrap(); - for (_, child) in &mut self.children { - child.fill_play(game.clone()); - } + GameNode::new(game, depth, None, PlayersTurn(starting_player, None)) } /** @@ -56,7 +35,6 @@ impl GameNode { let mut new_children = Vec::with_capacity(self.children.len()); for (coord, mut child) in self.children.into_iter() { if coord == play { - child.fill_play(self.game.unwrap()); return (true, child); } new_children.push((coord, child)); @@ -77,20 +55,16 @@ impl GameNode { &self.children } - pub fn game(&self) -> Option<&G> { - self.game.as_ref() - } - - pub fn expect_game(&self) -> &G { - self.game.as_ref().expect("GameNode has no game") + pub fn game(&self) -> &G { + &self.game } pub fn expect_game_mut(&mut self) -> &mut G { - self.game.as_mut().expect("GameNode has no game") + &mut self.game } - pub fn into_expect_game(self) -> G { - self.game.expect("GameNode has no game") + pub fn into_game(self) -> G { + self.game } // Setters @@ -114,7 +88,7 @@ impl GameNode { return s; } let spaces = "| ".repeat((self.depth + 1) as usize); - for (input, child) in &self.children { + for (input, child) in self.children.iter() { s += &format!( "\n{spaces}({}) {input} scores {}", self.game_state, @@ -126,7 +100,7 @@ impl GameNode { fn count_depth(&self) -> u32 { let mut max_depth = 0; - for (_, child) in &self.children { + for (_, child) in self.children.iter() { let child_depth = child.count_depth() + 1; if child_depth > max_depth { max_depth = child_depth; @@ -145,7 +119,7 @@ impl Debug for GameNode { }; let mut s = format!("{weight_str}: "); let spaces = "| ".repeat((self.depth + 1) as usize); - for (input, child) in &self.children { + for (input, child) in self.children.iter() { s += &format!("\n{spaces}({}) {input} scores {child:?}", self.game_state); } f.write_str(&s)