use super::Feature;
use super::TriggerEvent;
use crate::nnue::accumulator::{DirtyPiece, IndexList, MAX_ACTIVE_FEATURES, MAX_CHANGED_FEATURES};
use crate::nnue::bona_piece::BonaPiece;
use crate::nnue::bona_piece_halfka_hm::{halfka_index, is_hm_mirror, king_bucket, pack_bonapiece};
use crate::nnue::constants::HALFKA_HM_DIMENSIONS;
use crate::nnue::piece_list::PieceNumber;
use crate::position::Position;
use crate::types::{Color, Square};
#[allow(non_camel_case_types)]
pub struct HalfKA_hm;
impl Feature for HalfKA_hm {
const DIMENSIONS: usize = HALFKA_HM_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 kb = king_bucket(king_sq, perspective);
let hm_mirror = is_hm_mirror(king_sq, perspective);
let pieces = if perspective == Color::Black {
pos.piece_list().piece_list_fb()
} else {
pos.piece_list().piece_list_fw()
};
for bp in &pieces[..PieceNumber::NB] {
if *bp != BonaPiece::ZERO {
let packed = pack_bonapiece(*bp, hm_mirror);
let _ = active.push(halfka_index(kb, packed));
}
}
}
#[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 kb = king_bucket(king_sq, perspective);
let hm_mirror = is_hm_mirror(king_sq, perspective);
for i in 0..dirty_piece.dirty_num as usize {
let cp = &dirty_piece.changed_piece[i];
let old_bp = if perspective == Color::Black {
cp.old_piece.fb
} else {
cp.old_piece.fw
};
let new_bp = if perspective == Color::Black {
cp.new_piece.fb
} else {
cp.new_piece.fw
};
if old_bp != BonaPiece::ZERO {
let packed = pack_bonapiece(old_bp, hm_mirror);
let _ = removed.push(halfka_index(kb, packed));
}
if new_bp != BonaPiece::ZERO {
let packed = pack_bonapiece(new_bp, hm_mirror);
let _ = added.push(halfka_index(kb, packed));
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::nnue::accumulator::{ChangedBonaPiece, DirtyPiece};
use crate::nnue::bona_piece::ExtBonaPiece;
use crate::nnue::constants::BASE_INPUTS_HALFKA;
use crate::nnue::piece_list::PieceNumber;
use crate::position::Position;
use crate::types::{Color, File, Piece, PieceType, Rank, Square};
#[test]
fn test_halfka_hm_dimensions() {
assert_eq!(HalfKA_hm::DIMENSIONS, 73_305);
assert_eq!(HalfKA_hm::DIMENSIONS, BASE_INPUTS_HALFKA);
}
#[test]
fn test_halfka_hm_max_active() {
assert_eq!(HalfKA_hm::MAX_ACTIVE, 40);
}
#[test]
fn test_halfka_hm_refresh_trigger() {
assert_eq!(HalfKA_hm::REFRESH_TRIGGER, TriggerEvent::FriendKingMoved);
}
#[test]
fn test_append_active_indices_startpos() {
let mut pos = Position::new();
pos.set_sfen("lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1")
.unwrap();
let mut active = IndexList::new();
HalfKA_hm::append_active_indices(&pos, Color::Black, &mut active);
assert_eq!(active.len(), 40);
}
#[test]
fn test_append_changed_indices_piece_move() {
let sq_77 = Square::new(File::File7, Rank::Rank7);
let sq_76 = Square::new(File::File7, Rank::Rank6);
let king_sq = Square::new(File::File5, Rank::Rank9);
let old_bp = ExtBonaPiece::from_board(Piece::B_PAWN, sq_77);
let new_bp = ExtBonaPiece::from_board(Piece::B_PAWN, sq_76);
let mut dirty_piece = DirtyPiece::new();
dirty_piece.dirty_num = 1;
dirty_piece.piece_no[0] = PieceNumber(0);
dirty_piece.changed_piece[0] = ChangedBonaPiece {
old_piece: old_bp,
new_piece: new_bp,
};
let mut removed = IndexList::new();
let mut added = IndexList::new();
HalfKA_hm::append_changed_indices(
&dirty_piece,
Color::Black,
king_sq,
&mut removed,
&mut added,
);
assert_eq!(removed.len(), 1);
assert_eq!(added.len(), 1);
}
#[test]
fn test_append_changed_indices_capture() {
let sq_24 = Square::new(File::File2, Rank::Rank4);
let sq_23 = Square::new(File::File2, Rank::Rank3);
let king_sq = Square::new(File::File5, Rank::Rank9);
let mut dirty_piece = DirtyPiece::new();
dirty_piece.dirty_num = 2;
dirty_piece.piece_no[0] = PieceNumber(0);
dirty_piece.changed_piece[0] = ChangedBonaPiece {
old_piece: ExtBonaPiece::from_board(Piece::B_PAWN, sq_24),
new_piece: ExtBonaPiece::from_board(Piece::B_PAWN, sq_23),
};
dirty_piece.piece_no[1] = PieceNumber(1);
dirty_piece.changed_piece[1] = ChangedBonaPiece {
old_piece: ExtBonaPiece::from_board(Piece::W_PAWN, sq_23),
new_piece: ExtBonaPiece::from_hand(Color::Black, PieceType::Pawn, 1),
};
let mut removed = IndexList::new();
let mut added = IndexList::new();
HalfKA_hm::append_changed_indices(
&dirty_piece,
Color::Black,
king_sq,
&mut removed,
&mut added,
);
assert_eq!(removed.len(), 2);
assert_eq!(added.len(), 2);
}
#[test]
fn test_append_changed_indices_hand_change() {
let king_sq = Square::new(File::File5, Rank::Rank9);
let mut dirty_piece = DirtyPiece::new();
dirty_piece.dirty_num = 1;
dirty_piece.piece_no[0] = PieceNumber(0);
dirty_piece.changed_piece[0] = ChangedBonaPiece {
old_piece: ExtBonaPiece::ZERO, new_piece: ExtBonaPiece::from_hand(Color::Black, PieceType::Pawn, 1),
};
let mut removed = IndexList::new();
let mut added = IndexList::new();
HalfKA_hm::append_changed_indices(
&dirty_piece,
Color::Black,
king_sq,
&mut removed,
&mut added,
);
assert_eq!(removed.len(), 0);
assert_eq!(added.len(), 1);
}
#[test]
fn test_append_active_indices_with_hand_pieces() {
let mut pos = Position::new();
pos.set_sfen("1nsgkgsnl/1r5b1/pppppp3/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b 3P1L 1")
.unwrap();
let mut active = IndexList::new();
HalfKA_hm::append_active_indices(&pos, Color::Black, &mut active);
assert_eq!(active.len(), 40, "手駒の枚数分すべての特徴量が追加されるべき");
}
#[test]
fn test_append_active_indices_multiple_hand_pieces() {
let mut pos = Position::new();
pos.set_sfen("l2gkgs1l/1r5b1/pppp5/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b 5P2N1S 1")
.unwrap();
let mut active = IndexList::new();
HalfKA_hm::append_active_indices(&pos, Color::Black, &mut active);
assert_eq!(active.len(), 40, "手駒の枚数分すべての特徴量が追加されるべき");
}
#[test]
fn test_append_changed_indices_hand_increase() {
let king_sq = Square::new(File::File5, Rank::Rank9);
let mut dirty_piece = DirtyPiece::new();
dirty_piece.dirty_num = 1;
dirty_piece.piece_no[0] = PieceNumber(0);
dirty_piece.changed_piece[0] = ChangedBonaPiece {
old_piece: ExtBonaPiece::from_hand(Color::Black, PieceType::Pawn, 1),
new_piece: ExtBonaPiece::from_hand(Color::Black, PieceType::Pawn, 2),
};
let mut removed = IndexList::new();
let mut added = IndexList::new();
HalfKA_hm::append_changed_indices(
&dirty_piece,
Color::Black,
king_sq,
&mut removed,
&mut added,
);
assert_eq!(removed.len(), 1, "旧手駒BonaPieceを削除");
assert_eq!(added.len(), 1, "新手駒BonaPieceを追加");
}
#[test]
fn test_append_changed_indices_hand_decrease() {
let king_sq = Square::new(File::File5, Rank::Rank9);
let mut dirty_piece = DirtyPiece::new();
dirty_piece.dirty_num = 1;
dirty_piece.piece_no[0] = PieceNumber(0);
dirty_piece.changed_piece[0] = ChangedBonaPiece {
old_piece: ExtBonaPiece::from_hand(Color::Black, PieceType::Pawn, 2),
new_piece: ExtBonaPiece::from_hand(Color::Black, PieceType::Pawn, 1),
};
let mut removed = IndexList::new();
let mut added = IndexList::new();
HalfKA_hm::append_changed_indices(
&dirty_piece,
Color::Black,
king_sq,
&mut removed,
&mut added,
);
assert_eq!(removed.len(), 1, "旧手駒BonaPieceを削除");
assert_eq!(added.len(), 1, "新手駒BonaPieceを追加");
}
#[test]
fn test_append_changed_indices_hand_increase_multiple() {
let king_sq = Square::new(File::File5, Rank::Rank9);
let mut dirty_piece = DirtyPiece::new();
dirty_piece.dirty_num = 1;
dirty_piece.piece_no[0] = PieceNumber(0);
dirty_piece.changed_piece[0] = ChangedBonaPiece {
old_piece: ExtBonaPiece::ZERO, new_piece: ExtBonaPiece::from_hand(Color::Black, PieceType::Pawn, 3),
};
let mut removed = IndexList::new();
let mut added = IndexList::new();
HalfKA_hm::append_changed_indices(
&dirty_piece,
Color::Black,
king_sq,
&mut removed,
&mut added,
);
assert_eq!(removed.len(), 0);
assert_eq!(added.len(), 1, "3枚目のBonaPieceを追加");
}
#[test]
fn test_append_changed_indices_enemy_king_move() {
let sq_51 = Square::new(File::File5, Rank::Rank1);
let sq_41 = Square::new(File::File4, Rank::Rank1);
let king_sq = Square::new(File::File5, Rank::Rank9);
let old_bp = ExtBonaPiece::from_board(Piece::W_KING, sq_51);
let new_bp = ExtBonaPiece::from_board(Piece::W_KING, sq_41);
let mut dirty_piece = DirtyPiece::new();
dirty_piece.dirty_num = 1;
dirty_piece.piece_no[0] = PieceNumber(0);
dirty_piece.changed_piece[0] = ChangedBonaPiece {
old_piece: old_bp,
new_piece: new_bp,
};
let mut removed = IndexList::new();
let mut added = IndexList::new();
HalfKA_hm::append_changed_indices(
&dirty_piece,
Color::Black,
king_sq,
&mut removed,
&mut added,
);
assert_eq!(removed.len(), 1, "相手の王の旧位置の特徴量を削除");
assert_eq!(added.len(), 1, "相手の王の新位置の特徴量を追加");
}
}