86 lines
2.9 KiB
Rust
86 lines
2.9 KiB
Rust
use super::board_value::BoardValueMap;
|
|
use crate::repr::{Board, CoordPair, Piece, Winner};
|
|
use allocative::Allocative;
|
|
use std::sync::LazyLock;
|
|
|
|
#[derive(Clone, Debug, Allocative)]
|
|
pub struct Move {
|
|
/// Coordinates (i, j) of the move (if it exists)
|
|
pub coord: Option<CoordPair>,
|
|
|
|
/// Current winner of the match
|
|
pub winner: Winner,
|
|
|
|
/// Index of this move's parent
|
|
pub parent: Option<usize>,
|
|
|
|
/// Indices of this Move's Children
|
|
// PERF! this accounts for ~40% of memory usage
|
|
// I'm thinking maybe switching to this actually being `Vec<Box<Move>>`
|
|
// and ditching the entire `Arena` idea, that would have
|
|
// cascading effects though, something to think about
|
|
pub children: Vec<usize>,
|
|
|
|
/// Has this [`Move`] already attempted to create children?
|
|
pub tried_children: bool,
|
|
|
|
/// Value of this move (including children)
|
|
pub value: Option<i32>,
|
|
|
|
/// What is the inherit value of this move (not including children)
|
|
pub self_value: i16,
|
|
|
|
/// Which color made a move on this move?
|
|
pub color: Piece,
|
|
|
|
/// Was this move's children previously trimmed?
|
|
pub is_trimmed: bool,
|
|
}
|
|
|
|
const BVM: BoardValueMap = BoardValueMap::weighted();
|
|
|
|
impl Move {
|
|
pub fn new(coord: Option<CoordPair>, board: Board, color: Piece, agent_color: Piece) -> Self {
|
|
let mut m = Move {
|
|
coord,
|
|
winner: board.game_winner(),
|
|
parent: None,
|
|
children: Vec::new(),
|
|
value: None,
|
|
color,
|
|
is_trimmed: false,
|
|
self_value: 0,
|
|
tried_children: false,
|
|
};
|
|
m.self_value = m.compute_self_value(agent_color, &board);
|
|
m
|
|
}
|
|
|
|
fn compute_self_value(&self, agent_color: Piece, board: &Board) -> i16 {
|
|
if self.winner == Winner::Player(!agent_color) {
|
|
// if this board results in the opponent winning, MAJORLY negatively weigh this move
|
|
// NOTE! this branch isn't completely deleted because if so, the bot wouldn't make a move.
|
|
// We shouldn't prune branches because we still need to always react to the opponent's moves
|
|
return i16::MIN + 1;
|
|
} else if self.winner == Winner::Player(agent_color) {
|
|
// results in a win for the agent
|
|
return i16::MAX - 1;
|
|
}
|
|
// 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(board, agent_color)
|
|
}
|
|
|
|
/// Sort children of the [`Move`] by their self_value in `arena`
|
|
pub fn sort_children(&mut self, arena: &[Move]) {
|
|
self.children.sort_by(|&a, &b| {
|
|
arena[b].value.cmp(&arena[a].value) // Descending order for agent's max nodes
|
|
});
|
|
}
|
|
}
|