From 657bb967a0c73e661edd63978c40f2a9ad1700e1 Mon Sep 17 00:00:00 2001 From: Simon Gardling Date: Tue, 11 Feb 2025 00:27:29 -0500 Subject: [PATCH] more iterator stuff --- src/board.rs | 10 +++-- src/complexagent.rs | 106 ++++++++++++++++++++++++-------------------- src/main.rs | 4 +- src/misc.rs | 16 ++++++- 4 files changed, 80 insertions(+), 56 deletions(-) diff --git a/src/board.rs b/src/board.rs index 54727ae..a655e63 100644 --- a/src/board.rs +++ b/src/board.rs @@ -1,5 +1,5 @@ use crate::{ - misc::{diag, split_from}, + misc::{diag_raw, split_from}, piece::Piece, }; use std::{cmp::Ordering, fmt}; @@ -148,17 +148,19 @@ impl Board { chains.extend( i_chain + .clone() .map(|range| range.map(move |i| (i, j))) .map(Iterator::collect), ); chains.extend( j_chain + .clone() .map(|range| range.map(move |j| (i, j))) .map(Iterator::collect), ); // handle diagonals - chains.extend(diag(i, j, 0, 0, BOARD_SIZE - 1, BOARD_SIZE - 1).map(Iterator::collect)); + chains.extend(diag_raw(i_chain, j_chain).map(Iterator::collect)); // Longest chain is (BOARD_SIZE - 2) as there needs to be the two pieces containing it let mut fill: Vec<(usize, usize)> = Vec::with_capacity((BOARD_SIZE - 2) * chains.len()); @@ -214,7 +216,9 @@ impl Board { match white_score.cmp(&black_score) { Ordering::Greater => Some(Piece::White), // White win Ordering::Less => Some(Piece::Black), // Black win - Ordering::Equal => None, // Tie + + // TODO! this will end up being parsed the same as a "no winner", it should be a seperate type + Ordering::Equal => None, // Tie } } } diff --git a/src/complexagent.rs b/src/complexagent.rs index 77a0951..74205e8 100644 --- a/src/complexagent.rs +++ b/src/complexagent.rs @@ -56,6 +56,7 @@ struct FutureMoves { current_root: Option, current_depth: usize, max_depth: usize, + /// Color w.r.t agent_color: Piece, } @@ -80,22 +81,30 @@ impl FutureMoves { self.extend_layers(root_nodes, color, self.max_depth); } - fn extend_layers(&mut self, nodes: Vec, color: Piece, remaining_depth: usize) { + fn extend_layers( + &mut self, + nodes: impl Iterator, + color: Piece, + remaining_depth: usize, + ) { if remaining_depth == 0 { return; } let next_color = !color; - let mut next_nodes = Vec::new(); - for node_idx in nodes { - let board = &self.arena[node_idx].board.clone(); - let children = self.generate_children(Some(node_idx), board, next_color); - next_nodes.extend(children); - } + let next_nodes: Vec = nodes + .flat_map(|node_idx| { + self.generate_children( + Some(node_idx), + &self.arena[node_idx].board.clone(), + next_color, + ) + }) + .collect(); self.current_depth += 1; - self.extend_layers(next_nodes, next_color, remaining_depth - 1); + self.extend_layers(next_nodes.into_iter(), next_color, remaining_depth - 1); } fn generate_children( @@ -103,31 +112,28 @@ impl FutureMoves { parent: Option, board: &Board, color: Piece, - ) -> Vec { - let parent_idx = parent; + ) -> impl Iterator { let start_idx = self.arena.len(); self.arena.extend( board .possible_moves(color) .flat_map(|(i, j)| board.what_if(i, j, color).map(|x| (i, j, x))) - .map(|(i, j, (new_board, captured))| { - let winner = new_board.game_winner(color); - Move { - i, - j, - captured, - color, - board: new_board, - winner, - parent, - children: Vec::new(), - value: 0, - } + .map(|(i, j, (new_board, captured))| Move { + i, + j, + captured, + color, + board: new_board, + winner: new_board.game_winner(color), + parent, + children: Vec::new(), + value: 0, }), ); - let new_indices: Vec = (start_idx..self.arena.len()).collect(); - if let Some(parent_idx) = parent_idx { + let new_indices = start_idx..self.arena.len(); + + if let Some(parent_idx) = parent { self.arena[parent_idx].children.extend(new_indices.clone()); } @@ -144,10 +150,8 @@ impl FutureMoves { .collect(); for idx in nodes_at_depth { - let self_value = { - let node = &self.arena[idx]; - node.compute_self_value(self.agent_color, self.current_depth - depth + 1) - }; + let self_value = self.arena[idx] + .compute_self_value(self.agent_color, self.current_depth - depth + 1); let children_value = self.arena[idx] .children @@ -191,18 +195,18 @@ impl FutureMoves { } pub fn update_root(&mut self, i: usize, j: usize) -> bool { - let new_root = self.arena.iter().enumerate().find_map(|(idx, node)| { - (node.parent == self.current_root && node.i == i && node.j == j).then_some(idx) - }); - - if let Some(root) = new_root { - self.current_root = Some(root); - self.current_depth = self.max_depth - self.depth_of(Some(root)); - self.prune_unrelated(); - true - } else { - false - } + self.arena + .iter() + .enumerate() + .find_map(|(idx, node)| { + (node.parent == self.current_root && node.i == i && node.j == j).then_some(idx) + }) + .inspect(|&root| { + self.current_root = Some(root); + self.current_depth = self.max_depth - self.depth_of(Some(root)); + self.prune_unrelated(); + }) + .is_some() } fn prune_unrelated(&mut self) { @@ -215,19 +219,26 @@ impl FutureMoves { // traverse children of the current root while let Some(idx) = stack.pop() { retain[idx] = true; - stack.extend(self.arena[idx].children.iter().copied()); + stack.extend(self.arena[idx].children.iter()); } let mut new_arena = Vec::with_capacity(self.arena.len()); let mut index_map = vec![None; self.arena.len()]; + // PERF: reverse iterator to avoid unneeded `memcpy`s when deconstructing the Arena for (old_idx, keep) in retain.iter().enumerate().rev() { let mut node = self.arena.remove(old_idx); if *keep { index_map[old_idx] = Some(new_arena.len()); node.parent = node.parent.and_then(|p| index_map[p]); - node.children = node.children.iter().filter_map(|&c| index_map[c]).collect(); + + node.children = node + .children + .into_iter() + .filter_map(|c| index_map[c]) + .collect(); + new_arena.push(node); } } @@ -244,7 +255,7 @@ pub struct ComplexAgent { impl ComplexAgent { pub fn new(color: Piece) -> Self { - const MAX_DEPTH: usize = 7; + const MAX_DEPTH: usize = 6; Self { color, future_moves: FutureMoves::new(color, MAX_DEPTH), @@ -259,12 +270,9 @@ impl Agent for ComplexAgent { println!("# of moves stored: {}", self.future_moves.arena.len()); - if let Some((i, j)) = self.future_moves.best_move() { + self.future_moves.best_move().inspect(|&(i, j)| { self.future_moves.update_root(i, j); - Some((i, j)) - } else { - None - } + }) } fn name(&self) -> &'static str { diff --git a/src/main.rs b/src/main.rs index 6506e54..5c9bb14 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 a379381..e330e3b 100644 --- a/src/misc.rs +++ b/src/misc.rs @@ -25,7 +25,7 @@ where ] } -pub fn diag( +pub fn diag_test_helper( i: T, j: T, min_i: T, @@ -40,7 +40,19 @@ where { let i_chains = split_from(min_i, max_i, i); let j_chains = split_from(min_j, max_j, j); + diag_raw(i_chains, j_chains) +} +pub fn diag_raw( + i_chains: [impl Iterator + Clone; 2], + + j_chains: [impl Iterator + Clone; 2], +) -> [impl Iterator + Clone; 4] +where + T: num::Integer + Copy, + RangeInclusive: Iterator + DoubleEndedIterator, + Rev>: Iterator, +{ [(0, 0), (1, 1), (1, 0), (0, 1)].map(move |(a, b)| i_chains[a].clone().zip(j_chains[b].clone())) } @@ -75,7 +87,7 @@ mod test { #[test] fn diag_test() { assert_eq!( - diag(2, 3, 0, 0, 7, 7).map(Iterator::collect::>), + diag_test_helper(2, 3, 0, 0, 7, 7).map(Iterator::collect::>), [ vec![(1, 2), (0, 1)], vec![(3, 4), (4, 5), (5, 6), (6, 7)],