Make mvv_lva to map pieces to their material score instead of abritrary matrix
Add time limits, use anyhow crate to improve error handling, remove depth limit on quiescence search
This commit is contained in:
@@ -1,14 +1,17 @@
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
|
||||
use crate::{
|
||||
board::game::Game,
|
||||
evaluation::{MATE_SCORE, MIN_SCORE},
|
||||
movegen::r#move::Move,
|
||||
};
|
||||
|
||||
use super::{
|
||||
move_ordering::score_by_mvv_lva,
|
||||
iterative_deepening::hard_limit,
|
||||
move_ordering::mvv_lva,
|
||||
quiescence::quiescence,
|
||||
time::TimeInfo,
|
||||
transposition_table::{Bound, TTEntry, TranspositionTable},
|
||||
QUIESCENCE_DEPTH,
|
||||
SearchResult,
|
||||
};
|
||||
|
||||
pub fn negamax(
|
||||
@@ -17,11 +20,16 @@ pub fn negamax(
|
||||
beta: i32,
|
||||
depth: u8,
|
||||
plies: u8,
|
||||
time_info: &TimeInfo,
|
||||
tt: &mut TranspositionTable,
|
||||
) -> (Option<Move>, i32) {
|
||||
) -> Result<SearchResult> {
|
||||
if hard_limit(&time_info.time, time_info.remaining_time_in_ms) {
|
||||
bail!("Time is up! In Negamax");
|
||||
}
|
||||
|
||||
if depth == 0 {
|
||||
let q = quiescence(game, alpha, beta, QUIESCENCE_DEPTH).1;
|
||||
return (None, q);
|
||||
let q_score = quiescence(game, alpha, beta, time_info).map_err(|e| anyhow!("{e}"))?;
|
||||
return Ok(SearchResult::new(None, q_score));
|
||||
}
|
||||
|
||||
let mut tt_move = None;
|
||||
@@ -32,7 +40,7 @@ pub fn negamax(
|
||||
|| (matches!(entry.bound, Bound::Lower) && entry.score >= beta)
|
||||
|| (matches!(entry.bound, Bound::Upper) && entry.score <= alpha))
|
||||
{
|
||||
return (entry.mv, entry.score);
|
||||
return Ok(SearchResult::new(entry.mv, entry.score));
|
||||
}
|
||||
tt_move = entry.mv;
|
||||
}
|
||||
@@ -41,7 +49,7 @@ pub fn negamax(
|
||||
let (mut best_move, mut best_score, mate_score) = (None, MIN_SCORE, -MATE_SCORE + plies as i32);
|
||||
let mut legal_moves = 0;
|
||||
let mut pseudo_legal_moves = game.board.pseudo_moves_all();
|
||||
pseudo_legal_moves.sort_unstable_by_key(|mv| score_by_mvv_lva(&game.mailbox, *mv));
|
||||
pseudo_legal_moves.sort_unstable_by_key(|mv| mvv_lva(&game.mailbox, *mv));
|
||||
|
||||
if let Some(tt_move) = tt_move {
|
||||
if let Some(tt_move_index) = pseudo_legal_moves.iter().position(|&mv| mv == tt_move) {
|
||||
@@ -59,7 +67,8 @@ pub fn negamax(
|
||||
}
|
||||
|
||||
legal_moves += 1;
|
||||
let move_score = -negamax(game, -beta, -alpha, depth - 1, plies + 1, tt).1;
|
||||
let move_score =
|
||||
-negamax(game, -beta, -alpha, depth - 1, plies + 1, time_info, tt)?.best_score;
|
||||
game.unmake_move();
|
||||
|
||||
if move_score > best_score {
|
||||
@@ -75,7 +84,7 @@ pub fn negamax(
|
||||
best_move,
|
||||
Bound::Lower,
|
||||
));
|
||||
return (best_move, beta);
|
||||
return Ok(SearchResult::new(best_move, beta));
|
||||
}
|
||||
|
||||
alpha = alpha.max(move_score);
|
||||
@@ -90,10 +99,10 @@ pub fn negamax(
|
||||
best_move,
|
||||
Bound::Exact,
|
||||
));
|
||||
return (None, mate_score);
|
||||
return Ok(SearchResult::new(None, mate_score));
|
||||
}
|
||||
tt.insert(TTEntry::new(game.hash, depth, 0, best_move, Bound::Exact));
|
||||
return (None, 0);
|
||||
return Ok(SearchResult::new(None, 0));
|
||||
}
|
||||
|
||||
if best_score < alpha {
|
||||
@@ -114,7 +123,7 @@ pub fn negamax(
|
||||
));
|
||||
}
|
||||
|
||||
(best_move, best_score)
|
||||
Ok(SearchResult::new(best_move, best_score))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -124,6 +133,7 @@ mod tests {
|
||||
use crate::movegen::attack_generator::init_attacks;
|
||||
use crate::movegen::r#move::Move;
|
||||
use crate::search::negamax::negamax;
|
||||
use crate::search::time::TimeInfo;
|
||||
use crate::search::transposition_table::TranspositionTable;
|
||||
use crate::search::MAX_TT_SIZE;
|
||||
|
||||
@@ -133,24 +143,29 @@ mod tests {
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_negamax() -> Result<(), String> {
|
||||
fn test_negamax() -> anyhow::Result<()> {
|
||||
init_attacks();
|
||||
let mut game = from_fen(FEN_MATE_IN_1[0])?;
|
||||
let mut game = from_fen(FEN_MATE_IN_1[0]).unwrap();
|
||||
|
||||
let mut tt = TranspositionTable::new(MAX_TT_SIZE);
|
||||
let e3f2 = Move::new(Square::E3, Square::F2);
|
||||
let anointed_move = negamax(&mut game, MIN_SCORE, MAX_SCORE, 2, 0, &mut tt)
|
||||
.0
|
||||
.unwrap();
|
||||
let time_info = TimeInfo::new(std::time::Instant::now(), 1000);
|
||||
let anointed_move = negamax(&mut game, MIN_SCORE, MAX_SCORE, 2, 0, &time_info, &mut tt)
|
||||
.expect("Expected a search result")
|
||||
.best_move
|
||||
.expect("Expected a move");
|
||||
|
||||
assert_eq!(e3f2, anointed_move);
|
||||
|
||||
// let mut game = from_fen(FEN_MATE_IN_1[1])?;
|
||||
let mut game = from_fen(FEN_MATE_IN_1[1]).unwrap();
|
||||
|
||||
// let e3f2 = Move::new(Square::E3, Square::F2);
|
||||
// let anointed_move = negamax(&mut game, MIN_SCORE, MAX_SCORE, 2, 0).0.unwrap();
|
||||
let e3f2 = Move::new(Square::E3, Square::F2);
|
||||
let anointed_move = negamax(&mut game, MIN_SCORE, MAX_SCORE, 2, 0, &time_info, &mut tt)
|
||||
.expect("Expected a search result")
|
||||
.best_move
|
||||
.expect("Expected a move");
|
||||
|
||||
// assert_eq!(e3f2, anointed_move);
|
||||
assert_eq!(e3f2, anointed_move);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user