add Move::lazy_children
This commit is contained in:
parent
7ec23f1bab
commit
ad28713775
@ -29,6 +29,8 @@ struct Move {
|
|||||||
value: i64,
|
value: i64,
|
||||||
|
|
||||||
color: Piece,
|
color: Piece,
|
||||||
|
|
||||||
|
lazy_children: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Move {
|
impl Move {
|
||||||
@ -82,25 +84,39 @@ impl FutureMoves {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LAZY_EXPIRE: usize = 6;
|
||||||
|
|
||||||
/// Generate children for all children of `nodes`
|
/// Generate children for all children of `nodes`
|
||||||
fn extend_layers(&mut self) {
|
fn extend_layers(&mut self) {
|
||||||
let mut next_nodes: Vec<usize> = (0..self.arena.len())
|
let mut next_nodes: Vec<usize> = (0..self.arena.len())
|
||||||
.filter(|&idx| self.arena[idx].children.is_empty())
|
// we want to select all nodes that don't have children, or are lazy (need to maybe be regenerated)
|
||||||
|
.filter(|&idx| self.arena[idx].children.is_empty() || self.arena[idx].lazy_children)
|
||||||
.filter(|&idx| self.is_connected_to_root(idx)) // put here so this will not extend needlessly before prunes
|
.filter(|&idx| self.is_connected_to_root(idx)) // put here so this will not extend needlessly before prunes
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
for _ in self.current_depth..=self.max_depth {
|
for i in self.current_depth..=self.max_depth {
|
||||||
// TODO! use `i` in order to prune along-the-way
|
|
||||||
// i.e, every 4 moves of color `self.agent_color`, do a pruning step,
|
|
||||||
// only keeping the top [`Move`]
|
|
||||||
|
|
||||||
next_nodes = next_nodes
|
next_nodes = next_nodes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.progress_with_style(
|
.progress_with_style(
|
||||||
ProgressStyle::with_template("Generating children: ({pos}/{len}) {per_sec}")
|
ProgressStyle::with_template(&format!(
|
||||||
|
"Generating children (depth: {}/{}): ({{pos}}/{{len}}) {{per_sec}}",
|
||||||
|
i, self.max_depth
|
||||||
|
))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
)
|
)
|
||||||
.flat_map(|node_idx| self.generate_children(node_idx))
|
.flat_map(|node_idx| {
|
||||||
|
self.generate_children(
|
||||||
|
node_idx,
|
||||||
|
if self.arena[node_idx].lazy_children
|
||||||
|
&& self.depth_of(node_idx) - 1 < Self::LAZY_EXPIRE
|
||||||
|
{
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
// this is a non-lazy_children child, should it be lazy?
|
||||||
|
i > Self::LAZY_EXPIRE
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
@ -124,17 +140,23 @@ impl FutureMoves {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates children for a parent (`parent`), returns an iterator it's children's indexes
|
/// Creates children for a parent (`parent`), returns an iterator it's children's indexes
|
||||||
fn generate_children(&mut self, parent_idx: usize) -> Option<impl Iterator<Item = usize>> {
|
fn generate_children(
|
||||||
|
&mut self,
|
||||||
|
parent_idx: usize,
|
||||||
|
lazy_children: bool,
|
||||||
|
) -> Option<impl Iterator<Item = usize>> {
|
||||||
// early-exit if a winner for the parent already exists
|
// early-exit if a winner for the parent already exists
|
||||||
if self.arena[parent_idx].winner != Winner::None {
|
if self.arena[parent_idx].winner != Winner::None {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let new_color = !self.arena[parent_idx].color;
|
||||||
|
let parent_lazy = self.arena[parent_idx].lazy_children;
|
||||||
let mut new: Vec<Move> =
|
let mut new: Vec<Move> =
|
||||||
// use [`Board::all_positions`] here instead of [`Board::possible_moves`]
|
// use [`Board::all_positions`] here instead of [`Board::possible_moves`]
|
||||||
// because we use [`Board::what_if`] later and we want to reduce calls to [`Board::propegate_from_dry`]
|
// because we use [`Board::what_if`] later and we want to reduce calls to [`Board::propegate_from_dry`]
|
||||||
Board::all_positions()
|
Board::all_positions()
|
||||||
.flat_map(|(i, j)| self.arena[parent_idx].board.what_if(i, j, !self.arena[parent_idx].color).map(|x| (i, j, x)))
|
.flat_map(|(i, j)| self.arena[parent_idx].board.what_if(i, j, new_color).map(|x| (i, j, x)))
|
||||||
.map(|(i, j, new_board)| Move {
|
.map(|(i, j, new_board)| Move {
|
||||||
i,
|
i,
|
||||||
j,
|
j,
|
||||||
@ -143,22 +165,37 @@ impl FutureMoves {
|
|||||||
parent: Some(parent_idx),
|
parent: Some(parent_idx),
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
value: 0,
|
value: 0,
|
||||||
color: !self.arena[parent_idx].color
|
color: new_color,
|
||||||
|
lazy_children,
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
|
// negative, because we want the max value to be at the first index
|
||||||
|
new.sort_by_key(|x| -x.compute_self_value(self.agent_color));
|
||||||
|
|
||||||
// keep the TOP_K children `self.agent_color`-color moves
|
// keep the TOP_K children `self.agent_color`-color moves
|
||||||
const TOP_K_CHILDREN: usize = 1;
|
const TOP_K_CHILDREN: usize = 1;
|
||||||
|
|
||||||
// we want to keep only the best move of the agent
|
// we want to keep only the best move of the agent
|
||||||
if !self.arena[parent_idx].color == self.agent_color && new.len() > TOP_K_CHILDREN {
|
if lazy_children && new_color == self.agent_color && new.len() > TOP_K_CHILDREN {
|
||||||
// TODO! Move this to `extend_layers` so we can prune based on recursive [`Move`] value
|
// TODO! Move this to `extend_layers` so we can prune based on recursive [`Move`] value
|
||||||
// negative, because we want the max value to be at the first index
|
|
||||||
new.sort_by_key(|x| -x.compute_self_value(self.agent_color));
|
|
||||||
new.drain(TOP_K_CHILDREN..);
|
new.drain(TOP_K_CHILDREN..);
|
||||||
}
|
}
|
||||||
|
|
||||||
let start_idx = self.arena.len();
|
let start_idx = self.arena.len();
|
||||||
|
if parent_lazy && !lazy_children {
|
||||||
|
// this move's children are being regenerated after lazy child expiration, don't append first node
|
||||||
|
if new.len() > 1 {
|
||||||
|
self.arena.extend(new.drain(1..));
|
||||||
|
} else {
|
||||||
|
// nothing will be appended
|
||||||
|
// even though it was sorted the first time around
|
||||||
|
// there's still no more than one element (which is already in the arena)
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
self.arena.extend(new);
|
self.arena.extend(new);
|
||||||
|
}
|
||||||
let new_indices = start_idx..self.arena.len();
|
let new_indices = start_idx..self.arena.len();
|
||||||
|
|
||||||
self.arena[parent_idx].children.extend(new_indices.clone());
|
self.arena[parent_idx].children.extend(new_indices.clone());
|
||||||
@ -250,6 +287,7 @@ impl FutureMoves {
|
|||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
value: 0,
|
value: 0,
|
||||||
color: !self.agent_color,
|
color: !self.agent_color,
|
||||||
|
lazy_children: false,
|
||||||
});
|
});
|
||||||
self.update_root_idx(0);
|
self.update_root_idx(0);
|
||||||
}
|
}
|
||||||
@ -348,7 +386,7 @@ pub struct ComplexAgent {
|
|||||||
|
|
||||||
impl ComplexAgent {
|
impl ComplexAgent {
|
||||||
pub const fn new(color: Piece) -> Self {
|
pub const fn new(color: Piece) -> Self {
|
||||||
const MAX_DEPTH: usize = 15;
|
const MAX_DEPTH: usize = 10;
|
||||||
Self {
|
Self {
|
||||||
color,
|
color,
|
||||||
future_moves: FutureMoves::new(color, MAX_DEPTH),
|
future_moves: FutureMoves::new(color, MAX_DEPTH),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user