pub fn encode_square(rank: u8, file: u8) -> u8 {
assert!(rank < 8 && file < 8, "rank/file must be 0..7");
(rank << 3) | file
}
pub fn encode_piece(color: u8, is_pawn: bool, spawn_side_or_rank: u8, kind_or_unused: u8) -> u8 {
let bit_color = (color & 1) << 4;
let bit_pawn = (is_pawn as u8) << 3;
let lower3 = if is_pawn {
spawn_side_or_rank & 0b111
} else {
let side_bit = (spawn_side_or_rank & 1) << 2;
let kind_bits = kind_or_unused & 0b11;
side_bit | kind_bits
};
bit_color | bit_pawn | lower3
}
pub fn init_chess_positions() -> Vec<(u8, u8)> {
let mut v = Vec::with_capacity(32);
for file in 0..8 {
let pid = file as u8; let sq = encode_square(1, file);
v.push((pid, sq));
}
v.push((8, encode_square(0, 0))); v.push((9, encode_square(0, 7)));
v.push((10, encode_square(0, 1))); v.push((11, encode_square(0, 6)));
v.push((12, encode_square(0, 2))); v.push((13, encode_square(0, 5)));
v.push((14, encode_square(0, 3))); v.push((15, encode_square(0, 4)));
for file in 0..8 {
let pid = 16 + file as u8; let sq = encode_square(6, file);
v.push((pid, sq));
}
v.push((24, encode_square(7, 0))); v.push((25, encode_square(7, 7)));
v.push((26, encode_square(7, 1))); v.push((27, encode_square(7, 6)));
v.push((28, encode_square(7, 2))); v.push((29, encode_square(7, 5)));
v.push((30, encode_square(7, 3))); v.push((31, encode_square(7, 4)));
v
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_square_basics() {
assert_eq!(encode_square(0, 0), 0b000_000);
assert_eq!(encode_square(0, 7), 0b000_111);
assert_eq!(encode_square(7, 0), 0b111_000);
assert_eq!(encode_square(7, 7), 0b111_111);
assert_eq!(encode_square(3, 4), (3 << 3) | 4);
}
#[test]
fn test_encode_piece_non_pawn() {
assert_eq!(encode_piece(0, false, 0, 0), 0x00);
assert_eq!(encode_piece(1, false, 1, 0), 0x14);
assert_eq!(encode_piece(0, false, 1, 1), 0x05);
assert_eq!(encode_piece(1, false, 0, 2), 0x12);
}
#[test]
fn test_encode_piece_pawn() {
assert_eq!(encode_piece(0, true, 1, 0), 0b0_1_001); assert_eq!(encode_piece(1, true, 6, 0), 0b1_1_110); }
#[test]
fn test_init_chess_positions() {
let pos = init_chess_positions();
assert_eq!(pos.len(), 32);
let mut map: std::collections::BTreeMap<u8, Vec<u8>> =
std::collections::BTreeMap::new();
for &(pid, sq) in &pos {
map.entry(pid).or_default().push(sq);
}
let mut white_pawn_sqs: Vec<u8> = (0..8).map(|f| encode_square(1, f)).collect();
white_pawn_sqs.sort();
let mut found_wp: Vec<u8> = (0..8)
.flat_map(|pid| map.get(&pid).unwrap().clone())
.collect();
found_wp.sort();
assert_eq!(found_wp, white_pawn_sqs);
assert_eq!(map.get(&8), Some(&vec![encode_square(0, 0)]));
assert_eq!(map.get(&9), Some(&vec![encode_square(0, 7)]));
assert_eq!(map.get(&10), Some(&vec![encode_square(0, 1)]));
assert_eq!(map.get(&11), Some(&vec![encode_square(0, 6)]));
assert_eq!(map.get(&12), Some(&vec![encode_square(0, 2)]));
assert_eq!(map.get(&13), Some(&vec![encode_square(0, 5)]));
assert_eq!(map.get(&14), Some(&vec![encode_square(0, 3)]));
assert_eq!(map.get(&15), Some(&vec![encode_square(0, 4)]));
let mut black_pawn_sqs: Vec<u8> = (0..8).map(|f| encode_square(6, f)).collect();
black_pawn_sqs.sort();
let mut found_bp: Vec<u8> = (16..24)
.flat_map(|pid| map.get(&pid).unwrap().clone())
.collect();
found_bp.sort();
assert_eq!(found_bp, black_pawn_sqs);
assert_eq!(map.get(&24), Some(&vec![encode_square(7, 0)]));
assert_eq!(map.get(&25), Some(&vec![encode_square(7, 7)]));
assert_eq!(map.get(&26), Some(&vec![encode_square(7, 1)]));
assert_eq!(map.get(&27), Some(&vec![encode_square(7, 6)]));
assert_eq!(map.get(&28), Some(&vec![encode_square(7, 2)]));
assert_eq!(map.get(&29), Some(&vec![encode_square(7, 5)]));
assert_eq!(map.get(&30), Some(&vec![encode_square(7, 3)]));
assert_eq!(map.get(&31), Some(&vec![encode_square(7, 4)]));
}
}