more iterator stuff

This commit is contained in:
Simon Gardling 2025-02-11 00:27:29 -05:00
parent 3e2ab2fd32
commit 657bb967a0
Signed by: titaniumtown
GPG Key ID: 9AB28AC10ECE533D
4 changed files with 80 additions and 56 deletions

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
misc::{diag, split_from}, misc::{diag_raw, split_from},
piece::Piece, piece::Piece,
}; };
use std::{cmp::Ordering, fmt}; use std::{cmp::Ordering, fmt};
@ -148,17 +148,19 @@ impl Board {
chains.extend( chains.extend(
i_chain i_chain
.clone()
.map(|range| range.map(move |i| (i, j))) .map(|range| range.map(move |i| (i, j)))
.map(Iterator::collect), .map(Iterator::collect),
); );
chains.extend( chains.extend(
j_chain j_chain
.clone()
.map(|range| range.map(move |j| (i, j))) .map(|range| range.map(move |j| (i, j)))
.map(Iterator::collect), .map(Iterator::collect),
); );
// handle diagonals // 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 // 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()); let mut fill: Vec<(usize, usize)> = Vec::with_capacity((BOARD_SIZE - 2) * chains.len());
@ -214,6 +216,8 @@ impl Board {
match white_score.cmp(&black_score) { match white_score.cmp(&black_score) {
Ordering::Greater => Some(Piece::White), // White win Ordering::Greater => Some(Piece::White), // White win
Ordering::Less => Some(Piece::Black), // Black win Ordering::Less => Some(Piece::Black), // Black win
// TODO! this will end up being parsed the same as a "no winner", it should be a seperate type
Ordering::Equal => None, // Tie Ordering::Equal => None, // Tie
} }
} }

View File

