<commit_before>use search_algorithms::board::ExtendedBoard;
use search_algorithms::board::GameResult;
use search_algorithms::alpha_beta::Score;
use search_algorithms::monte_carlo;
use pgn::PgnBoard;
use rayon::prelude::*;

pub trait TunableBoard {
    fn static_eval_with_params(&self, params: &[f32]) -> f32;
}

pub fn error<B>(positions: &mut [(B, GameResult)], params: &[f32]) -> f32
    where B: TunableBoard + ExtendedBoard + PgnBoard + Send {
    positions.into_par_iter()
        .map(|(board, game_result)| {
            let color = board.side_to_move();
            if let Score::Val(qsearc_eval) = qsearch(&mut board.clone(), Score::Loss(0),
                                                     Score::Win(0), params) {
                let eval = qsearc_eval * color.multiplier() as f32;

                let answer = match game_result {
                    GameResult::WhiteWin => 1.0,
                    GameResult::Draw => 0.5,
                    GameResult::BlackWin => 0.0,
                };

                let error = f32::powf(answer - monte_carlo::eval_to_win_pct(eval), 2.0);
                error
            }
            else {
                warn!("Found mate score while tuning {}.", board.to_fen());
                0.0
            }

    })
        .sum()
}

fn qsearch<B: ExtendedBoard>(board: &mut B, mut alpha: Score, beta: Score, params: &[f32]) -> Score
    where B: TunableBoard + ExtendedBoard {
    let color = board.side_to_move();

    if let Some(result) = board.game_result() {
        return Score::from_game_result(result, color)
    }

    let stand_pat = Score::Val(board.static_eval_with_params(params) * color.multiplier() as f32);

    if stand_pat >= beta {
        return beta;
    }

    alpha = alpha.max(stand_pat);

    let mut active_moves = vec![];
    board.active_moves(&mut active_moves);


    for mv in active_moves {
        let reverse_move = board.do_move(mv);

        let mut score = qsearch(board, beta, alpha, params);

        score = !score;
        board.reverse_move(reverse_move);

        if score >= beta {
            alpha = score; // TODO: Could be beta as well. Test.
            break;
        }
        if score > alpha {
            alpha = score;
        }
    }

    return alpha;

}<commit_msg>Return pv from board_tuning::qsearch()<commit_after>use search_algorithms::board::ExtendedBoard;
use search_algorithms::board::GameResult;
use search_algorithms::alpha_beta::Score;
use search_algorithms::monte_carlo;
use pgn::PgnBoard;
use rayon::prelude::*;

pub trait TunableBoard {
    fn static_eval_with_params(&self, params: &[f32]) -> f32;
}

pub fn error<B>(positions: &mut [(B, GameResult)], params: &[f32]) -> f32
    where B: TunableBoard + ExtendedBoard + PgnBoard + Send {
    positions.into_par_iter()
        .map(|(board, game_result)| {
            let color = board.side_to_move();
            if let (Score::Val(qsearc_eval), _) = qsearch(&mut board.clone(), Score::Loss(0),
                                                     Score::Win(0), params) {
                let eval = qsearc_eval * color.multiplier() as f32;

                let answer = match game_result {
                    GameResult::WhiteWin => 1.0,
                    GameResult::Draw => 0.5,
                    GameResult::BlackWin => 0.0,
                };

                let error = f32::powf(answer - monte_carlo::eval_to_win_pct(eval), 2.0);
                error
            }
            else {
                warn!("Found mate score while tuning {}.", board.to_fen());
                0.0
            }

    })
        .sum()
}

fn qsearch<B: ExtendedBoard>(board: &mut B, mut alpha: Score, beta: Score, params: &[f32])
    -> (Score, Vec<B::Move>)
    where B: TunableBoard + ExtendedBoard {
    let color = board.side_to_move();

    if let Some(result) = board.game_result() {
        return (Score::from_game_result(result, color), vec![])
    }

    let stand_pat = Score::Val(board.static_eval_with_params(params) * color.multiplier() as f32);

    if stand_pat >= beta {
        return (beta, vec![]);
    }

    alpha = alpha.max(stand_pat);
    let mut best_pv = vec![];
    let mut best_move = None;

    let mut active_moves = vec![];
    board.active_moves(&mut active_moves);

    for mv in active_moves {
        let reverse_move = board.do_move(mv.clone());

        let (mut score, mut pv) = qsearch(board, beta, alpha, params);

        score = !score;
        board.reverse_move(reverse_move);

        if score >= beta {
            return (score, vec![mv]);
        }
        if score > alpha {
            alpha = score;
            best_pv = pv;
            best_move = Some(mv);
        }
    }
    best_pv.extend(best_move);
    return (alpha, best_pv);

}
{'fraction_non_alphanumeric': 0.12861938732689887, 'fraction_numerical': 0.01174989509022241, 'mean_word_length': 2.0854368932038834, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 26, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5353594', 'n_tokens_mistral': 1538, 'n_tokens_neox': 1453, 'n_words': 341}