use super::Solver;
use super::outcome::OptionalOutcome;
use crate::game::{Game, SimpleGame, DecomposableGame};
use crate::moves::{SimpleGameMoveSorter, DecomposableGameMoveSorter, ComponentsInfo};
use crate::dbs::{NimbersProvider, NimbersStorer};
use crate::nimber_set::NimberSet;
use crate::stats::{StatsCollector, ProgressReporter};
pub trait LVBSimpleGameSolver<G> where G: SimpleGame {
fn has_nimber(&mut self, position: &G::Position, nim: u8) -> bool;
fn nimber_lvb_report_progress<OO: OptionalOutcome, PR: ProgressReporter>(&mut self, position: G::Position, position_outcome: OO, progress_reporter: PR) -> u8;
#[inline(always)] fn nimber_lvb(&mut self, position: G::Position) -> u8 {
self.nimber_lvb_report_progress(position, (), ())
}
fn nimber_of_initial_lvb_report_progress<PR: ProgressReporter>(&mut self, progress_reporter: PR) -> u8;
fn nimber_of_initial_lvb(&mut self) -> u8 {
self.nimber_of_initial_lvb_report_progress(())
}
}
pub trait LVBDecomposableGameSolver<G> where G: DecomposableGame {
fn has_nimber(&mut self, position: &G::Position, nim: u8) -> bool;
fn nimber_of_component_lvb_report_progress<OO: OptionalOutcome, PR: ProgressReporter>(&mut self, position: G::Position, position_outcome: OO, progress_reporter: PR) -> u8;
#[inline(always)] fn nimber_of_component_lvb(&mut self, position: G::Position) -> u8 {
self.nimber_of_component_lvb_report_progress(position, None, ())
}
fn nimber_lvb_report_progress<PR: ProgressReporter + Clone>(&mut self, position: <G as DecomposableGame>::DecomposablePosition, progress_reporter: PR) -> u8;
#[inline(always)] fn nimber_lvb(&mut self, position: <G as DecomposableGame>::DecomposablePosition) -> u8 {
self.nimber_lvb_report_progress(position, ())
}
fn nimber_of_initial_lvb_report_progress<PR: ProgressReporter>(&mut self, progress_reporter: PR) -> u8;
fn nimber_of_initial_lvb(&mut self) -> u8 {
self.nimber_of_initial_lvb_report_progress(())
}
}
impl<G, TT, EDB, SORTER, STATS> LVBSimpleGameSolver<G> for Solver<'_, G, TT, EDB, SORTER, STATS>
where G: SimpleGame,
TT: NimbersProvider<G::Position> + NimbersStorer<G::Position>,
EDB: NimbersProvider<G::Position>,
SORTER: SimpleGameMoveSorter<G>,
STATS: StatsCollector,
G::Position: Clone {
fn has_nimber(&mut self, position: &G::Position, nim: u8) -> bool {
self.stats.pre();
let moves_count = self.game.moves_count(position);
if moves_count < nim as u16 {
self.stats.unknown();
return false;
}
if let Some(v) = self.nimber_from_tt(&position) { self.stats.db_cut(v);
return v == nim;
}
self.stats.etc();
let mut nimbers_to_skip = G::NimberSet::empty();
let mut moves: Vec::<G::Position> = Vec::with_capacity(moves_count as usize);
for m in self.game.successors_in_heuristic_ordered(&position) {
if let Some(v) = self.nimber_from_any_db(&m) {
if v == nim { self.stats.db_cut(v);
return false;
}
self.stats.db_skip(v);
nimbers_to_skip.append(v);
} else {
moves.push(m);
}
}
self.move_sorter.sort_moves(&self.game, &mut moves);
self.stats.recursive();
for new_nim in 0..nim {
if nimbers_to_skip.includes(new_nim) { continue; } if let Some(index) = moves.iter().position(|m| self.has_nimber(&m, new_nim)) {
SORTER::remove(&mut moves, index); } else { self.transposition_table.store_nimber(position.clone(), new_nim);
self.stats.exact(new_nim);
return false; }
}
if moves_count > nim as u16 && moves.iter().any(|m| self.has_nimber(&m, nim)) {
self.stats.unknown();
return false; }
self.transposition_table.store_nimber(position.clone(), nim);
self.stats.exact(nim);
true
}
fn nimber_lvb_report_progress<OO: OptionalOutcome, PR: ProgressReporter>(&mut self, position: G::Position, position_outcome: OO, mut progress_reporter: PR) -> u8 {
if position_outcome.is_losing() { return 0; }
self.stats.pre();
if let Some(v) = self.nimber_from_any_db(&position) {
self.stats.db_cut(v);
return v;
}
let (moves_count, nimbers_to_skip, mut moves) = self.etc_simple(&position);
self.stats.recursive();
progress_reporter.begin(moves_count);
for result in if position_outcome.is_winning(){1}else{0}..moves_count {
progress_reporter.progress(result);
debug_assert!(result < 256);
let result = result as u8;
if nimbers_to_skip.includes(result) { continue; } if let Some(index) = moves.iter().position(|m| self.has_nimber(&m, result)) {
SORTER::remove(&mut moves, index); } else { self.transposition_table.store_nimber(position, result);
self.stats.exact(result);
progress_reporter.end();
return result;
}
}
debug_assert!(moves_count < 256);
let moves_count = moves_count as u8;
self.transposition_table.store_nimber(position, moves_count);
self.stats.exact(moves_count);
progress_reporter.end();
moves_count }
fn nimber_of_initial_lvb_report_progress<PR: ProgressReporter>(&mut self, progress_reporter: PR) -> u8 {
self.nimber_lvb_report_progress(self.game.initial_position(), self.game.is_initial_position_winning(), progress_reporter)
}
}
impl<G, TT, EDB, SORTER, STATS, DP> Solver<'_, G, TT, EDB, SORTER, STATS>
where G: DecomposableGame<DecomposablePosition=DP>,
TT: NimbersProvider<G::Position> + NimbersStorer<G::Position>,
EDB: NimbersProvider<G::Position>,
SORTER: DecomposableGameMoveSorter<G>,
STATS: StatsCollector,
G::Position: Clone {
#[inline(always)]
fn decomposable_has_nimber(&mut self, m: &mut ComponentsInfo, move_components: &Vec<<G as Game>::Position>, nim: u8) -> bool {
while m.len > 1 {
self.stats.pre();
m.nimber ^= self.nimber_of_component_inner(&move_components[m.first + m.len - 1], (), ());
m.len -= 1;
}
self.has_nimber(&move_components[m.first], nim ^ m.nimber)
}
fn nimber_of_component_inner<OO: OptionalOutcome, PR: ProgressReporter>(&mut self, position: &G::Position, position_outcome: OO, mut progress_reporter: PR) -> u8 {
if let Some(v) = self.nimber_from_tt(&position) {
self.stats.db_cut(v);
return v;
}
let (moves_count, nimbers_to_skip, move_components, mut moves) = self.etc_decomposable(&position);
self.stats.recursive();
progress_reporter.begin(moves_count);
for new_nim in if position_outcome.is_winning(){1}else{0} .. moves_count {
progress_reporter.progress(new_nim);
debug_assert!(new_nim < 256);
let new_nim = new_nim as u8;
if nimbers_to_skip.includes(new_nim) { continue; }
if let Some(index) = moves.iter_mut().position(|m| self.decomposable_has_nimber(m, &move_components, new_nim)) {
SORTER::remove(&mut moves, index); } else { self.transposition_table.store_nimber(position.clone(), new_nim);
self.stats.exact(new_nim);
progress_reporter.end();
return new_nim;
}
}
debug_assert!(moves_count < 256);
let moves_count = moves_count as u8;
self.transposition_table.store_nimber(position.clone(), moves_count);
self.stats.exact(moves_count);
progress_reporter.end();
moves_count }
}
impl<G, TT, EDB, SORTER, STATS, DP> LVBDecomposableGameSolver<G> for Solver<'_, G, TT, EDB, SORTER, STATS>
where G: DecomposableGame<DecomposablePosition=DP>,
TT: NimbersProvider<G::Position> + NimbersStorer<G::Position>,
EDB: NimbersProvider<G::Position>,
SORTER: DecomposableGameMoveSorter<G>,
STATS: StatsCollector,
G::Position: Clone
{
fn has_nimber(&mut self, position: &G::Position, nim: u8) -> bool {
self.stats.pre();
let moves_count = self.game.moves_count(position);
if moves_count < nim as u16 {
self.stats.unknown();
return false;
}
if let Some(v) = self.nimber_from_tt(&position) { self.stats.db_cut(v);
return v == nim;
}
self.stats.etc();
let mut nimbers_to_skip = G::NimberSet::empty();
let mut move_components: Vec::<G::Position> = Vec::with_capacity(moves_count as usize * 2);
let mut moves: Vec::<ComponentsInfo> = Vec::with_capacity(moves_count as usize);
for composed_move in self.game.successors_in_heuristic_ordered(&position) {
let info = self.decompose(&composed_move, &mut move_components);
if info.len == 0 { if info.nimber == nim { self.stats.unknown();
return false;
}
nimbers_to_skip.append(info.nimber);
} else {
moves.push(info);
}
}
self.move_sorter.sort_moves(&self.game, &mut moves, &mut move_components);
self.stats.recursive();
for new_nim in 0..nim {
if nimbers_to_skip.includes(new_nim) { continue; }
if let Some(index) = moves.iter_mut().position(|m| {
self.decomposable_has_nimber(m, &move_components, new_nim)
}) {
SORTER::remove(&mut moves, index); } else { self.transposition_table.store_nimber(position.clone(), new_nim);
self.stats.exact(new_nim);
return false; }
}
if moves_count > nim as u16 && moves.iter_mut().any(|m| self.decomposable_has_nimber(m, &move_components, nim)) {
self.stats.unknown();
return false; }
self.transposition_table.store_nimber(position.clone(), nim);
self.stats.exact(nim);
true
}
fn nimber_of_component_lvb_report_progress<OO: OptionalOutcome, PR: ProgressReporter>(&mut self, position: G::Position, position_outcome: OO, progress_reporter: PR) -> u8 {
if position_outcome.is_losing() { return 0; }
self.stats.pre();
if let Some(v) = self.nimber_from_const_db(&position) {
self.stats.db_cut(v);
return v;
}
self.nimber_of_component_inner(&position, position_outcome, progress_reporter)
}
fn nimber_lvb_report_progress<PR: ProgressReporter + Clone>(&mut self, position: <G as DecomposableGame>::DecomposablePosition, progress_reporter: PR) -> u8 {
let mut result = 0u8;
for component in self.game.decompose(&position) {
result ^= self.nimber_of_component_lvb_report_progress(component, None, progress_reporter.clone());
}
result
}
fn nimber_of_initial_lvb_report_progress<PR: ProgressReporter>(&mut self, progress_reporter: PR) -> u8 {
self.nimber_of_component_lvb_report_progress(self.game.initial_position(), self.game.is_initial_position_winning(), progress_reporter)
}
}