Add history heuristics, reorder killer move to come after captures
This commit is contained in:
@@ -38,6 +38,7 @@ pub fn from_fen(fen: &str) -> Result<Game, FenError> {
|
|||||||
hash,
|
hash,
|
||||||
tt: TranspositionTable::new(),
|
tt: TranspositionTable::new(),
|
||||||
killer: [None; MAX_DEPTH as usize],
|
killer: [None; MAX_DEPTH as usize],
|
||||||
|
history_heuristic: [[[0; 64]; 64]; 2],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ pub struct Game {
|
|||||||
pub hash: ZobristHash,
|
pub hash: ZobristHash,
|
||||||
pub tt: TranspositionTable,
|
pub tt: TranspositionTable,
|
||||||
pub killer: [Option<Move>; MAX_DEPTH as usize],
|
pub killer: [Option<Move>; MAX_DEPTH as usize],
|
||||||
|
pub history_heuristic: [[[i16; 64]; 64]; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
@@ -41,6 +42,7 @@ impl Game {
|
|||||||
hash: zobrist_keys().calculate_hash(&Board::startpos()),
|
hash: zobrist_keys().calculate_hash(&Board::startpos()),
|
||||||
tt: TranspositionTable::new(),
|
tt: TranspositionTable::new(),
|
||||||
killer: [None; MAX_DEPTH as usize],
|
killer: [None; MAX_DEPTH as usize],
|
||||||
|
history_heuristic: [[[0; 64]; 64]; 2],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,3 +12,4 @@ pub const INC: u128 = 80;
|
|||||||
pub const HARD_LIMIT_DIVISION: u128 = 3;
|
pub const HARD_LIMIT_DIVISION: u128 = 3;
|
||||||
pub const SOFT_LIMIT_DIVISION: u128 = 20;
|
pub const SOFT_LIMIT_DIVISION: u128 = 20;
|
||||||
pub const TT_SIZE_IN_MB: usize = 64;
|
pub const TT_SIZE_IN_MB: usize = 64;
|
||||||
|
pub const MAX_HISTORY: i16 = 8192;
|
||||||
|
|||||||
@@ -1,25 +1,36 @@
|
|||||||
use crate::{board::mailbox::Mailbox, movegen::r#move::Move};
|
use crate::{
|
||||||
|
board::{board::Color, mailbox::Mailbox},
|
||||||
|
movegen::r#move::Move,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::MAX_HISTORY;
|
||||||
|
|
||||||
|
// ttmove: -100, mvvlva: [-32, 5], killer:6, quiet[7, ..]
|
||||||
pub fn score_move(
|
pub fn score_move(
|
||||||
mailbox: &Mailbox,
|
mailbox: &Mailbox,
|
||||||
mv: Move,
|
mv: Move,
|
||||||
killer_move: Option<Move>,
|
killer_move: Option<Move>,
|
||||||
|
color: Color,
|
||||||
|
history_heuristic: &[[[i16; 64]; 64]; 2],
|
||||||
tt_move: Option<Move>,
|
tt_move: Option<Move>,
|
||||||
) -> i16 {
|
) -> i16 {
|
||||||
if Some(mv) == tt_move {
|
if Some(mv) == tt_move {
|
||||||
return -100;
|
return -100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mv.is_capture() {
|
||||||
|
let aggressor = mailbox.piece_at(mv.src()).unwrap();
|
||||||
|
if let Some(victim) = mailbox.piece_at(mv.dst()) {
|
||||||
|
return aggressor.0.idx() as i16 - 8 * victim.0.idx() as i16;
|
||||||
|
}
|
||||||
|
return 0; //en passant
|
||||||
|
}
|
||||||
|
|
||||||
if Some(mv) == killer_move {
|
if Some(mv) == killer_move {
|
||||||
return -90;
|
return 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
let aggressor = mailbox.piece_at(mv.src()).expect("No aggressor found.");
|
-history_heuristic[color][mv.src()][mv.dst()] + MAX_HISTORY + 6 + 1
|
||||||
if let Some(victim) = mailbox.piece_at(mv.dst()) {
|
|
||||||
return (aggressor.0.idx() - 8 * victim.0.idx()) as i16;
|
|
||||||
}
|
|
||||||
|
|
||||||
100
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -41,11 +52,29 @@ mod tests {
|
|||||||
let castle = Move::new_with_type(Square::E1, Square::C1, MoveType::QueenCastle);
|
let castle = Move::new_with_type(Square::E1, Square::C1, MoveType::QueenCastle);
|
||||||
|
|
||||||
let mut moves = vec![castle, queen_takes_pawn, pawn_takes_queen];
|
let mut moves = vec![castle, queen_takes_pawn, pawn_takes_queen];
|
||||||
moves.sort_unstable_by_key(|mv| score_move(&game.mailbox, *mv, None, None));
|
moves.sort_unstable_by_key(|mv| {
|
||||||
|
score_move(
|
||||||
|
&game.mailbox,
|
||||||
|
*mv,
|
||||||
|
None,
|
||||||
|
game.current_player(),
|
||||||
|
&[[[0; 64]; 64]; 2],
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
assert_eq!(moves, vec![pawn_takes_queen, queen_takes_pawn, castle]);
|
assert_eq!(moves, vec![pawn_takes_queen, queen_takes_pawn, castle]);
|
||||||
|
|
||||||
moves.sort_unstable_by_key(|mv| score_move(&game.mailbox, *mv, None, Some(castle)));
|
moves.sort_unstable_by_key(|mv| {
|
||||||
|
score_move(
|
||||||
|
&game.mailbox,
|
||||||
|
*mv,
|
||||||
|
None,
|
||||||
|
game.current_player(),
|
||||||
|
&[[[0; 64]; 64]; 2],
|
||||||
|
Some(castle),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
assert_eq!(moves, vec![castle, pawn_takes_queen, queen_takes_pawn]);
|
assert_eq!(moves, vec![castle, pawn_takes_queen, queen_takes_pawn]);
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use super::{
|
|||||||
quiescence::quiescence,
|
quiescence::quiescence,
|
||||||
time::TimeInfo,
|
time::TimeInfo,
|
||||||
transposition_table::{NodeType, TTEntry},
|
transposition_table::{NodeType, TTEntry},
|
||||||
|
MAX_HISTORY,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn negamax(
|
pub fn negamax(
|
||||||
@@ -78,6 +79,8 @@ pub fn negamax(
|
|||||||
&game.mailbox,
|
&game.mailbox,
|
||||||
*mv,
|
*mv,
|
||||||
game.killer[plies as usize],
|
game.killer[plies as usize],
|
||||||
|
color,
|
||||||
|
&game.history_heuristic,
|
||||||
entry.and_then(|entry| entry.mv),
|
entry.and_then(|entry| entry.mv),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
@@ -129,8 +132,16 @@ pub fn negamax(
|
|||||||
if score >= beta {
|
if score >= beta {
|
||||||
if !mv.is_capture() {
|
if !mv.is_capture() {
|
||||||
game.killer[plies as usize] = Some(mv);
|
game.killer[plies as usize] = Some(mv);
|
||||||
|
|
||||||
|
let current_score = game.history_heuristic[color][mv.src()][mv.dst()];
|
||||||
|
let bonus = (depth * depth) as i16;
|
||||||
|
let clamped_bonus = bonus.clamp(-MAX_HISTORY, MAX_HISTORY);
|
||||||
|
game.history_heuristic[color][mv.src()][mv.dst()] +=
|
||||||
|
clamped_bonus - current_score * clamped_bonus.abs() / MAX_HISTORY;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
} else if !mv.is_capture() {
|
||||||
|
game.history_heuristic[color][mv.src()][mv.dst()] -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,14 @@ pub fn quiescence(
|
|||||||
let mut moves = game.board.pseudo_moves_all_captures();
|
let mut moves = game.board.pseudo_moves_all_captures();
|
||||||
|
|
||||||
moves.sort_unstable_by_key(|mv| {
|
moves.sort_unstable_by_key(|mv| {
|
||||||
score_move(&game.mailbox, *mv, None, entry.and_then(|entry| entry.mv))
|
score_move(
|
||||||
|
&game.mailbox,
|
||||||
|
*mv,
|
||||||
|
None,
|
||||||
|
color,
|
||||||
|
&[[[0; 64]; 64]; 2],
|
||||||
|
entry.and_then(|entry| entry.mv),
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
for mv in moves {
|
for mv in moves {
|
||||||
|
|||||||
Reference in New Issue
Block a user