Skip to content

Commit 12bda91

Browse files
chore: integrate killers into engine
bench: 1046397
1 parent 8a3ea99 commit 12bda91

File tree

4 files changed

+121
-18
lines changed

4 files changed

+121
-18
lines changed

engine/src/engine.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use crate::{
2424
defs::About,
2525
history_table::HistoryTable,
2626
input_handler::{CommandProxy, EngineCommand, InputHandler},
27+
killer_moves_table::KillerMovesTable,
2728
log_level::{LogDebug, LogInfo, LogLevel},
2829
search::SearchParameters,
2930
search_thread::SearchThread,
@@ -35,6 +36,7 @@ pub struct ByteKnight {
3536
search_thread: SearchThread,
3637
transposition_table: Arc<Mutex<TranspositionTable>>,
3738
history_table: Arc<Mutex<HistoryTable>>,
39+
killers_table: Arc<Mutex<KillerMovesTable>>,
3840
debug: bool,
3941
}
4042

@@ -45,18 +47,26 @@ impl ByteKnight {
4547
search_thread: SearchThread::new(),
4648
transposition_table: Default::default(),
4749
history_table: Default::default(),
50+
killers_table: Default::default(),
4851
debug: false,
4952
}
5053
}
5154

5255
fn clear_hash_tables(&mut self) {
56+
// Clear transposition table
5357
if let Ok(tt) = self.transposition_table.lock().as_mut() {
5458
tt.clear();
5559
}
5660

61+
// Clear history table
5762
if let Ok(ht) = self.history_table.lock().as_mut() {
5863
ht.clear();
5964
}
65+
66+
// Clear killers table
67+
if let Ok(kt) = self.killers_table.lock().as_mut() {
68+
kt.clear();
69+
}
6070
}
6171

6272
/// Run the engine loop. This will block until the engine is told to quit by the input handler.
@@ -212,6 +222,7 @@ impl ByteKnight {
212222
params,
213223
Arc::clone(&self.transposition_table),
214224
Arc::clone(&self.history_table),
225+
Arc::clone(&self.killers_table),
215226
);
216227
}
217228
}

engine/src/move_order.rs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@ use arrayvec::ArrayVec;
55
use chess::{definitions::MAX_MOVE_LIST_SIZE, moves::Move, pieces::Piece, side::Side};
66

77
use crate::{
8-
evaluation::Evaluation, hce_values::ByteKnightValues, history_table, score::LargeScoreType,
8+
evaluation::Evaluation, hce_values::ByteKnightValues, history_table, killer_moves_table,
9+
score::LargeScoreType,
910
};
1011

1112
#[derive(PartialEq, Eq, Copy, Clone, Debug, Default)]
1213
pub enum MoveOrder {
1314
#[default]
1415
TtMove,
1516
Capture(Piece, Piece),
17+
Killer(LargeScoreType),
1618
Quiet(LargeScoreType),
1719
}
1820

