use rand::RngExt;
pub trait PieceSelector: Send + Sync {
fn select(&self, our_bitfield: &[bool], availability: &[usize]) -> Option<u32>;
}
pub struct RarestFirst;
impl PieceSelector for RarestFirst {
fn select(&self, our_bitfield: &[bool], availability: &[usize]) -> Option<u32> {
let len = our_bitfield.len().min(availability.len());
let mut best_idx: Option<u32> = None;
let mut best_count = usize::MAX;
for i in 0..len {
if our_bitfield[i] {
continue; }
let count = availability[i];
if count == 0 {
continue; }
if count < best_count {
best_count = count;
best_idx = Some(i as u32);
}
}
best_idx
}
}
pub struct RandomFirst;
impl PieceSelector for RandomFirst {
fn select(&self, our_bitfield: &[bool], availability: &[usize]) -> Option<u32> {
let len = our_bitfield.len().min(availability.len());
let candidates: Vec<u32> = (0..len)
.filter(|&i| !our_bitfield[i] && availability[i] > 0)
.map(|i| i as u32)
.collect();
if candidates.is_empty() {
return None;
}
let idx = rand::rng().random_range(0..candidates.len());
Some(candidates[idx])
}
}
pub struct Sequential;
impl PieceSelector for Sequential {
fn select(&self, our_bitfield: &[bool], availability: &[usize]) -> Option<u32> {
let len = our_bitfield.len().min(availability.len());
for i in 0..len {
if !our_bitfield[i] && availability[i] > 0 {
return Some(i as u32);
}
}
None
}
}
pub struct EndGame;
impl PieceSelector for EndGame {
fn select(&self, our_bitfield: &[bool], availability: &[usize]) -> Option<u32> {
Sequential.select(our_bitfield, availability)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sequential_basic() {
let our_bf = vec![false, false, false, false];
let avail = vec![3, 0, 2, 1];
assert_eq!(Sequential.select(&our_bf, &avail), Some(0));
}
#[test]
fn sequential_skip_zero_availability() {
let our_bf = vec![false, false, false];
let avail = vec![0, 0, 2];
assert_eq!(Sequential.select(&our_bf, &avail), Some(2));
}
#[test]
fn sequential_all_downloaded() {
let our_bf = vec![true, true];
let avail = vec![3, 5];
assert_eq!(Sequential.select(&our_bf, &avail), None);
}
#[test]
fn sequential_none_available() {
let our_bf = vec![false, false];
let avail = vec![0, 0]; assert_eq!(Sequential.select(&our_bf, &avail), None);
}
#[test]
fn random_first_basic() {
let our_bf = vec![false, false, false];
let avail = vec![2, 2, 2];
let result = RandomFirst.select(&our_bf, &avail);
assert!(result.is_some());
let idx = result.unwrap() as usize;
assert!(idx < 3);
}
#[test]
fn random_first_empty() {
let our_bf = vec![false, false];
let avail = vec![0, 0];
assert_eq!(RandomFirst.select(&our_bf, &avail), None);
}
#[test]
fn rarest_first_picks_rarest() {
let our_bf = vec![false, false, false];
let avail = vec![3, 1, 5];
assert_eq!(RarestFirst.select(&our_bf, &avail), Some(1));
}
#[test]
fn rarest_first_skips_owned() {
let our_bf = vec![true, false, false];
let avail = vec![0, 1, 5];
assert_eq!(RarestFirst.select(&our_bf, &avail), Some(1));
}
#[test]
fn rarest_first_skips_zero_availability() {
let our_bf = vec![false, false];
let avail = vec![0, 3];
assert_eq!(RarestFirst.select(&our_bf, &avail), Some(1));
}
#[test]
fn rarest_first_empty_when_none_available() {
let our_bf = vec![false, false];
let avail = vec![0, 0];
assert_eq!(RarestFirst.select(&our_bf, &avail), None);
}
#[test]
fn endgame_select_any() {
let our_bf = vec![false, true]; let avail = vec![4, 0];
assert_eq!(EndGame.select(&our_bf, &avail), Some(0));
}
#[test]
fn endgame_none_available() {
let our_bf = vec![false, false];
let avail = vec![0, 0];
assert_eq!(EndGame.select(&our_bf, &avail), None);
}
#[test]
fn availability_shorter_than_bitfield() {
let our_bf = vec![false, false, false];
let avail = vec![1]; assert_eq!(Sequential.select(&our_bf, &avail), Some(0));
}
}