rust-sadari-cli 0.1.1

rust-sadari-cli is sadari game (Ghost leg in other words) based on terminal. rust-sadari can take file as input or interaction from dynamic user input. Wish you good luck! Ghost leg is lottery game to find random pairs between two sets.
Documentation
use rand::Rng;
use rust_sadari_cli::helper;
use std::{collections::HashSet, iter::FromIterator};

#[test]
fn calc_name_layout_sum_is_100() {
    let iter = 12;
    let block_width_ratio = 8;
    let space_width_ratio = 2;

    for number_of_blocks in 2..(iter + 1) {
        let mut sum = 0;
        let vec = helper::calc_names_layout(number_of_blocks, block_width_ratio, space_width_ratio);

        match vec {
            Ok(v) => {
                for item in &v {
                    sum += item;
                }
                let is_positive = &v
                    .iter()
                    .enumerate()
                    .all(|(index, value)| value > &0 || index == 0 || index == v.len() - 1);
                assert_eq!(sum, 100);
                assert_eq!(*is_positive, true);
            }
            Err((unit_width, string)) => {
                println!("{}", &string);
                assert!(string.contains("unit_width"));
                assert_eq!(unit_width, 0);
            }
        }
    }
}

#[test]
fn calc_bridge_indexes_produce_rand_vec() {
    let mut rng = rand::thread_rng();
    let y_coordinate = 10;
    let number_of_max_bridge = 6;

    for _ in 0..1000 {
        let number_of_bridge = rng.gen_range(2, number_of_max_bridge);
        let vec_candidate = (0..y_coordinate).into_iter().collect();

        let vec = helper::calc_bridge_indexes(&mut rng, number_of_bridge, vec_candidate);
        assert_eq!(vec.len(), number_of_bridge as usize);
        assert!(vec.iter().all(|x| x >= &0 && x < &y_coordinate));
    }
}

#[test]
fn calc_bridge_indexes_should_not_have_duplicate() {
    let mut rng = rand::thread_rng();
    let number_of_max_bridge = 10;

    for number_of_bridge in 2..number_of_max_bridge {
        let vec_candidate = (0..10).into_iter().collect();
        let mut vec = helper::calc_bridge_indexes(&mut rng, number_of_bridge, vec_candidate);
        &mut vec.sort();

        let mut is_duplicate = false;

        for i in 0..(vec.len() - 1) {
            if vec[i] == vec[i + 1] {
                is_duplicate = true;
                break;
            }
        }

        assert_eq!(is_duplicate, false);
    }
}

#[test]
fn calc_distributed_height_should_well_distributed() {
    let height = 30;
    let number_of_bridge = 11u16;

    fn run(height: u16, number_of_bridge: u16) {
        let vec = helper::calc_distributed_height(number_of_bridge, height);
        assert_eq!(number_of_bridge as usize, vec.len());

        let possible_heights = (height / number_of_bridge, height / number_of_bridge + 1);
        let is_well_distributed = vec
            .iter()
            .all(|x| *x == possible_heights.0 || *x == possible_heights.1);
        assert!(is_well_distributed);

        let sum = vec.iter().fold(0u16, |acc, x| acc + x);
        assert_eq!(height, sum);
    }

    for h in 1..height {
        let height = h;
        run(height, number_of_bridge);
    }

    for n in 1..height + 1 {
        run(height, n);
    }
}

#[test]
fn calc_bridge_hashmap_should_distinct_indexes_vec_compared_to_adjacent_vec() {
    let mut rng = rand::thread_rng();
    let number_of_block = 10;
    let nubmer_of_max_bridges = 6;
    let y_coordinate = 10;

    let bridge_hashmap = helper::calc_bridge_hashmap(
        number_of_block,
        nubmer_of_max_bridges,
        y_coordinate,
        &mut rng,
    );

    for (key, value) in &bridge_hashmap {
        println!("{}: {:?}", key, value);

        if *key == 0 {
            continue;
        }

        let prev_key = key - 1u16;
        match bridge_hashmap.get(&prev_key) {
            Some(vec) => {
                let set: HashSet<&u16> = HashSet::from_iter(vec.iter());

                let is_duplicate = value.iter().all(|x| !set.contains(x));

                assert!(is_duplicate, "There is duplicate!");
            }
            None => {
                assert!(false, "There should be no None case!");
            }
        }
    }
}