@ -56,6 +56,7 @@ struct FutureMoves {
current_root: Option<usize>, current_root: Option<usize>,
current_depth: usize, current_depth: usize,
max_depth: usize, max_depth: usize,
/// Color w.r.t /// Color w.r.t
agent_color: Piece, agent_color: Piece,
} }
@ -80,22 +81,30 @@ impl FutureMoves {
self.extend_layers(root_nodes, color, self.max_depth); self.extend_layers(root_nodes, color, self.max_depth);
} }
fn extend_layers(&mut self, nodes: Vec<usize>, color: Piece, remaining_depth: usize) { fn extend_layers(
&mut self,
nodes: impl Iterator<Item = usize>,
color: Piece,
remaining_depth: usize,
) {
if remaining_depth == 0 { if remaining_depth == 0 {
return; return;
} }
let next_color = !color; let next_color = !color;
let mut next_nodes = Vec::new();
for node_idx in nodes { let next_nodes: Vec<usize> = nodes
let board = &self.arena[node_idx].board.clone(); .flat_map(|node_idx| {
let children = self.generate_children(Some(node_idx), board, next_color); self.generate_children(
next_nodes.extend(children); Some(node_idx),
} &self.arena[node_idx].board.clone(),
next_color,
)
})
.collect();
self.current_depth += 1; 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( fn generate_children(
@ -103,31 +112,28 @@ impl FutureMoves {
parent: Option<usize>, parent: Option<usize>,
board: &Board, board: &Board,
color: Piece, color: Piece,
) -> Vec<usize> { ) -> impl Iterator<Item = usize> {
let parent_idx = parent;
let start_idx = self.arena.len(); let start_idx = self.arena.len();
self.arena.extend( self.arena.extend(
board board
.possible_moves(color) .possible_moves(color)
.flat_map(|(i, j)| board.what_if(i, j, color).map(|x| (i, j, x))) .flat_map(|(i, j)| board.what_if(i, j, color).map(|x| (i, j, x)))
.map(|(i, j, (new_board, captured))| { .map(|(i, j, (new_board, captured))| Move {
let winner = new_board.game_winner(color);
Move {
i, i,
j, j,
captured, captured,
color, color,
board: new_board, board: new_board,
winner, winner: new_board.game_winner(color),
parent, parent,
children: Vec::new(), children: Vec::new(),
value: 0, value: 0,
}
}), }),
); );
let new_indices: Vec<usize> = (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()); self.arena[parent_idx].children.extend(new_indices.clone());
} }
@ -144,10 +150,8 @@ impl FutureMoves {
.collect(); .collect();
for idx in nodes_at_depth { for idx in nodes_at_depth {
let self_value = { let self_value = self.arena[idx]
let node = &self.arena[idx]; .compute_self_value(self.agent_color, self.current_depth - depth + 1);
node.compute_self_value(self.agent_color, self.current_depth - depth + 1)
};
let children_value = self.arena[idx] let children_value = self.arena[idx]
.children .children
@ -191,18 +195,18 @@ impl FutureMoves {
} }
pub fn update_root(&mut self, i: usize, j: usize) -> bool { pub fn update_root(&mut self, i: usize, j: usize) -> bool {
let new_root = self.arena.iter().enumerate().find_map(|(idx, node)| { self.arena
.iter()
.enumerate()
.find_map(|(idx, node)| {
(node.parent == self.current_root && node.i == i && node.j == j).then_some(idx) (node.parent == self.current_root && node.i == i && node.j == j).then_some(idx)
}); })
.inspect(|&root| {
if let Some(root) = new_root {
self.current_root = Some(root); self.current_root = Some(root);
self.current_depth = self.max_depth - self.depth_of(Some(root)); self.current_depth = self.max_depth - self.depth_of(Some(root));
self.prune_unrelated(); self.prune_unrelated();
true })
} else { .is_some()
false
}
} }
fn prune_unrelated(&mut self) { fn prune_unrelated(&mut self) {
@ -215,19 +219,26 @@ impl FutureMoves {
// traverse children of the current root // traverse children of the current root
while let Some(idx) = stack.pop() { while let Some(idx) = stack.pop() {
retain[idx] = true; 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 new_arena = Vec::with_capacity(self.arena.len());
let mut index_map = vec![None; 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() { for (old_idx, keep) in retain.iter().enumerate().rev() {
let mut node = self.arena.remove(old_idx); let mut node = self.arena.remove(old_idx);
if *keep { if *keep {
index_map[old_idx] = Some(new_arena.len()); index_map[old_idx] = Some(new_arena.len());
node.parent = node.parent.and_then(|p| index_map[p]); 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); new_arena.push(node);
} }
} }
@ -244,7 +255,7 @@ pub struct ComplexAgent {
impl ComplexAgent { impl ComplexAgent {
pub fn new(color: Piece) -> Self { pub fn new(color: Piece) -> Self {
const MAX_DEPTH: usize = 7; const MAX_DEPTH: usize = 6;
Self { Self {
color, color,
future_moves: FutureMoves::new(color, MAX_DEPTH), future_moves: FutureMoves::new(color, MAX_DEPTH),
@ -259,12 +270,9 @@ impl Agent for ComplexAgent {
println!("# of moves stored: {}", self.future_moves.arena.len()); 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); self.future_moves.update_root(i, j);
Some((i, j)) })
} else {
None
}
} }
fn name(&self) -> &'static str { fn name(&self) -> &'static str {

View File

@ -11,8 +11,8 @@ mod piece;
fn main() { fn main() {
let player1 = complexagent::ComplexAgent::new(Piece::Black); let player1 = complexagent::ComplexAgent::new(Piece::Black);
// let player2 = complexagent::ComplexAgent::new(Piece::White); // let player2 = complexagent::ComplexAgent::new(Piece::White);
// let player2 = agent::ManualAgent::new(Piece::White); let player2 = agent::ManualAgent::new(Piece::White);
let player2 = agent::RandomAgent::new(Piece::White); // let player2 = agent::RandomAgent::new(Piece::White);
let mut game = Game::new(Box::new(player1), Box::new(player2)); let mut game = Game::new(Box::new(player1), Box::new(player2));
game.game_loop(); game.game_loop();
} }

View File

@ -25,7 +25,7 @@ where
] ]
} }
pub fn diag<T>( pub fn diag_test_helper<T>(
i: T, i: T,
j: T, j: T,
min_i: T, min_i: T,
@ -40,7 +40,19 @@ where
{ {
let i_chains = split_from(min_i, max_i, i); let i_chains = split_from(min_i, max_i, i);
let j_chains = split_from(min_j, max_j, j); let j_chains = split_from(min_j, max_j, j);
diag_raw(i_chains, j_chains)
}
pub fn diag_raw<T>(
i_chains: [impl Iterator<Item = T> + Clone; 2],
j_chains: [impl Iterator<Item = T> + Clone; 2],
) -> [impl Iterator<Item = (T, T)> + Clone; 4]
where
T: num::Integer + Copy,
RangeInclusive<T>: Iterator<Item = T> + DoubleEndedIterator,
Rev<RangeInclusive<T>>: Iterator<Item = T>,
{
[(0, 0), (1, 1), (1, 0), (0, 1)].map(move |(a, b)| i_chains[a].clone().zip(j_chains[b].clone())) [(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] #[test]
fn diag_test() { fn diag_test() {
assert_eq!( assert_eq!(
diag(2, 3, 0, 0, 7, 7).map(Iterator::collect::<Vec<(usize, usize)>>), diag_test_helper(2, 3, 0, 0, 7, 7).map(Iterator::collect::<Vec<(usize, usize)>>),
[ [
vec![(1, 2), (0, 1)], vec![(1, 2), (0, 1)],
vec![(3, 4), (4, 5), (5, 6), (6, 7)], vec![(3, 4), (4, 5), (5, 6), (6, 7)],