@@ -44,6 +46,12 @@ impl Ord for MoveOrder {
4446
(MoveOrder::Capture(_, _), _) => Ordering::Less,
4547
(_, MoveOrder::Capture(_, _)) => Ordering::Greater,
4648

49+
// killer moves come next, according to their score
50+
(MoveOrder::Killer(left_score), MoveOrder::Killer(right_score)) => {
51+
right_score.cmp(left_score)
52+
}
53+
(MoveOrder::Killer(_), _) => Ordering::Less,
54+
(_, MoveOrder::Killer(_)) => Ordering::Greater,
4755
// quiet moves come last, according to their score
4856
(MoveOrder::Quiet(left_score), MoveOrder::Quiet(right_score)) => {
4957
right_score.cmp(left_score)
@@ -56,10 +64,12 @@ impl MoveOrder {
5664
/// Classify moves for move ordering purposes.
5765
#[allow(clippy::expect_used)]
5866
pub fn classify(
67+
ply: u8,
5968
stm: Side,
6069
mv: &Move,
6170
tt_move: &Option<Move>,
6271
history_table: &history_table::HistoryTable,
72+
killers_table: &killer_moves_table::KillerMovesTable,
6373
) -> Self {
6474
if tt_move.is_some_and(|tt| *mv == tt) {
6575
return Self::TtMove;
@@ -72,20 +82,37 @@ impl MoveOrder {
7282
}
7383

7484
let score = history_table.get(stm, mv.piece(), mv.to());
85+
if killers_table
86+
.get(ply)
87+
.iter()
88+
.any(|killer_mv| killer_mv.is_some_and(|k| k == *mv))
89+
{
90+
return Self::Killer(score);
91+
}
92+
7593
Self::Quiet(score)
7694
}
7795

7896
pub fn classify_all(
97+
ply: u8,
7998
stm: Side,
8099
moves: &[Move],
81100
tt_move: &Option<Move>,
82101
history_table: &history_table::HistoryTable,
102+
killers_table: &killer_moves_table::KillerMovesTable,
83103
move_order: &mut ArrayVec<MoveOrder, MAX_MOVE_LIST_SIZE>,
84104
) -> Result<()> {
85105
move_order.clear();
86106

87107
for mv in moves.iter() {
88-
move_order.try_push(Self::classify(stm, mv, tt_move, history_table))?;
108+
move_order.try_push(Self::classify(
109+
ply,
110+
stm,
111+
mv,
112+
tt_move,
113+
history_table,
114+
killers_table,
115+
))?;
89116
}
90117

91118
Ok(())
@@ -107,6 +134,7 @@ mod tests {
107134
fn verify_move_ordering() {
108135
let mut tt = TranspositionTable::from_capacity(10);
109136
let mut history_table = crate::history_table::HistoryTable::new();
137+
let killers_table = crate::killer_moves_table::KillerMovesTable::new();
110138

111139
let move_gen = MoveGenerator::new();
112140
let mut move_list = MoveList::new();
@@ -116,6 +144,7 @@ mod tests {
116144

117145
assert!(move_list.len() >= 6);
118146
let depth = 3i32;
147+
let ply = 3i32;
119148
let first_mv = move_list.at(4).unwrap();
120149
tt.store_entry(TranspositionTableEntry::new(
121150
board.zobrist_hash(),
@@ -132,13 +161,22 @@ mod tests {
132161
second_mv.to(),
133162
300 * depth - 250,
134163
);
164+
// TODO
165+
// killers_table.update(ply, mv);
135166
let tt_entry = tt.get_entry(board.zobrist_hash()).unwrap();
136167
let tt_move = tt_entry.board_move;
137168
// sort the moves
138169
let moves = move_list
139170
.iter()
140171
.sorted_by_key(|mv| {
141-
MoveOrder::classify(board.side_to_move(), mv, &Some(tt_move), &history_table)
172+
MoveOrder::classify(
173+
ply as u8,
174+
board.side_to_move(),
175+
mv,
176+
&Some(tt_move),
177+
&history_table,
178+
&killers_table,
179+
)
142180
})
143181
.collect::<Vec<&Move>>();
144182

engine/src/search.rs

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use crate::{
3636
evaluation::ByteKnightEvaluation,
3737
history_table::{self, HistoryTable},
3838
inplace_incremental_sort::InplaceIncrementalSort,
39+
killer_moves_table::KillerMovesTable,
3940
lmr,
4041
log_level::LogLevel,
4142
move_order::MoveOrder,
@@ -159,6 +160,7 @@ impl Display for SearchParameters {
159160
pub struct Search<'search_lifetime, Log> {
160161
transposition_table: &'search_lifetime mut TranspositionTable,
161162
history_table: &'search_lifetime mut HistoryTable,
163+
killers_table: &'search_lifetime mut KillerMovesTable,
162164
move_gen: MoveGenerator,
163165
nodes: u64,
164166
parameters: SearchParameters,
@@ -174,6 +176,7 @@ impl<'a, Log: LogLevel> Search<'a, Log> {
174176
parameters: &SearchParameters,
175177
ttable: &'a mut TranspositionTable,
176178
history_table: &'a mut HistoryTable,
179+
killers_table: &'a mut KillerMovesTable,
177180
) -> Self {
178181
// Initialize our LMR table as a 2D array of our LMR formula for depth and moves played
179182
let mut table = Table::<f64, 32_000>::new(MAX_DEPTH as usize, MAX_MOVE_LIST_SIZE);
@@ -182,6 +185,7 @@ impl<'a, Log: LogLevel> Search<'a, Log> {
182185
Self {
183186
transposition_table: ttable,
184187
history_table,
188+
killers_table,
185189
move_gen: MoveGenerator::new(),
186190
nodes: 0,
187191
parameters: parameters.clone(),
@@ -387,7 +391,7 @@ impl<'a, Log: LogLevel> Search<'a, Log> {
387391
let mut alpha_use = alpha;
388392

389393
if depth <= 0 {
390-
return self.quiescence::<Node>(board, alpha, beta, pv);
394+
return self.quiescence::<Node>(ply as u8, board, alpha, beta, pv);
391395
}
392396

393397
let mut local_pv = PrincipleVariation::new();
@@ -440,10 +444,12 @@ impl<'a, Log: LogLevel> Search<'a, Log> {
440444
}
441445

442446
let classify_res = MoveOrder::classify_all(
447+
ply as u8,
443448
board.side_to_move(),
444449
move_list.as_slice(),
445450
&tt_move,
446451
self.history_table,
452+
self.killers_table,
447453
&mut order_list,
448454
);
449455

@@ -535,6 +541,9 @@ impl<'a, Log: LogLevel> Search<'a, Log> {
535541
if alpha_use >= beta {
536542
// update history table for quiets
537543
if mv.is_quiet() {
544+
// store the "killer" move
545+
self.killers_table.update(ply as u8, mv);
546+
538547
// calculate history bonus
539548
let bonus = history_table::calculate_bonus_for_depth(depth);
540549
self.history_table.update(
@@ -676,6 +685,7 @@ impl<'a, Log: LogLevel> Search<'a, Log> {
676685
///
677686
fn quiescence<Node: NodeType>(
678687
&mut self,
688+
ply: u8,
679689
board: &mut Board,
680690
alpha: Score,
681691
beta: Score,
@@ -727,10 +737,12 @@ impl<'a, Log: LogLevel> Search<'a, Log> {
727737

728738
// sort moves by MVV/LVA
729739
let classify_res = MoveOrder::classify_all(
740+
ply,
730741
board.side_to_move(),
731742
captures.as_slice(),
732743
&tt_move,
733744
self.history_table,
745+
self.killers_table,
734746
&mut move_order_list,
735747
);
736748
// TODO(PT): Should we log a message to the CLI or a log?
@@ -752,7 +764,7 @@ impl<'a, Log: LogLevel> Search<'a, Log> {
752764
let score = if board.is_draw() {
753765
Score::DRAW
754766
} else {
755-
let eval = -self.quiescence::<Node>(board, -beta, -alpha_use, &mut local_pv);
767+
let eval = -self.quiescence::<Node>(ply, board, -beta, -alpha_use, &mut local_pv);
756768
self.nodes += 1;
757769
eval
758770
};
@@ -823,7 +835,9 @@ mod tests {
823835
fn run_search_tests(test_pairs: &[(&str, &str)], config: SearchParameters) {
824836
let mut ttable = TranspositionTable::default();
825837
let mut history_table = Default::default();
826-
let mut search = Search::<LogDebug>::new(&config, &mut ttable, &mut history_table);
838+
let mut killers_table = Default::default();
839+
let mut search =
840+
Search::<LogDebug>::new(&config, &mut ttable, &mut history_table, &mut killers_table);
827841

828842
for (fen, expected_move) in test_pairs {
829843
let mut board = Board::from_fen(fen).unwrap();
@@ -846,7 +860,9 @@ mod tests {
846860

847861
let mut ttable = TranspositionTable::default();
848862
let mut history_table = Default::default();
849-
let mut search = Search::<LogDebug>::new(&config, &mut ttable, &mut history_table);
863+
let mut killers_table = Default::default();
864+
let mut search =
865+
Search::<LogDebug>::new(&config, &mut ttable, &mut history_table, &mut killers_table);
850866
let res = search.search(&mut board.clone(), None);
851867
// b6a7
852868
assert_eq!(
@@ -866,7 +882,9 @@ mod tests {
866882

867883
let mut ttable = Default::default();
868884
let mut history_table = Default::default();
869-
let mut search = Search::<LogDebug>::new(&config, &mut ttable, &mut history_table);
885+
let mut killers_table = Default::default();
886+
let mut search =
887+
Search::<LogDebug>::new(&config, &mut ttable, &mut history_table, &mut killers_table);
870888
let res = search.search(&mut board, None);
871889

872890
assert_eq!(res.best_move.unwrap().to_long_algebraic(), "b8a8")
@@ -919,7 +937,9 @@ mod tests {
919937

920938
let mut ttable = Default::default();
921939
let mut history_table = Default::default();
922-
let mut search = Search::<LogDebug>::new(&config, &mut ttable, &mut history_table);
940+
let mut killers_table = Default::default();
941+
let mut search =
942+
Search::<LogDebug>::new(&config, &mut ttable, &mut history_table, &mut killers_table);
923943
let res = search.search(&mut board, None);
924944
assert!(res.best_move.is_none());
925945
assert_eq!(res.score, Score::DRAW);
@@ -937,7 +957,9 @@ mod tests {
937957

938958
let mut ttable = Default::default();
939959
let mut history_table = Default::default();
940-
let mut search = Search::<LogDebug>::new(&config, &mut ttable, &mut history_table);
960+
let mut killers_table = Default::default();
961+
let mut search =
962+
Search::<LogDebug>::new(&config, &mut ttable, &mut history_table, &mut killers_table);
941963
let res = search.search(&mut board, None);
942964

943965
assert!(res.best_move.is_some());
@@ -954,7 +976,9 @@ mod tests {
954976

955977
let mut ttable = Default::default();
956978
let mut history_table = Default::default();
957-
let mut search = Search::<LogDebug>::new(&config, &mut ttable, &mut history_table);
979+
let mut killers_table = Default::default();
980+
let mut search =
981+
Search::<LogDebug>::new(&config, &mut ttable, &mut history_table, &mut killers_table);
958982
let res = search.search(&mut board, None);
959983
assert!(res.best_move.is_some());
960984
println!("{}", res.best_move.unwrap().to_long_algebraic());
@@ -971,7 +995,9 @@ mod tests {
971995

972996
let mut ttable = Default::default();
973997
let mut history_table = Default::default();
974-
let mut search = Search::<LogDebug>::new(&config, &mut ttable, &mut history_table);
998+
let mut killers_table = Default::default();
999+
let mut search =
1000+
Search::<LogDebug>::new(&config, &mut ttable, &mut history_table, &mut killers_table);
9751001
let res = search.search(&mut board, None);
9761002
assert!(res.best_move.is_some());
9771003
println!("{}", res.best_move.unwrap().to_long_algebraic());
@@ -1031,7 +1057,13 @@ mod tests {
10311057

10321058
let mut ttable = Default::default();
10331059
let mut history_table = Default::default();
1034-
let mut search = Search::<LogDebug>::new(&config, &mut ttable, &mut history_table);
1060+
let mut killers_table = Default::default();
1061+
let mut search = Search::<LogDebug>::new(
1062+
&config,
1063+
&mut ttable,
1064+
&mut history_table,
1065+
&mut killers_table,
1066+
);
10351067
let res = search.search(&mut board, None);
10361068

10371069
assert!(res.best_move.is_some());

0 commit comments

Comments
 (0)