#[test]
fn calc_bridge_hashmap_is_sorted() {
    let mut rng = rand::thread_rng();
    let number_of_block = 10;
    let nubmer_of_max_bridges = 6;
    let y_coordinate = 10;

    let bridge_hashmap = helper::calc_bridge_hashmap(
        number_of_block,
        nubmer_of_max_bridges,
        y_coordinate,
        &mut rng,
    );

    for (key, value) in &bridge_hashmap {
        println!("{}: {:?}", key, value);

        let (_, is_sorted) = value.iter().fold((-1, true), |acc, x| {
            let (prev_num, boolean) = acc;

            if prev_num < (*x as i32) && boolean {
                (*x as i32, true)
            } else {
                (*x as i32, false)
            }
        });

        assert!(is_sorted);
    }
}

#[test]
fn calc_index_should_in_limit() {
    let mut index = 0;
    let limit = 10;

    for _ in 0..20 {
        index = helper::calc_next_index(index, limit);
        assert!(index < limit);
    }

    index = 0;
    for _ in 0..20 {
        index = helper::calc_prev_index(index, limit);
        assert!(index < limit);
    }
}

#[test]
fn calc_bridge_points_should_not_overlap() {
    let mut rng = rand::thread_rng();
    let number_of_block = 10;
    let nubmer_of_max_bridges = 6;
    let y_coordinate = 10;

    let bridge_hashmap = helper::calc_bridge_hashmap(
        number_of_block,
        nubmer_of_max_bridges,
        y_coordinate,
        &mut rng,
    );

    for index in 0..number_of_block as i32 {
        let vec = helper::calc_bridge_points(index, &bridge_hashmap);

        if index == 0 {
            let count_1 = bridge_hashmap.get(&0).unwrap().len();
            let count_2 = vec.iter().filter(|p| p.y == index + 1).count();

            assert_eq!(count_1, count_2);
            assert_eq!(vec.len(), count_1);
        } else if index == (number_of_block - 1) as i32 {
            let count_1 = bridge_hashmap.get(&(index as u16 - 1)).unwrap().len();
            let count_2 = vec.iter().filter(|p| p.y == index - 1).count();

            assert_eq!(count_1, count_2);
            assert_eq!(vec.len(), count_1);
        } else {
            let count_1 = bridge_hashmap.get(&(index as u16 - 1)).unwrap().len();
            let count_2 = bridge_hashmap.get(&(index as u16)).unwrap().len();

            let count_3 = vec.iter().filter(|p| p.y == index - 1).count();
            let count_4 = vec.iter().filter(|p| p.y == index + 1).count();

            assert_eq!(count_1, count_3);
            assert_eq!(count_2, count_4);
            assert_eq!(vec.len(), count_1 + count_2);
        }
    }
}

#[test]
pub fn calc_path_result_should_not_overlap() {
    let mut rng = rand::thread_rng();
    let number_of_block = 10;
    let nubmer_of_max_bridges = 6;
    let y_coordinate = 10;

    let bridge_hashmap = helper::calc_bridge_hashmap(
        number_of_block,
        nubmer_of_max_bridges,
        y_coordinate,
        &mut rng,
    );

    let mut result = HashSet::new();

    for index in 0..number_of_block {
        let path = helper::calc_path(index, &bridge_hashmap, y_coordinate as u8);

        let helper::Point { x: last_x, y: _ } = path.get(path.len() - 1).unwrap();

        let is_exist = result.contains(last_x);
        assert_eq!(is_exist, false);
        result.insert(*last_x);
    }

    println!("Test cal_path, result is {:?}", result);
}