use super::{Feature, TriggerEvent, BOARD_PIECE_TYPES};
use crate::nnue::accumulator::{DirtyPiece, IndexList, MAX_ACTIVE_FEATURES, MAX_CHANGED_FEATURES};
use crate::nnue::bona_piece::PIECE_BASE;
use crate::nnue::bona_piece_halfka::{halfka_index, king_bonapiece, king_index, BonaPieceHalfKA};
use crate::nnue::constants::HALFKA_DIMENSIONS;
use crate::position::Position;
use crate::types::{Color, PieceType, Square};
pub struct HalfKA;
impl Feature for HalfKA {
const DIMENSIONS: usize = HALFKA_DIMENSIONS;
const MAX_ACTIVE: usize = 40;
const REFRESH_TRIGGER: TriggerEvent = TriggerEvent::FriendKingMoved;
#[inline]
fn append_active_indices(
pos: &Position,
perspective: Color,
active: &mut IndexList<MAX_ACTIVE_FEATURES>,
) {
let king_sq = pos.king_square(perspective);
let k_index = king_index(king_sq, perspective);
for color in [Color::Black, Color::White] {
let is_friend = (color == perspective) as usize;
for &pt in &BOARD_PIECE_TYPES {
let base = PIECE_BASE[pt as usize][is_friend];
let bb = pos.pieces(color, pt);
for sq in bb.iter() {
let sq_index = if perspective == Color::Black {
sq.index()
} else {
sq.inverse().index()
};
let bp = BonaPieceHalfKA::new(base + sq_index as u16);
let _ = active.push(halfka_index(k_index, bp.value() as usize));
}
}
}
let friend_king_sq_index = if perspective == Color::Black {
king_sq.index()
} else {
king_sq.inverse().index()
};
let friend_king_bp = king_bonapiece(friend_king_sq_index, true); let _ = active.push(halfka_index(k_index, friend_king_bp.value() as usize));
let enemy = perspective.opponent();
let enemy_king_sq = pos.king_square(enemy);
let enemy_king_sq_index = if perspective == Color::Black {
enemy_king_sq.index()
} else {
enemy_king_sq.inverse().index()
};
let enemy_king_bp = king_bonapiece(enemy_king_sq_index, false); let _ = active.push(halfka_index(k_index, enemy_king_bp.value() as usize));
for owner in [Color::Black, Color::White] {
for pt in [
PieceType::Pawn,
PieceType::Lance,
PieceType::Knight,
PieceType::Silver,
PieceType::Gold,
PieceType::Bishop,
PieceType::Rook,
] {
let count = pos.hand(owner).count(pt) as u8;
if count == 0 {
continue;
}
for i in 1..=count {
let bp = BonaPieceHalfKA::from_hand_piece(perspective, owner, pt, i);
if bp != BonaPieceHalfKA::ZERO {
let _ = active.push(halfka_index(k_index, bp.value() as usize));
}
}
}
}
}
#[inline]
fn append_changed_indices(
dirty_piece: &DirtyPiece,
perspective: Color,
king_sq: Square,
removed: &mut IndexList<MAX_CHANGED_FEATURES>,
added: &mut IndexList<MAX_CHANGED_FEATURES>,
) {
let k_index = king_index(king_sq, perspective);
for dp in dirty_piece.pieces() {
if dp.old_piece.is_some() {
if let Some(sq) = dp.old_sq {
let sq_index = if perspective == Color::Black {
sq.index()
} else {
sq.inverse().index()
};
let bp = if dp.old_piece.piece_type() == PieceType::King {
let is_friend = dp.color == perspective;
king_bonapiece(sq_index, is_friend)
} else {
BonaPieceHalfKA::from_piece_square(dp.old_piece, sq, perspective)
};
if bp != BonaPieceHalfKA::ZERO {
let _ = removed.push(halfka_index(k_index, bp.value() as usize));
}
}
}
if dp.new_piece.is_some() {
if let Some(sq) = dp.new_sq {
let sq_index = if perspective == Color::Black {
sq.index()
} else {
sq.inverse().index()
};
let bp = if dp.new_piece.piece_type() == PieceType::King {
let is_friend = dp.color == perspective;
king_bonapiece(sq_index, is_friend)
} else {
BonaPieceHalfKA::from_piece_square(dp.new_piece, sq, perspective)
};
if bp != BonaPieceHalfKA::ZERO {
let _ = added.push(halfka_index(k_index, bp.value() as usize));
}
}
}
}
for hc in dirty_piece.hand_changes() {
if hc.old_count < hc.new_count {
for i in (hc.old_count + 1)..=hc.new_count {
let bp =
BonaPieceHalfKA::from_hand_piece(perspective, hc.owner, hc.piece_type, i);
if bp != BonaPieceHalfKA::ZERO {
let _ = added.push(halfka_index(k_index, bp.value() as usize));
}
}
} else if hc.old_count > hc.new_count {
for i in (hc.new_count + 1)..=hc.old_count {
let bp =
BonaPieceHalfKA::from_hand_piece(perspective, hc.owner, hc.piece_type, i);
if bp != BonaPieceHalfKA::ZERO {
let _ = removed.push(halfka_index(k_index, bp.value() as usize));
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_halfka_dimensions() {
assert_eq!(HalfKA::DIMENSIONS, 138_510);
}
#[test]
fn test_halfka_max_active() {
assert_eq!(HalfKA::MAX_ACTIVE, 40);
}
#[test]
fn test_halfka_refresh_trigger() {
assert_eq!(HalfKA::REFRESH_TRIGGER, TriggerEvent::FriendKingMoved);
}
}