From 0842beff58d724956b457aa2a7f8b10191150767 Mon Sep 17 00:00:00 2001 From: Kyle Krowpman Date: Sat, 16 Nov 2024 23:43:11 -0800 Subject: [PATCH] a few optimizations --- src/board.rs | 52 +++++++++++++++++++++----------------------- src/moov.rs | 4 ++-- src/search.rs | 60 ++++++++++++++++++++++++++++++++------------------- src/timer.rs | 6 +++--- src/tt.rs | 9 ++++---- 5 files changed, 71 insertions(+), 60 deletions(-) diff --git a/src/board.rs b/src/board.rs index b0fd733..bc314ce 100644 --- a/src/board.rs +++ b/src/board.rs @@ -491,20 +491,21 @@ impl Board { let checker_square = checkers.lsb(); let pt = self .piece_type_at(checker_square) - .expect(format!("No checker at {}", checker_square).as_str()); + .expect("Checker expected."); match pt { PieceType::Pawn | PieceType::Knight => { /////////////////////////////////////////////////////////////////// // If the checkers is a pawn, we have to look out for ep moves // that can capture it. /////////////////////////////////////////////////////////////////// - let epsq = self.history[self.ply].epsq(); if pt == PieceType::Pawn - && epsq.is_some_and(|epsq| { + && self.history[self.ply].epsq().is_some_and(|epsq| { checkers == epsq.bb().shift(Direction::South.relative(us)) }) { - let epsq = epsq.expect("No epsq found for checker."); + let epsq = self.history[self.ply] + .epsq() + .expect("No epsq found for checker."); let pawns = attacks::pawn_attacks_sq(epsq, them) & self.bitboard_of(us, PieceType::Pawn) & not_pinned; @@ -630,9 +631,9 @@ impl Board { /////////////////////////////////////////////////////////////////// let pinned_pieces = !(not_pinned | self.bitboard_of(us, PieceType::Knight)); for sq in pinned_pieces { - let pt = self.piece_type_at(sq).expect( - format!("Unexpected None for piece type at square {}.", sq).as_str(), - ); + let pt = self + .piece_type_at(sq) + .expect("Unexpected None for piece type."); let attacks_along_pin = attacks::attacks(pt, sq, all) & Bitboard::line(our_king, sq); if QUIET { @@ -886,22 +887,20 @@ impl Board { Some(PieceType::Bishop) => MoveFlags::PrBishop, Some(PieceType::Rook) => MoveFlags::PrRook, None => { - if self.piece_type_at(from_sq) == Some(PieceType::Pawn) - && Some(to_sq) == self.history[self.ply].epsq() - { + let moved_pt = moved_pc.type_of(); + if moved_pt == PieceType::Pawn && Some(to_sq) == self.history[self.ply].epsq() { MoveFlags::EnPassant - } else if self.piece_type_at(from_sq) == Some(PieceType::Pawn) - && (from_sq as i8 - to_sq as i8).abs() == 16 + } else if moved_pt == PieceType::Pawn + && from_sq.relative(self.ctm) + Direction::NorthNorth + == to_sq.relative(self.ctm) { MoveFlags::DoublePush - } else if self.piece_type_at(from_sq) == Some(PieceType::King) - && from_sq.file() == File::E - && to_sq.file() == File::G + } else if moved_pt == PieceType::King + && (from_sq.file(), to_sq.file()) == (File::E, File::G) { MoveFlags::OO - } else if self.piece_type_at(from_sq) == Some(PieceType::King) - && from_sq.file() == File::E - && to_sq.file() == File::C + } else if moved_pt == PieceType::King + && (from_sq.file(), to_sq.file()) == (File::E, File::C) { MoveFlags::OOO } else { @@ -934,22 +933,20 @@ impl Board { let pieces_placement = parts.next().ok_or("Invalid piece placement.")?; let ctm = parts .next() - .ok_or("Invalid color.")? - .chars() - .next() + .and_then(|s| s.chars().next()) .ok_or("Invalid color.")?; let castling_ability = parts.next().ok_or("Invalid castling.")?; let en_passant_square = parts.next().unwrap_or("-"); let halfmove_clock = parts .next() .unwrap_or("0") - .parse() - .or(Err("Unable to parse half move clock"))?; + .parse::() + .map_err(|_| "Unable to parse half move clock")?; let fullmove_counter = parts .next() .unwrap_or("1") .parse::() - .or(Err("Unable to parse full move counter."))? + .map_err(|_| "Unable to parse full move counter.")? .max(1); if pieces_placement.split('/').count() != Rank::N_RANKS { @@ -1136,10 +1133,9 @@ impl fmt::Debug for Board { for file_idx in 0..=7 { let file = File::from(file_idx); let sq = SQ::encode(rank, file); - let pc_str = match self.piece_at(sq) { - Some(pc) => pc.to_string(), - None => "-".to_string(), - }; + let pc_str = self + .piece_at(sq) + .map_or("-".to_string(), |pc| pc.to_string()); s.push_str(&pc_str); s.push(' '); if sq.file() == File::H { diff --git a/src/moov.rs b/src/moov.rs index 1da9861..4a22fec 100644 --- a/src/moov.rs +++ b/src/moov.rs @@ -65,8 +65,8 @@ impl fmt::Display for Move { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}{}", self.from_sq(), self.to_sq())?; - if let Some(promotion_pc) = self.promotion() { - write!(f, "{}", promotion_pc)?; + if let Some(promotion_pt) = self.promotion() { + write!(f, "{}", promotion_pt)?; } Ok(()) diff --git a/src/search.rs b/src/search.rs index d61105b..5e1e906 100644 --- a/src/search.rs +++ b/src/search.rs @@ -80,9 +80,7 @@ impl<'a> Search<'a> { } else { // Only print info if we're in the main thread if self.id == 0 && !self.stop { - if let Some(best_move) = best_move { - self.print_info(&mut board, depth, best_move, value); - } + best_move.inspect(|&m| self.print_info(&mut board, depth, m, value)); } alpha = value - Self::ASPIRATION_WINDOW; beta = value + Self::ASPIRATION_WINDOW; @@ -157,9 +155,13 @@ impl<'a> Search<'a> { idx += 1; } - if best_move.is_none() && moves.len() > 0 { - best_move = Some(moves[0]); - } + best_move = best_move.or_else(|| { + if moves.len() > 0 { + Some(moves[0]) + } else { + None + } + }); if !self.stop { self.tt.insert(board, depth, alpha, best_move, Bound::Exact); @@ -244,7 +246,7 @@ impl<'a> Search<'a> { // Reverse Futility Pruning /////////////////////////////////////////////////////////////////// if Self::can_apply_rfp(depth, in_check, is_pv, beta) { - let eval = tt_entry.map_or(board.eval(), |entry| entry.value()); + let eval = tt_entry.map_or_else(|| board.eval(), |entry| entry.value()); if eval - Self::rfp_margin(depth) >= beta { return eval; @@ -361,11 +363,15 @@ impl<'a> Search<'a> { } } - if best_move.is_none() && moves.len() > 0 { - best_move = Some(moves[0]); - } - if !self.stop { + best_move = best_move.or_else(|| { + if moves.len() > 0 { + Some(moves[0]) + } else { + None + } + }); + self.tt.insert(board, depth, alpha, best_move, tt_flag); } alpha @@ -398,8 +404,8 @@ impl<'a> Search<'a> { } alpha = alpha.max(eval); - let mut hash_move = None; - if let Some(tt_entry) = self.tt.probe(board) { + let tt_entry = self.tt.probe(board); + if let Some(tt_entry) = tt_entry { match tt_entry.flag() { Bound::Exact => return tt_entry.value(), Bound::Lower => alpha = alpha.max(tt_entry.value()), @@ -408,15 +414,24 @@ impl<'a> Search<'a> { if alpha >= beta { return tt_entry.value(); } - hash_move = tt_entry.best_move(); } let mut moves = MoveList::from_q(board); - self.move_sorter - .score_moves(&mut moves, board, ply, hash_move); + self.move_sorter.score_moves( + &mut moves, + board, + ply, + tt_entry.and_then(|entry| entry.best_move()), + ); let mut idx = 0; while let Some(m) = moves.next_best() { + if m.is_quiet() { + println!("{}", board); + println!("{}", m); + println!("{:?}", m.flags()); + } + debug_assert!(!m.is_quiet()); /////////////////////////////////////////////////////////////////// // Effectively a SEE check. Bad captures will have a score < 0 // given by the SEE + the bad capture offset, @@ -498,12 +513,13 @@ impl<'a> Search<'a> { if let Some(tt_entry) = self.tt.probe(board) { let mut pv = String::new(); - if let Some(hash_move) = tt_entry.best_move() { - if MoveList::from(board).contains(hash_move) { - board.push(hash_move); - pv = format!("{} {}", hash_move, self.get_pv(board, depth - 1)); - board.pop(); - } + if let Some(m) = tt_entry + .best_move() + .filter(|&m| MoveList::from(board).contains(m)) + { + board.push(m); + pv = format!("{} {}", m, self.get_pv(board, depth - 1)); + board.pop(); } return pv; } diff --git a/src/timer.rs b/src/timer.rs index a5a93b4..484da86 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -121,13 +121,13 @@ impl Timer { Color::Black => (btime, binc), }; - let mtg = moves_to_go.unwrap_or( + let mtg = moves_to_go.unwrap_or_else(|| { (Self::MTG_INTERCEPT + Self::MTG_EVAL_WEIGHT * (board.simple_eval().abs() as f32) + Self::MTG_MOVE_WEIGHT * (board.fullmove_number() as f32)) .ceil() - .max(1.0) as u32, - ); + .max(1.0) as u32 + }); time_target = time.min(time / mtg + inc.unwrap_or(Duration::ZERO)); time_maximum = time_target + (time - time_target) / 4; diff --git a/src/tt.rs b/src/tt.rs index 583889e..d7f83d7 100644 --- a/src/tt.rs +++ b/src/tt.rs @@ -22,7 +22,7 @@ pub struct TTEntry { impl TTEntry { pub fn new(value: Value, best_move: Option, depth: Depth, flag: Bound) -> Self { TTEntry { - best_move: best_move.map(|m| m.move_int()).unwrap_or(0), + best_move: best_move.map_or(0, |m| m.move_int()), depth, value, flag, @@ -30,10 +30,9 @@ impl TTEntry { } pub fn best_move(&self) -> Option { - if self.best_move > 0 { - Some(Move::from(self.best_move)) - } else { - None + match self.best_move { + 0 => None, + 1.. => Some(Move::from(self.best_move)), } }