small rewrite and addition of by_depth + by_depth_test
This commit is contained in:
parent
f60de28c58
commit
a95b9e08c1
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -334,6 +334,12 @@ version = "2.7.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nohash-hasher"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num"
|
name = "num"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
@ -436,6 +442,7 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"nohash-hasher",
|
||||||
"num",
|
"num",
|
||||||
"rand",
|
"rand",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
|
|||||||
@ -33,6 +33,7 @@ const_fn = "0.4"
|
|||||||
either = "1.13"
|
either = "1.13"
|
||||||
indicatif = "0.17"
|
indicatif = "0.17"
|
||||||
lazy_static = "1.5"
|
lazy_static = "1.5"
|
||||||
|
nohash-hasher = "0.2.0"
|
||||||
num = "0.4"
|
num = "0.4"
|
||||||
rand = "0.9"
|
rand = "0.9"
|
||||||
static_assertions = "1.1"
|
static_assertions = "1.1"
|
||||||
|
|||||||
@ -12,8 +12,8 @@ pub struct ComplexAgent {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
impl ComplexAgent {
|
impl ComplexAgent {
|
||||||
pub const fn new(color: Piece) -> Self {
|
pub const fn new(color: Piece) -> Self {
|
||||||
const MAX_DEPTH: usize = 8;
|
const MAX_DEPTH: usize = 5;
|
||||||
const NON_LAZY_DEPTH: usize = 8;
|
const NON_LAZY_DEPTH: usize = 3;
|
||||||
Self {
|
Self {
|
||||||
color,
|
color,
|
||||||
future_moves: FutureMoves::new(color, MAX_DEPTH, NON_LAZY_DEPTH),
|
future_moves: FutureMoves::new(color, MAX_DEPTH, NON_LAZY_DEPTH),
|
||||||
|
|||||||
@ -3,7 +3,7 @@ use crate::{
|
|||||||
repr::{Board, Piece, Winner},
|
repr::{Board, Piece, Winner},
|
||||||
};
|
};
|
||||||
use indicatif::{ProgressIterator, ProgressStyle};
|
use indicatif::{ProgressIterator, ProgressStyle};
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, hash::BuildHasherDefault};
|
||||||
|
|
||||||
pub struct FutureMoves {
|
pub struct FutureMoves {
|
||||||
/// Arena containing all [`Move`]
|
/// Arena containing all [`Move`]
|
||||||
@ -49,7 +49,7 @@ impl FutureMoves {
|
|||||||
// we want to select all nodes that don't have children, or are lazy (need to maybe be regenerated)
|
// we want to select all nodes that don't have children, or are lazy (need to maybe be regenerated)
|
||||||
.filter(|&idx| {
|
.filter(|&idx| {
|
||||||
let got = &self.arena[idx];
|
let got = &self.arena[idx];
|
||||||
got.lazy_children || got.children.is_empty()
|
got.is_lazy || got.children.is_empty()
|
||||||
})
|
})
|
||||||
.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();
|
||||||
@ -65,14 +65,14 @@ impl FutureMoves {
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
)
|
)
|
||||||
.flat_map(|node_idx| {
|
.flat_map(|node_idx| {
|
||||||
self.generate_children(
|
if (self.arena[node_idx].is_lazy
|
||||||
node_idx,
|
&& self.depth_of(node_idx) + 1 > self.lazy_expire)
|
||||||
if self.arena[node_idx].lazy_children {
|
|| !self.arena[node_idx].is_lazy
|
||||||
self.depth_of(node_idx)
|
{
|
||||||
|
self.generate_children(node_idx, i > self.lazy_expire)
|
||||||
} else {
|
} else {
|
||||||
i
|
None
|
||||||
} > self.lazy_expire,
|
}
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect();
|
.collect();
|
||||||
@ -126,7 +126,6 @@ impl FutureMoves {
|
|||||||
j,
|
j,
|
||||||
new_board,
|
new_board,
|
||||||
new_color,
|
new_color,
|
||||||
lazy_children,
|
|
||||||
self.agent_color,
|
self.agent_color,
|
||||||
Some(parent_idx),
|
Some(parent_idx),
|
||||||
)
|
)
|
||||||
@ -134,7 +133,7 @@ impl FutureMoves {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// keep the TOP_K children of their magnitude
|
// keep the TOP_K children of their magnitude
|
||||||
const TOP_K_CHILDREN: usize = 10;
|
const TOP_K_CHILDREN: usize = 3;
|
||||||
|
|
||||||
let start_idx = self.arena.len();
|
let start_idx = self.arena.len();
|
||||||
self.arena.extend(new);
|
self.arena.extend(new);
|
||||||
@ -142,13 +141,14 @@ impl FutureMoves {
|
|||||||
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());
|
||||||
|
|
||||||
|
if lazy_children && new_indices.clone().count() > TOP_K_CHILDREN {
|
||||||
let mut parent_copy = self.arena[parent_idx].clone();
|
let mut parent_copy = self.arena[parent_idx].clone();
|
||||||
parent_copy.sort_children(self.arena.as_mut_slice());
|
parent_copy.sort_children(self.arena.as_mut_slice());
|
||||||
self.arena[parent_idx] = parent_copy;
|
self.arena[parent_idx] = parent_copy;
|
||||||
|
|
||||||
if lazy_children && new_indices.clone().count() > TOP_K_CHILDREN {
|
|
||||||
for i in new_indices.clone().skip(TOP_K_CHILDREN) {
|
for i in new_indices.clone().skip(TOP_K_CHILDREN) {
|
||||||
self.arena[i].lazy_children = true;
|
self.arena[i].is_lazy = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,13 +167,12 @@ impl FutureMoves {
|
|||||||
depth - 1
|
depth - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute `Move.value`, propegating upwards from the furthest out Moves
|
fn by_depth(&self, indexes: impl Iterator<Item = usize>) -> Vec<(usize, Vec<usize>)> {
|
||||||
/// in the Arena.
|
let mut by_depth: HashMap<
|
||||||
fn compute_values(&mut self, indexes: impl Iterator<Item = usize>) {
|
usize,
|
||||||
// PERF! pre-organize all indexes based on what depth they're at
|
Vec<usize>,
|
||||||
// previously, I did a lookup map based on if a node was visited, still resulted in a full
|
BuildHasherDefault<nohash_hasher::NoHashHasher<usize>>,
|
||||||
// O(n) iteration each depth
|
> = HashMap::with_hasher(BuildHasherDefault::default());
|
||||||
let mut by_depth: HashMap<usize, Vec<usize>> = HashMap::new();
|
|
||||||
for idx in indexes {
|
for idx in indexes {
|
||||||
let depth = self.depth_of(idx);
|
let depth = self.depth_of(idx);
|
||||||
if let Some(got) = by_depth.get_mut(&depth) {
|
if let Some(got) = by_depth.get_mut(&depth) {
|
||||||
@ -182,36 +181,42 @@ impl FutureMoves {
|
|||||||
by_depth.insert(depth, vec![idx]);
|
by_depth.insert(depth, vec![idx]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut by_depth_vec: Vec<(usize, Vec<usize>)> = by_depth.into_iter().collect();
|
let mut by_depth_vec: Vec<(usize, Vec<usize>)> = by_depth.into_iter().collect();
|
||||||
by_depth_vec.sort_by_key(|x| x.0);
|
by_depth_vec.sort_by_key(|x| x.0);
|
||||||
|
by_depth_vec
|
||||||
|
}
|
||||||
|
|
||||||
for (depth, nodes) in by_depth_vec {
|
/// Compute `Move.value`, propegating upwards from the furthest out Moves
|
||||||
|
/// in the Arena.
|
||||||
|
fn compute_values(&mut self, indexes: impl Iterator<Item = usize>) {
|
||||||
|
// PERF! pre-organize all indexes based on what depth they're at
|
||||||
|
// previously, I did a lookup map based on if a node was visited, still resulted in a full
|
||||||
|
// O(n) iteration each depth
|
||||||
|
let by_depth_vec = self.by_depth(indexes);
|
||||||
|
|
||||||
|
// reversed so we build up the value of the closest (in time) moves from the future
|
||||||
|
for (depth, nodes) in by_depth_vec.into_iter().rev() {
|
||||||
for idx in nodes {
|
for idx in nodes {
|
||||||
// TODO! impl dynamic sorting based on children's states, maybe it propegates
|
// TODO! impl dynamic sorting based on children's states, maybe it propegates
|
||||||
// upwards using the `parent` field
|
// upwards using the `parent` field
|
||||||
// SAFETY! the sort_by_key function should not modify anything
|
|
||||||
unsafe { &mut (*(self as *mut Self)) }.arena[idx]
|
// let mut parent_copy = self.arena[idx].clone();
|
||||||
.children
|
// parent_copy.sort_children(self.arena.as_mut_slice());
|
||||||
// negative because we want the largest value in the first index
|
// self.arena[idx] = parent_copy;
|
||||||
// abs so we get the most extreme solutions
|
|
||||||
// but base on `.value` for recursive behavior
|
|
||||||
.sort_by_key(|&x| -self.arena[x].value.abs());
|
|
||||||
|
|
||||||
let children_value = self.arena[idx]
|
let children_value = self.arena[idx]
|
||||||
.children
|
.children
|
||||||
.iter()
|
.iter()
|
||||||
.rev() // rev then reverse so we get an index starting from the back
|
.map(|&child| self.arena[child].value.expect("child has no value??"))
|
||||||
.enumerate()
|
|
||||||
// since children are sorted by value, we should weight the first one more
|
|
||||||
.map(|(i, &child)| self.arena[child].value * ((i + 1) as i128))
|
|
||||||
.sum::<i128>();
|
.sum::<i128>();
|
||||||
|
|
||||||
// previously we used `depth` and divided `self_value` by it, idk if this is worth it
|
// 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
|
// 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
|
// each other or something, could be cool to benchmark these more subjective things, not
|
||||||
// just performance
|
// just performance (cycles/time wise)
|
||||||
self.arena[idx].value = self.arena[idx].self_value as i128 + children_value;
|
self.arena[idx].value = Some(
|
||||||
|
(self.arena[idx].self_value as i128 + children_value) / (depth + 1) as i128,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -260,7 +265,6 @@ impl FutureMoves {
|
|||||||
0, // dummy
|
0, // dummy
|
||||||
board,
|
board,
|
||||||
!self.agent_color,
|
!self.agent_color,
|
||||||
false,
|
|
||||||
self.agent_color,
|
self.agent_color,
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
@ -347,7 +351,7 @@ impl FutureMoves {
|
|||||||
|
|
||||||
let mut children = self.arena[root].children.clone();
|
let mut children = self.arena[root].children.clone();
|
||||||
|
|
||||||
children.sort_by_key(|&i| -self.arena[i].value);
|
children.sort_by_key(|&i| -self.arena[i].value.expect("child has no value"));
|
||||||
let start_len = ((children.len()) as f32 * (1.0 - BOTTOM_PERC)) as usize;
|
let start_len = ((children.len()) as f32 * (1.0 - BOTTOM_PERC)) as usize;
|
||||||
let drained = children.drain(start_len..);
|
let drained = children.drain(start_len..);
|
||||||
println!("{}", drained.len());
|
println!("{}", drained.len());
|
||||||
@ -439,10 +443,10 @@ mod tests {
|
|||||||
winner: Winner::None,
|
winner: Winner::None,
|
||||||
parent: None,
|
parent: None,
|
||||||
children: vec![1, 3, 4],
|
children: vec![1, 3, 4],
|
||||||
value: 0,
|
value: None,
|
||||||
self_value: 0,
|
self_value: 0,
|
||||||
color: Piece::Black,
|
color: Piece::Black,
|
||||||
lazy_children: false,
|
is_lazy: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
futm.update_root_idx_raw(0);
|
futm.update_root_idx_raw(0);
|
||||||
@ -453,7 +457,6 @@ mod tests {
|
|||||||
0,
|
0,
|
||||||
Board::new(),
|
Board::new(),
|
||||||
Piece::White,
|
Piece::White,
|
||||||
false,
|
|
||||||
Piece::Black,
|
Piece::Black,
|
||||||
Some(0),
|
Some(0),
|
||||||
));
|
));
|
||||||
@ -464,7 +467,6 @@ mod tests {
|
|||||||
1234,
|
1234,
|
||||||
Board::new(),
|
Board::new(),
|
||||||
Piece::White,
|
Piece::White,
|
||||||
false,
|
|
||||||
Piece::Black,
|
Piece::Black,
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
@ -474,7 +476,6 @@ mod tests {
|
|||||||
0,
|
0,
|
||||||
Board::new(),
|
Board::new(),
|
||||||
Piece::White,
|
Piece::White,
|
||||||
false,
|
|
||||||
Piece::Black,
|
Piece::Black,
|
||||||
Some(0),
|
Some(0),
|
||||||
));
|
));
|
||||||
@ -484,7 +485,6 @@ mod tests {
|
|||||||
0,
|
0,
|
||||||
Board::new(),
|
Board::new(),
|
||||||
Piece::White,
|
Piece::White,
|
||||||
false,
|
|
||||||
Piece::Black,
|
Piece::Black,
|
||||||
Some(0),
|
Some(0),
|
||||||
));
|
));
|
||||||
@ -510,7 +510,6 @@ mod tests {
|
|||||||
0,
|
0,
|
||||||
Board::new().starting_pos(),
|
Board::new().starting_pos(),
|
||||||
Piece::Black,
|
Piece::Black,
|
||||||
false,
|
|
||||||
Piece::Black,
|
Piece::Black,
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
@ -546,10 +545,10 @@ mod tests {
|
|||||||
winner: Winner::None,
|
winner: Winner::None,
|
||||||
parent: None,
|
parent: None,
|
||||||
children: vec![1],
|
children: vec![1],
|
||||||
value: 0,
|
value: None,
|
||||||
self_value: 0,
|
self_value: 0,
|
||||||
color: Piece::Black,
|
color: Piece::Black,
|
||||||
lazy_children: false,
|
is_lazy: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
futm.update_root_idx_raw(0);
|
futm.update_root_idx_raw(0);
|
||||||
@ -560,7 +559,6 @@ mod tests {
|
|||||||
0,
|
0,
|
||||||
Board::new(),
|
Board::new(),
|
||||||
Piece::White,
|
Piece::White,
|
||||||
false,
|
|
||||||
Piece::Black,
|
Piece::Black,
|
||||||
Some(0),
|
Some(0),
|
||||||
));
|
));
|
||||||
@ -573,7 +571,6 @@ mod tests {
|
|||||||
1234,
|
1234,
|
||||||
Board::new(),
|
Board::new(),
|
||||||
Piece::White,
|
Piece::White,
|
||||||
false,
|
|
||||||
Piece::Black,
|
Piece::Black,
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
@ -583,7 +580,6 @@ mod tests {
|
|||||||
0,
|
0,
|
||||||
Board::new(),
|
Board::new(),
|
||||||
Piece::White,
|
Piece::White,
|
||||||
false,
|
|
||||||
Piece::Black,
|
Piece::Black,
|
||||||
Some(0),
|
Some(0),
|
||||||
));
|
));
|
||||||
@ -595,11 +591,68 @@ mod tests {
|
|||||||
0,
|
0,
|
||||||
Board::new(),
|
Board::new(),
|
||||||
Piece::White,
|
Piece::White,
|
||||||
false,
|
|
||||||
Piece::Black,
|
Piece::Black,
|
||||||
Some(0),
|
Some(0),
|
||||||
));
|
));
|
||||||
|
|
||||||
assert_eq!(futm.depth_of(3), 2);
|
assert_eq!(futm.depth_of(3), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn by_depth_test() {
|
||||||
|
let mut futm = FutureMoves::new(Piece::Black, 0, 0);
|
||||||
|
|
||||||
|
futm.arena.push(Move {
|
||||||
|
i: 0,
|
||||||
|
j: 0,
|
||||||
|
board: Board::new(),
|
||||||
|
winner: Winner::None,
|
||||||
|
parent: None,
|
||||||
|
children: vec![1],
|
||||||
|
value: None,
|
||||||
|
self_value: 0,
|
||||||
|
color: Piece::Black,
|
||||||
|
is_lazy: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
futm.update_root_idx_raw(0);
|
||||||
|
|
||||||
|
// child 1
|
||||||
|
futm.arena.push(Move::new(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
Board::new(),
|
||||||
|
Piece::White,
|
||||||
|
Piece::Black,
|
||||||
|
Some(0),
|
||||||
|
));
|
||||||
|
futm.arena[1].parent = Some(0);
|
||||||
|
futm.arena[1].children = vec![3];
|
||||||
|
|
||||||
|
// dummy
|
||||||
|
futm.arena.push(Move::new(
|
||||||
|
1234,
|
||||||
|
1234,
|
||||||
|
Board::new(),
|
||||||
|
Piece::White,
|
||||||
|
Piece::Black,
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
|
||||||
|
futm.arena.push(Move::new(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
Board::new(),
|
||||||
|
Piece::White,
|
||||||
|
Piece::Black,
|
||||||
|
Some(0),
|
||||||
|
));
|
||||||
|
|
||||||
|
futm.arena[3].parent = Some(1);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
futm.by_depth(0..futm.arena.len()),
|
||||||
|
vec![(0, vec![0, 2]), (1, vec![1]), (2, vec![3])]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ pub struct Move {
|
|||||||
pub children: Vec<usize>,
|
pub children: Vec<usize>,
|
||||||
|
|
||||||
/// Value of this move (including children)
|
/// Value of this move (including children)
|
||||||
pub value: i128,
|
pub value: Option<i128>,
|
||||||
|
|
||||||
/// What is the inherit value of this move (not including children)
|
/// What is the inherit value of this move (not including children)
|
||||||
pub self_value: i64,
|
pub self_value: i64,
|
||||||
@ -31,8 +31,8 @@ pub struct Move {
|
|||||||
/// Which color made a move on this move?
|
/// Which color made a move on this move?
|
||||||
pub color: Piece,
|
pub color: Piece,
|
||||||
|
|
||||||
/// Should the children of this move be lazily generated?
|
/// Was this child lazily created? (it will have delayed child generation)
|
||||||
pub lazy_children: bool,
|
pub is_lazy: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@ -45,7 +45,6 @@ impl Move {
|
|||||||
j: usize,
|
j: usize,
|
||||||
board: Board,
|
board: Board,
|
||||||
color: Piece,
|
color: Piece,
|
||||||
lazy_children: bool,
|
|
||||||
agent_color: Piece,
|
agent_color: Piece,
|
||||||
parent: Option<usize>,
|
parent: Option<usize>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -56,13 +55,13 @@ impl Move {
|
|||||||
winner: board.game_winner(),
|
winner: board.game_winner(),
|
||||||
parent,
|
parent,
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
value: 0,
|
value: None,
|
||||||
color,
|
color,
|
||||||
lazy_children,
|
is_lazy: false,
|
||||||
self_value: 0,
|
self_value: 0,
|
||||||
};
|
};
|
||||||
m.self_value = m.compute_self_value(agent_color);
|
m.self_value = m.compute_self_value(agent_color);
|
||||||
m.value = m.self_value as i128;
|
m.value = Some(m.self_value as i128);
|
||||||
m
|
m
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,11 +78,14 @@ impl Move {
|
|||||||
} else if self.winner == Winner::Player(agent_color) {
|
} else if self.winner == Winner::Player(agent_color) {
|
||||||
// results in a win for the agent
|
// results in a win for the agent
|
||||||
return i64::MAX - 1;
|
return i64::MAX - 1;
|
||||||
} else if self.winner == Winner::Tie {
|
|
||||||
// idk what a Tie should be valued?
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
// else if self.winner == Winner::Tie {
|
||||||
|
// // idk what a Tie should be valued?
|
||||||
|
// return 0;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// I guess ignore Ties here, don't give them an explicit value,
|
||||||
|
// because even in the case of ties, we want to have a higher score
|
||||||
BVM.board_value(&self.board, agent_color)
|
BVM.board_value(&self.board, agent_color)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user