use rand::RngExt;
use crate::Error;
use crate::rules::Player;
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Dice {
values: (u8, u8),
available: Vec<u8>,
}
impl Dice {
pub fn new() -> Self {
Self::default()
}
pub fn consume(&mut self, dice: u8) -> Result<(), Error> {
if self.available.is_empty() {
return Err(Error::DiceConsumed);
}
if let Some(pos) = self.available.iter().position(|&x| x == dice) {
let _ = self.available.remove(pos);
Ok(())
} else {
Err(Error::DiceInvalid)
}
}
}
pub trait Roll {
fn roll(&mut self, player: Player) -> Result<(), Error>;
fn get_dice(&self) -> (u8, u8);
fn set_dice(&mut self, player: Player, v: (u8, u8)) -> Result<(), Error>;
fn dice_available(&self) -> &Vec<u8>;
fn dice_consumed(&self) -> bool;
}
impl Roll for Dice {
fn roll(&mut self, player: Player) -> Result<(), Error> {
let mut rng = rand::rng();
let v = (rng.random_range(1..=6), rng.random_range(1..=6));
self.set_dice(player, v)
}
fn get_dice(&self) -> (u8, u8) {
self.values
}
fn set_dice(&mut self, _player: Player, v: (u8, u8)) -> Result<(), Error> {
if v.0 > 6 || v.1 > 6 || v.0 < 1 || v.1 < 1 {
return Err(Error::DiceInvalid);
};
if v.0 == v.1 {
self.values = (v.0, v.0);
self.available = vec![v.0, v.0, v.0, v.0];
} else {
self.values = (v.0, v.1);
self.available = vec![v.0, v.1];
}
Ok(())
}
fn dice_available(&self) -> &Vec<u8> {
&self.available
}
fn dice_consumed(&self) -> bool {
self.available.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dice_default() {
let dice = Dice::default();
assert_eq!(dice.values, (0, 0));
assert_eq!(dice.available, Vec::<u8>::new());
}
#[test]
fn test_set_valid_different_values() {
let mut dice = Dice::default();
let mut dice_a: u8 = 1;
let mut dice_b: u8 = 1;
while dice_a < 7 {
while dice_b < 7 {
if dice_a.eq(&dice_b) {
break;
}
let result = dice.set_dice(Player::Nobody, (dice_a, dice_b));
assert!(result.is_ok());
assert_eq!(dice.values, (dice_a, dice_b));
assert_eq!(dice.available, vec![dice_a, dice_b]);
dice_b += 1;
}
dice_a += 1;
}
}
#[test]
fn test_set_valid_equal_values() {
let mut dice = Dice::default();
let mut dice_a: u8 = 1;
while dice_a < 7 {
let result = dice.set_dice(Player::Nobody, (dice_a, dice_a));
assert!(result.is_ok());
assert_eq!(dice.values, (dice_a, dice_a));
assert_eq!(dice.available, vec![dice_a, dice_a, dice_a, dice_a]);
dice_a += 1;
}
}
#[test]
fn test_set_invalid_value_too_high() {
let mut dice = Dice::default();
let result = dice.set_dice(Player::Nobody, (7, 3));
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::DiceInvalid);
let result = dice.set_dice(Player::Nobody, (3, 7));
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::DiceInvalid);
}
#[test]
fn test_set_invalid_both_values_too_high() {
let mut dice = Dice::default();
let result = dice.set_dice(Player::Nobody, (7, 8));
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::DiceInvalid);
}
#[test]
fn test_set_invalid_value_zero() {
let mut dice = Dice::default();
let result = dice.set_dice(Player::Nobody, (0, 3));
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::DiceInvalid);
let result = dice.set_dice(Player::Nobody, (3, 0));
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::DiceInvalid);
}
#[test]
fn test_set_invalid_both_values_zero() {
let mut dice = Dice::default();
let result = dice.set_dice(Player::Nobody, (0, 0));
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::DiceInvalid);
}
#[test]
fn test_consume_valid_dice() -> Result<(), Error> {
let mut dice = Dice::default();
dice.set_dice(Player::Nobody, (3, 5))?;
let result = dice.consume(3);
assert!(result.is_ok());
assert_eq!(dice.available, vec![5]);
assert!(!dice.dice_consumed());
dice.set_dice(Player::Nobody, (3, 5))?;
let result = dice.consume(5);
assert!(result.is_ok());
assert_eq!(dice.available, vec![3]);
assert!(!dice.dice_consumed());
Ok(())
}
#[test]
fn test_consume_both_dice() -> Result<(), Error> {
let mut dice = Dice::default();
dice.set_dice(Player::Nobody, (3, 5))?;
assert!(dice.consume(3).is_ok());
assert_eq!(dice.available, vec![5]);
assert!(dice.consume(5).is_ok());
assert_eq!(dice.available, Vec::<u8>::new());
assert_eq!(dice.dice_available(), &vec![]);
assert!(dice.dice_consumed());
Ok(())
}
#[test]
fn test_consume_doubles() -> Result<(), Error> {
let mut dice = Dice::default();
dice.set_dice(Player::Nobody, (4, 4))?;
assert_eq!(dice.available, vec![4, 4, 4, 4]);
assert_eq!(dice.dice_available(), &vec![4, 4, 4, 4]);
assert!(!dice.dice_consumed());
assert!(dice.consume(4).is_ok());
assert_eq!(dice.available, vec![4, 4, 4]);
assert!(!dice.dice_consumed());
assert!(dice.consume(4).is_ok());
assert_eq!(dice.dice_available(), &vec![4, 4]);
assert!(!dice.dice_consumed());
assert!(dice.consume(4).is_ok());
assert_eq!(dice.dice_available(), &vec![4]);
assert!(!dice.dice_consumed());
assert!(dice.consume(4).is_ok());
assert_eq!(dice.available, Vec::<u8>::new());
assert!(dice.dice_consumed());
Ok(())
}
#[test]
fn test_consume_invalid_dice_value() -> Result<(), Error> {
let mut dice = Dice::default();
dice.set_dice(Player::Nobody, (3, 5))?;
let result = dice.consume(2);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::DiceInvalid);
assert_eq!(dice.available, vec![3, 5]); assert!(!dice.dice_consumed());
Ok(())
}
#[test]
fn test_consume_already_consumed() -> Result<(), Error> {
let mut dice = Dice::default();
dice.set_dice(Player::Nobody, (3, 5))?;
assert!(dice.consume(3).is_ok());
assert!(dice.consume(5).is_ok());
assert!(dice.dice_consumed());
let result = dice.consume(3);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::DiceConsumed);
Ok(())
}
#[test]
fn test_consume_wrong_value_after_partial_consumption() -> Result<(), Error> {
let mut dice = Dice::default();
dice.set_dice(Player::Nobody, (3, 5))?;
assert!(dice.consume(3).is_ok());
let result = dice.consume(3);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::DiceInvalid);
assert!(!dice.dice_consumed());
Ok(())
}
#[test]
fn test_roll_generates_valid_values() {
for _ in 0..100 {
let mut dice = Dice::default();
let result = dice.roll(Player::Nobody);
assert!(result.is_ok());
assert!(dice.values.0 >= 1 && dice.values.0 <= 6);
assert!(dice.values.1 >= 1 && dice.values.1 <= 6);
if dice.values.0 == dice.values.1 {
assert_eq!(dice.available.len(), 4);
} else {
assert_eq!(dice.available.len(), 2);
}
}
}
#[test]
fn test_dice_clone() -> Result<(), Error> {
let mut dice = Dice::default();
dice.set_dice(Player::Nobody, (3, 5))?;
let cloned = dice.clone();
assert_eq!(dice, cloned);
assert_eq!(cloned.values, (3, 5));
assert_eq!(cloned.available, vec![3, 5]);
Ok(())
}
#[test]
fn test_dice_partial_eq() -> Result<(), Error> {
let mut die1 = Dice::default();
die1.set_dice(Player::Nobody, (3, 5))?;
let mut die2 = Dice::default();
die2.set_dice(Player::Nobody, (3, 5))?;
let mut die3 = Dice::default();
die3.set_dice(Player::Nobody, (4, 6))?;
assert_eq!(die1, die2);
assert_ne!(die1, die3);
Ok(())
}
#[test]
fn test_dice_debug() -> Result<(), Error> {
let mut dice = Dice::default();
dice.set_dice(Player::Nobody, (3, 5))?;
let debug_str = format!("{:?}", dice);
assert!(debug_str.contains("Dice"));
assert!(debug_str.contains("values"));
assert!(debug_str.contains("available"));
Ok(())
}
#[test]
fn test_values_returns_dice_values() -> Result<(), Error> {
let mut dice = Dice::default();
dice.set_dice(Player::Nobody, (3, 5))?;
assert_eq!(dice.get_dice(), (3, 5));
Ok(())
}
#[test]
fn test_values_returns_doubles() -> Result<(), Error> {
let mut dice = Dice::default();
dice.set_dice(Player::Nobody, (4, 4))?;
assert_eq!(dice.get_dice(), (4, 4));
Ok(())
}
#[test]
fn test_dice_consumed_default() {
let dice = Dice::default();
assert!(dice.dice_consumed());
}
#[test]
fn test_dice_consumed_after_set() -> Result<(), Error> {
let mut dice = Dice::default();
dice.set_dice(Player::Nobody, (3, 5))?;
assert!(!dice.dice_consumed());
Ok(())
}
#[test]
fn test_dice_new() {
let dice = Dice::new();
assert_eq!(dice.values, (0, 0));
assert_eq!(dice.available, Vec::<u8>::new());
assert!(dice.dice_consumed());
}
#[test]
fn set_dice_with_invalid_values() {
let mut dice = Dice::default();
let result = dice.set_dice(Player::Nobody, (0, 5));
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::DiceInvalid);
}
}