use rand::{RngExt, seq::SliceRandom};
use crate::{combination_check_result::CombinationCheckResult, messages::Messages};
pub struct Game {
prevent_duplicates: bool,
current_try: usize,
max_tries: usize,
total_slots: usize,
total_colors: usize,
answer_combination: Vec<usize>,
}
impl Game {
pub fn new_parameterized(
prevent_duplicates: bool,
total_slots: usize,
total_colors: usize,
max_tries: usize,
colors_combination: Vec<usize>,
) -> Self {
let game = Self {
prevent_duplicates: prevent_duplicates,
current_try: 0,
max_tries: max_tries,
total_slots: total_slots,
total_colors: total_colors,
answer_combination: colors_combination,
};
game
}
pub fn generate_random_colors_combination(
duplicates_prevented: bool,
total_slots: usize,
total_colors: usize,
) -> Vec<usize> {
let mut rng = rand::rng();
if duplicates_prevented {
assert!(
total_slots <= total_colors,
"{}",
Messages::slots_exceed_colors_without_duplicates()
);
let mut colors: Vec<usize> = (0..total_colors).collect();
colors.shuffle(&mut rng);
colors.truncate(total_slots);
colors
} else {
(0..total_slots)
.map(|_| rng.random_range(0..total_colors))
.collect()
}
}
pub fn prevent_duplicates(&self) -> bool {
self.prevent_duplicates
}
pub fn current_try(&self) -> usize {
self.current_try
}
pub fn max_tries(&self) -> usize {
self.max_tries
}
pub fn total_slots(&self) -> usize {
self.total_slots
}
pub fn total_colors(&self) -> usize {
self.total_colors
}
pub fn colors_combination(&self) -> Vec<usize> {
self.answer_combination.clone()
}
pub fn inc_current_try(&mut self) {
self.current_try += 1;
}
pub fn check_combination(&self, combination: &Vec<usize>) -> CombinationCheckResult {
let mut well_placed: usize = 0;
let mut misplaced_slots: usize = 0;
let mut exact_matches = vec![false; self.answer_combination.len()];
let mut misplaced_matches = vec![false; combination.len()];
if combination.len() > 0 {
for i in 0..combination.len() {
if combination[i] == self.answer_combination[i] {
well_placed += 1;
exact_matches[i] = true;
misplaced_matches[i] = true;
}
}
for i in 0..combination.len() {
if misplaced_matches[i] {
continue;
}
for j in 0..self.answer_combination.len() {
if !exact_matches[j] && combination[i] == self.answer_combination[j] {
misplaced_slots += 1;
exact_matches[j] = true;
misplaced_matches[i] = true;
break;
}
}
}
}
let is_victory = well_placed == self.total_slots;
let result: CombinationCheckResult = CombinationCheckResult::new_parameterized(
is_victory,
!is_victory && self.current_try == self.max_tries,
well_placed,
misplaced_slots,
);
result
}
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
#[rstest]
#[case([1,2,3,4], vec![1,0,0,0], 1)]
#[case([1,2,3,4], vec![0,2,0,0], 1)]
#[case([1,2,3,4], vec![0,0,3,0], 1)]
#[case([1,2,3,4], vec![0,0,0,4], 1)]
#[case([1,2,3,4], vec![1,2,0,0], 2)]
#[case([1,2,3,4], vec![0,2,3,0], 2)]
#[case([1,2,3,4], vec![0,0,3,4], 2)]
#[case([1,2,3,4], vec![1,0,0,4], 2)]
#[case([1,2,3,4], vec![1,2,3,0], 3)]
#[case([1,2,3,4], vec![0,2,3,4], 3)]
#[case([1,2,3,4], vec![1,0,3,4], 3)]
#[case([1,2,3,4], vec![1,2,3,4], 4)]
fn test_check_combination_handles_well_placed(
#[case] combination: [usize; 4],
#[case] combination_try: Vec<usize>,
#[case] expected: usize,
) {
let game = Game::new_parameterized(false, 4, 4, 1, combination.to_vec());
let result = game.check_combination(&combination_try);
assert_eq!(result.well_placed(), expected);
}
#[rstest]
#[case([1,1,2,2], vec![1,0,0,0], 1)]
#[case([1,1,2,2], vec![0,1,0,0], 1)]
#[case([1,1,2,2], vec![1,1,0,0], 2)]
#[case([1,1,2,2], vec![0,0,2,0], 1)]
#[case([1,1,2,2], vec![0,0,0,2], 1)]
#[case([1,1,2,2], vec![0,0,2,2], 2)]
#[case([1,1,1,2], vec![1,0,0,0], 1)]
#[case([1,1,1,2], vec![0,1,0,0], 1)]
#[case([1,1,1,2], vec![0,0,1,0], 1)]
#[case([1,1,1,2], vec![1,1,1,0], 3)]
#[case([1,2,2,2], vec![0,0,0,2], 1)]
#[case([1,2,2,2], vec![0,0,2,2], 2)]
#[case([1,2,2,2], vec![0,2,2,2], 3)]
#[case([1,1,1,1], vec![1,1,1,1], 4)]
fn test_check_combination_handles_well_placed_duplicates(
#[case] combination: [usize; 4],
#[case] combination_try: Vec<usize>,
#[case] expected: usize,
) {
let game = Game::new_parameterized(false, 4, 4, 1, combination.to_vec());
let result = game.check_combination(&combination_try);
assert_eq!(result.well_placed(), expected);
}
#[rstest]
#[case([1,2,3,4], vec![4,0,0,0], 1)]
#[case([1,2,3,4], vec![0,1,0,0], 1)]
#[case([1,2,3,4], vec![0,0,2,0], 1)]
#[case([1,2,3,4], vec![0,0,0,3], 1)]
#[case([1,2,3,4], vec![4,1,0,0], 2)]
#[case([1,2,3,4], vec![0,1,2,0], 2)]
#[case([1,2,3,4], vec![0,0,2,3], 2)]
#[case([1,2,3,4], vec![4,0,0,3], 2)]
#[case([1,2,3,4], vec![4,1,2,0], 3)]
#[case([1,2,3,4], vec![0,1,2,3], 3)]
#[case([1,2,3,4], vec![4,0,2,3], 3)]
#[case([1,2,3,4], vec![1,2,3,4], 0)]
fn test_check_combination_handles_misplaced(
#[case] combination: [usize; 4],
#[case] combination_try: Vec<usize>,
#[case] expected: usize,
) {
let game = Game::new_parameterized(false, 4, 4, 1, combination.to_vec());
let result = game.check_combination(&combination_try);
assert_eq!(result.misplaced(), expected);
}
#[rstest]
#[case([1,1,2,2], vec![2,0,0,0], 1)]
#[case([1,1,2,2], vec![0,2,0,0], 1)]
#[case([1,1,2,2], vec![2,2,0,0], 2)]
#[case([1,1,2,2], vec![0,0,1,0], 1)]
#[case([1,1,2,2], vec![0,0,0,1], 1)]
#[case([1,1,2,2], vec![0,0,1,1], 2)]
fn test_check_combination_handles_misplaced_duplicates(
#[case] combination: [usize; 4],
#[case] combination_try: Vec<usize>,
#[case] expected: usize,
) {
let game = Game::new_parameterized(false, 4, 4, 1, combination.to_vec());
let result = game.check_combination(&combination_try);
assert_eq!(result.misplaced(), expected);
}
#[rstest]
#[case([1,2,3,4], vec![4,4,0,0], 1)]
#[case([1,2,3,4], vec![0,1,1,0], 1)]
#[case([1,2,3,4], vec![0,0,2,2], 1)]
#[case([1,2,3,4], vec![3,0,0,3], 1)]
#[case([1,2,3,4], vec![4,1,4,1], 2)]
#[case([1,2,3,4], vec![2,1,2,1], 2)]
#[case([1,2,3,4], vec![2,3,2,3], 2)]
#[case([1,2,3,4], vec![4,3,4,3], 2)]
#[case([1,2,3,4], vec![4,1,2,1], 3)]
#[case([1,2,3,4], vec![3,1,2,3], 3)]
#[case([1,2,3,4], vec![4,3,2,3], 3)]
fn test_check_combination_handles_misplaced_proposed_duplicates(
#[case] combination: [usize; 4],
#[case] combination_try: Vec<usize>,
#[case] expected: usize,
) {
let game = Game::new_parameterized(false, 4, 4, 1, combination.to_vec());
let result = game.check_combination(&combination_try);
assert_eq!(result.misplaced(), expected);
}
}