diff --git a/.gitignore b/.gitignore index ea8c4bf..f8d4aa0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /target +/flamegraph.svg +/perf.data* diff --git a/src/board.rs b/src/board.rs index a743c63..678ea37 100644 --- a/src/board.rs +++ b/src/board.rs @@ -136,6 +136,9 @@ impl Board { /// Propegate piece captures originating from (i, j) /// DO NOT USE THIS ALONE, this should be called as a part of /// [`Board::place`] or [`Board::place_and_prop_unchecked`] + // TODO! this function is responsible for approx 64% of the time spent computing moves in `ComplexAgent` + // IDEAS: early-exit from each chain so we don't have to call `diag` (which allocs a lot and uses a lot of cycles) + // NOTE! got it down to 24.86% (61.1% decrease) with allocator optimizations fn propegate_from_dry(&self, i: usize, j: usize, starting_color: Piece) -> Vec<(usize, usize)> { // Create all chains from the piece being propegated from in `i` and `j` coordinates let (i_chain, j_chain) = ( @@ -143,21 +146,16 @@ impl Board { split_from(0, BOARD_SIZE - 1, j), ); - let mut chains: Vec> = i_chain - .into_iter() - .map(|range| range.into_iter().map(|i| (i, j)).collect()) - .collect(); + let mut chains: Vec> = Vec::with_capacity(8); - chains.extend( - j_chain - .into_iter() - .map(|range| range.into_iter().map(|j| (i, j)).collect()), - ); + chains.extend(i_chain.map(|range| range.into_iter().map(|i| (i, j)).collect())); + + chains.extend(j_chain.map(|range| range.into_iter().map(|j| (i, j)).collect())); // handle diagonals chains.extend(diag(i, j, 0, 0, BOARD_SIZE - 1, BOARD_SIZE - 1)); - let mut fill: Vec<(usize, usize)> = Vec::new(); + let mut fill: Vec<(usize, usize)> = Vec::with_capacity(chains.iter().map(Vec::len).sum()); for chain in chains { for (chain_length, &(new_i, new_j)) in chain.iter().enumerate() { @@ -167,15 +165,14 @@ impl Board { }; if piece == &starting_color { - // fill all opposite colors with this color - let Some(history) = chain.get(..chain_length) else { - break; - }; - - // fill all opposite colors with this color - for &(i_o, j_o) in history { - fill.push((i_o, j_o)); + // get history of this chain + if let Some(history) = chain.get(..chain_length) { + // fill all opposite colors with this color + for &(i_o, j_o) in history { + fill.push((i_o, j_o)); + } } + // either the other pieces were replaced, or this was an invalid chain, // in both cases, the loop needs to be breaked break; diff --git a/src/complexagent.rs b/src/complexagent.rs index 33cc296..db2929a 100644 --- a/src/complexagent.rs +++ b/src/complexagent.rs @@ -58,7 +58,6 @@ struct FutureMoves { max_depth: usize, /// Color w.r.t agent_color: Piece, - gc: usize, } impl FutureMoves { @@ -69,7 +68,6 @@ impl FutureMoves { current_depth: 0, max_depth, agent_color, - gc: 0, } } @@ -205,10 +203,7 @@ impl FutureMoves { if let Some(root) = new_root { self.current_root = Some(root); self.current_depth = self.max_depth - self.depth_of(Some(root)); - self.gc += 1; - if self.gc > 3 { - self.prune_unrelated(); - } + self.prune_unrelated(); true } else { false diff --git a/src/main.rs b/src/main.rs index 5c9bb14..6506e54 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,8 +11,8 @@ mod piece; fn main() { let player1 = complexagent::ComplexAgent::new(Piece::Black); // let player2 = complexagent::ComplexAgent::new(Piece::White); - let player2 = agent::ManualAgent::new(Piece::White); - // let player2 = agent::RandomAgent::new(Piece::White); + // let player2 = agent::ManualAgent::new(Piece::White); + let player2 = agent::RandomAgent::new(Piece::White); let mut game = Game::new(Box::new(player1), Box::new(player2)); game.game_loop(); } diff --git a/src/misc.rs b/src/misc.rs index 6573271..f998bbd 100644 --- a/src/misc.rs +++ b/src/misc.rs @@ -1,33 +1,30 @@ use std::{iter::Rev, ops::RangeInclusive}; -pub fn split_from(min: T, max: T, x: T) -> Vec> +pub fn split_from(min: T, max: T, x: T) -> [Vec; 2] where T: num::Integer + Copy, RangeInclusive: Iterator + DoubleEndedIterator, Rev>: Iterator, { + let mut output = [const { Vec::new() }; 2]; + // check that x is in range if min > x || x > max { - return Vec::new(); + return output; } - let mut output: Vec> = Vec::with_capacity(2); if x > min + T::one() { let x_lower = x - T::one(); - output.push((min..=x_lower).rev().collect()); - } else { - output.push(Vec::new()); + output[0] = (min..=x_lower).rev().collect(); } if x + T::one() < max { - output.push(((x + T::one())..=max).collect()); - } else { - output.push(Vec::new()); + output[1] = ((x + T::one())..=max).collect(); } output } -pub fn diag(i: T, j: T, min_i: T, min_j: T, max_i: T, max_j: T) -> Vec> +pub fn diag(i: T, j: T, min_i: T, min_j: T, max_i: T, max_j: T) -> [Vec<(T, T)>; 4] where T: num::Integer + Copy, RangeInclusive: Iterator + DoubleEndedIterator, @@ -36,25 +33,25 @@ where let i_chains = split_from(min_i, max_i, i); let j_chains = split_from(min_j, max_j, j); - vec![ + [ i_chains[0] - .clone() - .into_iter() + .iter() + .cloned() .zip(j_chains[0].clone()) .collect(), i_chains[1] - .clone() - .into_iter() + .iter() + .cloned() .zip(j_chains[1].clone()) .collect(), i_chains[1] - .clone() - .into_iter() + .iter() + .cloned() .zip(j_chains[0].clone()) .collect(), i_chains[0] - .clone() - .into_iter() + .iter() + .cloned() .zip(j_chains[1].clone()) .collect(), ] @@ -66,16 +63,16 @@ mod test { #[test] fn split_test() { - assert_eq!(split_from(0, 6, 2), vec![vec![1, 0], vec![3, 4, 5, 6]]); + assert_eq!(split_from(0, 6, 2), [vec![1, 0], vec![3, 4, 5, 6]]); - assert_eq!(split_from(0, 6, 0), vec![vec![], vec![1, 2, 3, 4, 5, 6]]); + assert_eq!(split_from(0, 6, 0), [vec![], vec![1, 2, 3, 4, 5, 6]]); - assert_eq!(split_from(0, 6, 6), vec![vec![5, 4, 3, 2, 1, 0], vec![]]); + assert_eq!(split_from(0, 6, 6), [vec![5, 4, 3, 2, 1, 0], vec![]]); // test out-of-bounds and also generics assert_eq!( split_from::(-1i16, 4i16, 10i16), - Vec::>::new() + [const { Vec::new() }; 2] ); } @@ -83,7 +80,7 @@ mod test { fn diag_test() { assert_eq!( diag(2, 3, 0, 0, 7, 7), - vec![ + [ vec![(1, 2), (0, 1)], vec![(3, 4), (4, 5), (5, 6), (6, 7)], vec![(3, 2), (4, 1), (5, 0)],