Skip to content

Commit

Permalink
game is now always present
Browse files Browse the repository at this point in the history
  • Loading branch information
bananasmoothii committed Oct 18, 2023
1 parent 69fe6e7 commit 78055da
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 78 deletions.
10 changes: 3 additions & 7 deletions src/bot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,7 @@ impl<G: Game> Bot<G> {
.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
Expand All @@ -52,7 +49,7 @@ impl<G: Game> Bot<G> {
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;
Expand Down Expand Up @@ -86,7 +83,6 @@ impl<G: Game> Bot<G> {
println!("Ok I'm basically dead...");
}

debug_assert!(game_tree.game().is_some());
game_tree.game_state.get_last_play().1.unwrap()
}

Expand All @@ -95,6 +91,6 @@ impl<G: Game> Bot<G> {
}

pub fn expect_game(&self) -> &G {
self.game_tree.as_ref().unwrap().expect_game()
self.game_tree.as_ref().unwrap().game()
}
}
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
56 changes: 23 additions & 33 deletions src/min_max.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ impl<G: Game> GameNode<G> {

// 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);

Expand Down Expand Up @@ -182,38 +182,30 @@ impl<G: Game> GameNode<G> {
}

/// 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: <G as Game>::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: <G as Game>::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
Expand All @@ -232,12 +224,11 @@ impl<G: Game> GameNode<G> {
PlayersTurn(now_playing.other(), Some(input_coord)),
),
));
}

}*/
self.children = vec;
true
} else {
self.regenerate_children_games();
//self.regenerate_children_games();
false
}
}
Expand All @@ -251,7 +242,7 @@ impl<G: Game> GameNode<G> {
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;
Expand All @@ -264,7 +255,7 @@ impl<G: Game> GameNode<G> {
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();
Expand All @@ -291,8 +282,8 @@ impl<G: Game> GameNode<G> {
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
Expand Down Expand Up @@ -332,8 +323,7 @@ impl<G: Game> GameNode<G> {
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();
Expand Down
48 changes: 11 additions & 37 deletions src/min_max/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,12 @@ pub struct GameNode<G: Game> {
depth: u32,
weight: Option<G::Score>,
pub(super) children: Vec<(G::InputCoordinate, Self)>,
pub(super) game: Option<G>,
pub(super) game: G,
pub game_state: GameState<G>,
}

impl<G: Game> GameNode<G> {
pub fn new(
game: Option<G>,
depth: u32,
weight: Option<G::Score>,
game_state: GameState<G>,
) -> Self {
pub fn new(game: G, depth: u32, weight: Option<G::Score>, game_state: GameState<G>) -> Self {
Self {
depth,
weight,
Expand All @@ -30,23 +25,7 @@ impl<G: Game> GameNode<G> {
}

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))
}

/**
Expand All @@ -56,7 +35,6 @@ impl<G: Game> GameNode<G> {
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));
Expand All @@ -77,20 +55,16 @@ impl<G: Game> GameNode<G> {
&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
Expand All @@ -114,7 +88,7 @@ impl<G: Game> GameNode<G> {
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,
Expand All @@ -126,7 +100,7 @@ impl<G: Game> GameNode<G> {

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;
Expand All @@ -145,7 +119,7 @@ impl<G: Game> Debug for GameNode<G> {
};
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)
Expand Down

0 comments on commit 78055da

Please sign in to comment.