From c3c07fcb283b9d380d3f4fcbc2c173a5614aaaca Mon Sep 17 00:00:00 2001 From: Simon Gardling Date: Mon, 28 Apr 2025 01:12:15 -0400 Subject: [PATCH] probabilistic progress --- src/elo.rs | 40 +++++++++++++++++++------------------ src/logic/future_moves.rs | 27 +++++++------------------ src/logic/move.rs | 15 +++++++------- src/logic/mvs.rs | 42 ++++++++++++++++++++------------------- 4 files changed, 58 insertions(+), 66 deletions(-) diff --git a/src/elo.rs b/src/elo.rs index b96077e..1ea378a 100644 --- a/src/elo.rs +++ b/src/elo.rs @@ -29,7 +29,7 @@ pub fn run() { children_eval_method: Default::default(), }; - let configs = [4, 5, 6] + let configs = [6] .into_iter() .map(move |d| FutureMoveConfig { max_depth: d, @@ -119,14 +119,17 @@ pub fn run() { ) }) .collect(); - vec.push(( - "RandomAgent".to_string(), - Box::new(move |piece| Box::new(RandomAgent::new(piece))), - )); + + if false { + vec.push(( + "RandomAgent".to_string(), + Box::new(move |piece| Box::new(RandomAgent::new(piece))), + )); + } let mut arena = PlayerArena::new(vec); - arena.prop_arena(1000); + arena.prop_arena(100); println!("{}", arena); } @@ -172,7 +175,10 @@ impl PlayerArena { .map(|&(i, j)| { ( (i, j), - Self::create_agents(&self.players[i].1, &self.players[j].1), + ( + (&self.players[i].1)(Piece::Black), + (&self.players[j].1)(Piece::White), + ), ) }) .collect::>(); @@ -221,14 +227,16 @@ impl PlayerArena { while let Ok((i, j, o)) = receiver.recv() { self.process_outcome(i, j, &o); - received_num += 1; - p.inc(1); - term.clear_last_lines(self.players.len()) - .expect("unable to clear prev lines"); + if received_num > 0 { + term.clear_last_lines(self.players.len()) + .expect("unable to clear prev lines"); + } term.write_str(format!("{}", self).as_str()) .expect("unable to write leaderboard"); + received_num += 1; + p.inc(1); // break if all pairs were recieved if received_num == num { break; @@ -265,19 +273,13 @@ impl PlayerArena { self.players[player2].2 = np2; } - fn create_agents( - player_1_fn: &AgentMaker, - player_2_fn: &AgentMaker, - ) -> (Box, Box) { - (player_1_fn(Piece::Black), player_2_fn(Piece::White)) - } - fn play_two_inner(player_1: Box, player_2: Box) -> Outcomes { let result = GameInner::new( player_1, player_2, false, - Board::random(rand::random_range(1..=15)), + // Board::random(rand::random_range(20..=30)), + Board::STARTING_POSITION, ) .expect("unable to create game") .loop_until_result(); diff --git a/src/logic/future_moves.rs b/src/logic/future_moves.rs index ac03c31..f6e9849 100644 --- a/src/logic/future_moves.rs +++ b/src/logic/future_moves.rs @@ -315,23 +315,14 @@ impl FutureMoves { // get best (for the adversary) enemy play // this assumes the adversary is playing optimally - children_values - .into_iter() - .min_by_key(|x| x.value) - .map(|x| x.value) + children_values.into_iter().map(|x| x.value).min() } else { - children_values - .into_iter() - .max_by_key(|x| x.value) - .map(|x| x.value) + children_values.into_iter().map(|x| x.value).max() } .unwrap_or(0); - // we use `depth` and divided `self_value` by it, idk if this is worth it - // we should really setup some sort of ELO rating for each commit, playing them against - // each other or something, could be cool to benchmark these more subjective things, not - // just performance (cycles/time wise) + self.arena[idx].value.value = - self.arena[idx].self_value as i32 + child_value; + self.arena[idx].self_value.value as i32 + child_value; } ChildrenEvalMethod::MinMaxProb => { let child_value = if self.arena[idx].color == self.agent_color { @@ -344,16 +335,12 @@ impl FutureMoves { } .cloned() .unwrap_or(Default::default()); - // we use `depth` and divided `self_value` by it, idk if this is worth it - // we should really setup some sort of ELO rating for each commit, playing them against - // each other or something, could be cool to benchmark these more subjective things, not - // just performance (cycles/time wise) + + self.arena[idx].value = self.arena[idx].self_value; self.arena[idx] .value .populate_self_from_children(&children_values); - - self.arena[idx].value.value = - self.arena[idx].self_value as i32 + child_value.value; + self.arena[idx].value.value += child_value.value; } } } diff --git a/src/logic/move.rs b/src/logic/move.rs index 6c83e76..2e3d085 100644 --- a/src/logic/move.rs +++ b/src/logic/move.rs @@ -29,7 +29,7 @@ pub struct Move { pub value: MoveValueStats, /// What is the inherit value of this move (not including children) - pub self_value: i16, + pub self_value: MoveValueStats, /// Which color made a move on this move? pub color: Piece, @@ -58,28 +58,29 @@ impl Move { value: Default::default(), color, is_trimmed: false, - self_value: 0, + self_value: Default::default(), }; // set wins/losses values appropriately match m.winner { Winner::Player(piece) => { if piece == agent_color { - m.value.set_state(Some(MVSGameState::Win)); + m.self_value.set_state(Some(MVSGameState::Win)); } else { - m.value.set_state(Some(MVSGameState::Loss)); + m.self_value.set_state(Some(MVSGameState::Loss)); } } Winner::Tie => { - m.value.set_state(Some(MVSGameState::Tie)); + m.self_value.set_state(Some(MVSGameState::Tie)); } Winner::None => {} } if mvc.self_value_raw { - m.self_value = const { BoardValueMap::weighted() }.board_value(&board, agent_color); + m.self_value.value = + const { BoardValueMap::weighted() }.board_value(&board, agent_color) as i32; } else { - m.self_value = m.compute_self_value(agent_color, &board, mvc); + m.self_value.value = m.compute_self_value(agent_color, &board, mvc) as i32; } m } diff --git a/src/logic/mvs.rs b/src/logic/mvs.rs index 2c84788..7ad4ea0 100644 --- a/src/logic/mvs.rs +++ b/src/logic/mvs.rs @@ -8,48 +8,44 @@ pub enum MVSGameState { Loss = -1, } -#[derive(Clone, Copy, Debug, Allocative, PartialEq, Eq, Default)] +#[derive(Clone, Copy, Debug, Allocative, PartialEq, Eq, Default, PartialOrd)] pub struct MoveValueStats { state: Option, wins: u16, losses: u16, + ties: u16, pub value: i32, } impl MoveValueStats { #[cfg(test)] - pub const fn new_from_wins_losses(wins: u16, losses: u16) -> Self { + pub fn new_from_wins_losses(wins: u16, losses: u16) -> Self { Self { - state: None, wins, losses, - value: 0, + ..Default::default() } } #[cfg(test)] - pub const fn new_from_value(value: i32) -> Self { + pub fn new_from_value(value: i32) -> Self { Self { - state: None, - wins: 0, - losses: 0, value, + ..Default::default() } } #[cfg(test)] - pub const fn new_from_state(state: Option) -> Self { + pub fn new_from_state(state: Option) -> Self { Self { state, - wins: 0, - losses: 0, - value: 0, + ..Default::default() } } fn chance_win(&self) -> Option { - let sum = self.losses + self.wins; - if sum == 0 { + let sum = self.losses + self.wins + self.ties; + if 20 > sum { return None; } Some(self.wins as f32 / sum as f32) @@ -70,12 +66,11 @@ impl MoveValueStats { .iter() .filter(|x| x.state == Some(MVSGameState::Loss)) .count() as u16; - } -} - -impl PartialOrd for MoveValueStats { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) + self.ties = others.iter().map(|x| x.ties).sum::() + + others + .iter() + .filter(|x| x.state == Some(MVSGameState::Tie)) + .count() as u16; } } @@ -122,4 +117,11 @@ mod tests { let two = MoveValueStats::new_from_state(Some(MVSGameState::Win)); assert!(one < two); } + + #[test] + fn two_prob_zero() { + let one = MoveValueStats::new_from_wins_losses(10, 0); + let two = MoveValueStats::new_from_wins_losses(0, 6); + assert!(one > two); + } }