tetris-io 0.1.1

Terminal-based Tetris game built with Ratatui and Crossterm
Documentation
use std::collections::VecDeque;

use rand::SeedableRng;
use rand::rngs::StdRng;
use rand::seq::SliceRandom;

use super::piece::PieceType;

pub trait PieceRng {
    fn next_piece(&mut self) -> PieceType;
}

#[derive(Debug)]
pub struct BagRng {
    rng: StdRng,
    bag: VecDeque<PieceType>,
}

impl BagRng {
    pub fn new(seed: u64) -> Self {
        let mut rng = StdRng::seed_from_u64(seed);
        let mut bag = VecDeque::new();
        refill_bag(&mut rng, &mut bag);
        Self { rng, bag }
    }
}

impl PieceRng for BagRng {
    fn next_piece(&mut self) -> PieceType {
        if self.bag.is_empty() {
            refill_bag(&mut self.rng, &mut self.bag);
        }
        self.bag.pop_front().unwrap_or(PieceType::I)
    }
}

fn refill_bag(rng: &mut StdRng, bag: &mut VecDeque<PieceType>) {
    let mut pieces = PieceType::ALL;
    pieces.shuffle(rng);
    for p in pieces {
        bag.push_back(p);
    }
}

pub fn random_seed() -> u64 {
    rand::random::<u64>()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn bag_contains_all_pieces() {
        let mut rng = BagRng::new(1234);
        let mut seen = std::collections::HashSet::new();
        for _ in 0..7 {
            seen.insert(rng.next_piece());
        }
        assert_eq!(seen.len(), 7);
    }
}