improvements
This commit is contained in:
parent
43585802d8
commit
cd28790d80
18
Cargo.lock
generated
18
Cargo.lock
generated
@ -280,7 +280,7 @@ checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"zerocopy 0.8.17",
|
||||
"zerocopy 0.8.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -295,12 +295,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff"
|
||||
checksum = "a88e0da7a2c97baa202165137c158d0a2e824ac465d13d81046727b34cb247d3"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"zerocopy 0.8.17",
|
||||
"zerocopy 0.8.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -517,11 +517,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.17"
|
||||
version = "0.8.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa91407dacce3a68c56de03abe2760159582b846c6a4acd2f456618087f12713"
|
||||
checksum = "79386d31a42a4996e3336b0919ddb90f81112af416270cff95b5f5af22b839c2"
|
||||
dependencies = [
|
||||
"zerocopy-derive 0.8.17",
|
||||
"zerocopy-derive 0.8.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -537,9 +537,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.17"
|
||||
version = "0.8.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06718a168365cad3d5ff0bb133aad346959a2074bd4a85c121255a11304a8626"
|
||||
checksum = "76331675d372f91bf8d17e13afbd5fe639200b73d01f0fc748bb059f9cca2db7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@ -11,7 +11,7 @@ lto = true
|
||||
|
||||
[dependencies]
|
||||
arrayvec = "0.7"
|
||||
bitvec = "1.0.1"
|
||||
bitvec = "1"
|
||||
either = "1.13"
|
||||
indicatif = "0.17"
|
||||
lazy_static = "1.5"
|
||||
|
||||
@ -264,6 +264,10 @@ impl Board {
|
||||
.count()
|
||||
}
|
||||
|
||||
pub fn net_score(&self, piece: Piece) -> isize {
|
||||
self.count(piece) as isize - self.count(!piece) as isize
|
||||
}
|
||||
|
||||
/// Returns (White score, Black score)
|
||||
pub fn get_score(&self) -> (usize, usize) {
|
||||
(self.count(Piece::White), self.count(Piece::Black))
|
||||
|
||||
@ -3,7 +3,7 @@ use crate::{
|
||||
board::{Board, Winner},
|
||||
piece::Piece,
|
||||
};
|
||||
use indicatif::{ProgressBar, ProgressIterator, ProgressStyle};
|
||||
use indicatif::{ProgressIterator, ProgressStyle};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Move {
|
||||
@ -32,8 +32,12 @@ struct Move {
|
||||
}
|
||||
|
||||
impl Move {
|
||||
fn compute_self_value(&self, agent_color: Piece, depth: i64) -> i64 {
|
||||
let mut self_value = self.value;
|
||||
pub const fn coords(&self) -> (usize, usize) {
|
||||
(self.i, self.j)
|
||||
}
|
||||
|
||||
fn compute_self_value(&self, agent_color: Piece) -> i64 {
|
||||
let mut self_value = self.board.net_score(agent_color) as i64;
|
||||
|
||||
if self.winner == Winner::Player(!agent_color) {
|
||||
// if this board results in the opponent winning, MAJORLY negatively weigh this move
|
||||
@ -44,17 +48,23 @@ impl Move {
|
||||
// results in a win for the agent
|
||||
self_value = i64::MAX;
|
||||
}
|
||||
// TODO! handle ties... what should they be valued as? maybe `i64::MAX / 2`?
|
||||
// TODO! handle ties... what should they be valued as? maybe `i64::MAX / 2` or 0?
|
||||
|
||||
self_value / depth
|
||||
self_value
|
||||
}
|
||||
}
|
||||
|
||||
struct FutureMoves {
|
||||
/// Arena containing all [`Move`]
|
||||
arena: Vec<Move>,
|
||||
|
||||
/// Index of the [`Move`] tree's root node
|
||||
current_root: Option<usize>,
|
||||
|
||||
/// Current generated depth of the Arena
|
||||
current_depth: isize,
|
||||
|
||||
/// Target depth of children to generate
|
||||
max_depth: usize,
|
||||
|
||||
/// Color w.r.t
|
||||
@ -76,23 +86,23 @@ impl FutureMoves {
|
||||
fn extend_layers(&mut self) {
|
||||
let mut next_nodes: Vec<usize> = (0..self.arena.len())
|
||||
.filter(|&idx| self.arena[idx].children.is_empty())
|
||||
.filter(|&idx| self.is_connected_to_root(idx)) // put here so this will not extend needlessly before prunes
|
||||
.collect();
|
||||
|
||||
for _ in self.current_depth..=(self.max_depth as isize) {
|
||||
let prog_len = next_nodes.len();
|
||||
// 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
|
||||
.into_iter()
|
||||
.progress_with(
|
||||
ProgressBar::new(prog_len as u64).with_style(
|
||||
ProgressStyle::with_template(
|
||||
"Generating children: ({pos}/{len}) {per_sec}",
|
||||
)
|
||||
.progress_with_style(
|
||||
ProgressStyle::with_template("Generating children: ({pos}/{len}) {per_sec}")
|
||||
.unwrap(),
|
||||
),
|
||||
)
|
||||
.flat_map(|node_idx| {
|
||||
self.generate_children(
|
||||
Some(node_idx),
|
||||
node_idx,
|
||||
&self.arena[node_idx].board.clone(),
|
||||
!self.arena[node_idx].color,
|
||||
)
|
||||
@ -102,44 +112,60 @@ impl FutureMoves {
|
||||
self.current_depth = self.max_depth as isize;
|
||||
}
|
||||
|
||||
/// Determines if a [`Move`] at index `idx` is connected to `self.current_root`
|
||||
/// Returns `false` if `self.current_root` is None
|
||||
fn is_connected_to_root(&self, idx: usize) -> bool {
|
||||
if let Some(root) = self.current_root {
|
||||
let mut current = Some(idx);
|
||||
while let Some(parent_idx) = current {
|
||||
if parent_idx == root {
|
||||
return true;
|
||||
}
|
||||
current = self.arena[parent_idx].parent;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Creates children for a parent (`parent`), returns an iterator it's children's indexes
|
||||
fn generate_children(
|
||||
&mut self,
|
||||
parent: Option<usize>,
|
||||
parent_idx: usize,
|
||||
board: &Board,
|
||||
color: Piece,
|
||||
) -> impl Iterator<Item = usize> {
|
||||
let start_idx = self.arena.len();
|
||||
let mut new: Vec<Move> =
|
||||
// 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`]
|
||||
Board::all_positions()
|
||||
.flat_map(|(i, j)| board.what_if(i, j, color).map(|x| (i, j, x)))
|
||||
.flat_map(|(i, j)| board.what_if(i, j, !self.arena[parent_idx].color).map(|x| (i, j, x)))
|
||||
.map(|(i, j, new_board)| Move {
|
||||
i,
|
||||
j,
|
||||
board: new_board,
|
||||
winner: new_board.game_winner(color),
|
||||
parent,
|
||||
parent: Some(parent_idx),
|
||||
children: Vec::new(),
|
||||
value: new_board.count(self.agent_color) as i64 - new_board.count(!self.agent_color) as i64,
|
||||
color: parent.map(|idx| !self.arena[idx].color).unwrap_or(self.agent_color)
|
||||
value: 0,
|
||||
color: !self.arena[parent_idx].color
|
||||
}).collect();
|
||||
|
||||
// keep the TOP_K children `self.agent_color`-color moves
|
||||
const TOP_K_CHILDREN: usize = 1;
|
||||
|
||||
// we want to keep only the best move of the agent
|
||||
if color == self.agent_color && new.len() > 1 {
|
||||
if color == self.agent_color && new.len() > TOP_K_CHILDREN {
|
||||
// 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, 1));
|
||||
new.drain(1..);
|
||||
new.sort_by_key(|x| -x.compute_self_value(self.agent_color));
|
||||
new.drain(TOP_K_CHILDREN..);
|
||||
}
|
||||
|
||||
let start_idx = self.arena.len();
|
||||
self.arena.extend(new);
|
||||
|
||||
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());
|
||||
|
||||
new_indices
|
||||
}
|
||||
@ -168,12 +194,13 @@ impl FutureMoves {
|
||||
} else {
|
||||
*was_visited = true;
|
||||
}
|
||||
|
||||
if self.depth_of(idx) != depth as usize {
|
||||
continue;
|
||||
}
|
||||
|
||||
let self_value = self.arena[idx]
|
||||
.compute_self_value(self.agent_color, (self.current_depth - depth + 1) as i64);
|
||||
let self_value = self.arena[idx].compute_self_value(self.agent_color)
|
||||
/ (self.current_depth - depth + 1) as i64;
|
||||
|
||||
let children_value = self.arena[idx]
|
||||
.children
|
||||
@ -187,6 +214,7 @@ impl FutureMoves {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn best_move(&self) -> Option<(usize, usize)> {
|
||||
self.current_root
|
||||
.and_then(|x| {
|
||||
@ -201,7 +229,7 @@ impl FutureMoves {
|
||||
"selected move color should be the same as the color of the agent"
|
||||
);
|
||||
})
|
||||
.map(|&x| (self.arena[x].i, self.arena[x].j))
|
||||
.map(|&x| self.arena[x].coords())
|
||||
}
|
||||
|
||||
/// Updates `FutureMoves` based on the current state of the board
|
||||
@ -217,7 +245,6 @@ impl FutureMoves {
|
||||
.map(|(idx, _)| idx);
|
||||
|
||||
if let Some(curr_board_idx) = curr_board {
|
||||
self.current_root = Some(curr_board_idx);
|
||||
self.update_root_idx(curr_board_idx);
|
||||
} else {
|
||||
println!("Generating root of FutureMoves");
|
||||
@ -287,7 +314,11 @@ impl FutureMoves {
|
||||
.map(|(new_idx, (old_idx, mut node))| {
|
||||
index_map[old_idx] = Some(new_idx);
|
||||
|
||||
node.parent = node.parent.and_then(|p| index_map[p]);
|
||||
if let Some(parent) = node.parent.as_mut() {
|
||||
if let Some(new_parent) = index_map[*parent] {
|
||||
*parent = new_parent;
|
||||
}
|
||||
}
|
||||
|
||||
node.children.retain_mut(|c| {
|
||||
if let Some(new_c) = index_map[*c] {
|
||||
@ -313,7 +344,7 @@ pub struct ComplexAgent {
|
||||
|
||||
impl ComplexAgent {
|
||||
pub const fn new(color: Piece) -> Self {
|
||||
const MAX_DEPTH: usize = 17;
|
||||
const MAX_DEPTH: usize = 15;
|
||||
Self {
|
||||
color,
|
||||
future_moves: FutureMoves::new(color, MAX_DEPTH),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user