use crate::{OpeningBook, Position, Solver};
use indicatif::{ParallelProgressIterator, ProgressBar, ProgressStyle};
use rayon::prelude::*;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::sync::{Arc, Mutex};
thread_local! {
static THREAD_SOLVER: RefCell<Solver> = RefCell::new(Solver::empty());
}
pub struct OpeningBookGenerator;
impl OpeningBookGenerator {
pub fn generate(max_depth: usize) -> OpeningBook {
let map = Arc::new(Mutex::new(HashMap::new()));
let seen = Arc::new(Mutex::new(HashSet::new()));
let start_pos = Position::new();
seen.lock().unwrap().insert(start_pos.get_key());
let mut current_level = vec![start_pos];
for depth in 0..=max_depth {
if current_level.is_empty() {
break;
}
println!("Processing Depth: {}, Positions: {}", depth, current_level.len());
let progress_bar = Self::create_progress_bar(current_level.len() as u64);
let next_level_positions: Vec<Vec<Position>> = current_level
.par_iter()
.progress_with(progress_bar)
.map(|pos| {
THREAD_SOLVER.with(|s| {
let mut solver = s.borrow_mut();
let key = pos.get_key();
let score = solver.solve(pos);
map.lock().unwrap().insert(key, score);
Self::generate_children(pos)
})
})
.collect();
let mut next_level = Vec::new();
let mut seen_guard = seen.lock().unwrap();
for positions in next_level_positions {
for pos in positions {
let key = pos.get_key();
if seen_guard.insert(key) {
next_level.push(pos);
}
}
}
current_level = next_level;
}
let mut book = OpeningBook::new();
book.map = Arc::try_unwrap(map)
.expect("Error unwrapping opening book.")
.into_inner()
.expect("Error unwrapping opening book.");
println!("Generation complete. Final book size: {}", book.map.len());
book
}
fn generate_children(pos: &Position) -> Vec<Position> {
let mut children = Vec::with_capacity(Position::WIDTH);
let possible_moves = pos.possible();
for col in 0..Position::WIDTH {
if (possible_moves & Position::column_mask(col)) > 0 {
let mut next_pos = *pos;
next_pos.play(col);
if !next_pos.is_won_position() {
children.push(next_pos);
}
}
}
children
}
fn create_progress_bar(len: u64) -> ProgressBar {
let progress_bar_style = ProgressStyle::with_template(
"{spinner:.green} [{elapsed_precise}] \
[{wide_bar:.cyan/blue}] {human_pos}/{human_len} ({eta})"
)
.unwrap()
.progress_chars("#>-");
ProgressBar::new(len).with_style(progress_bar_style)
}
}