1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#[macro_use]
extern crate prettytable;

use itertools::Itertools;
use prettytable::{format, Table};
use std::collections::HashSet;

const NUM_SQUAD: usize = 11;

pub fn calc_rating(ratings: Vec<u32>) -> u32 {
    let num = ratings.len();
    assert_eq!(num, NUM_SQUAD);

    let sum = ratings.iter().sum::<u32>();
    let avg = sum as f32 / num as f32;
    let above_sum = ratings
        .into_iter()
        .map(|x| if x as f32 > avg { x as f32 - avg } else { 0f32 })
        .sum::<f32>();
    let rating = (sum as f32 + above_sum).round() / num as f32;

    rating as u32
}

pub fn generate(
    have: &Vec<u32>,
    cand: &Vec<u32>,
    target: u32,
    records_limit: usize,
) -> Vec<Vec<u32>> {
    assert!(have.len() <= NUM_SQUAD);

    let remain = NUM_SQUAD - have.len();
    let mut visited = HashSet::new();
    let mut possibles = Vec::new();
    let mut sorted_cand = cand.clone();
    sorted_cand.sort();

    for bench in (0..remain)
        .map(|_| sorted_cand.clone())
        .multi_cartesian_product()
    {
        let mut squad = have.clone();
        squad.extend(&bench);

        let mut bench_sorted = bench.clone();
        bench_sorted.sort();

        if visited.contains(&bench_sorted) {
            continue;
        }

        visited.insert(bench_sorted.clone());

        if calc_rating(squad) == target {
            possibles.push(bench_sorted);

            if possibles.len() > records_limit {
                break;
            }
        }
    }

    possibles
}

pub fn show(cand: Vec<u32>, possibles: Vec<Vec<u32>>) {
    let mut table = Table::new();
    table.set_format(*format::consts::FORMAT_CLEAN);

    let mut cand = cand;
    cand.sort();

    const SEP: &str = "      ";
    const WIDTH: usize = 2;

    let header = cand
        .iter()
        .map(|x| format!("{:<w$}", x, w=WIDTH))
        .collect::<Vec<_>>()
        .join(SEP);

    table.add_row(row![FdBbbl->header]);

    for (pos, ratings) in possibles.iter().enumerate() {
        let row = cand
            .iter()
            .map(|x| {
                let r = ratings.iter().filter(|&y| y == x).count();
                if r == 0 {
                    "  ".to_string()
                } else {
                    format!("{:<w$}", r, w=WIDTH)
                }
            })
            .collect::<Vec<_>>()
            .join(SEP);
        if pos % 2 == 0 {
            table.add_row(row![Fcl->row]);
        } else {
            table.add_row(row![FdBcl->row]);
        }
    }

    table.printstd();
}

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

    #[test]
    pub fn test_calc_rating() {
        {
            let v = vec![81; 11];
            assert_eq!(81, calc_rating(v));
        }

        {
            let v = vec![85, 86, 86, 86, 87, 87, 87, 87, 87, 87, 88];
            assert_eq!(87, calc_rating(v));
        }
    }

    #[test]
    pub fn test_generate() {
        let have = vec![88, 87, 87, 86, 85, 85];
        let cand = vec![85, 84, 83];
        let target = 86;
        let limits = 10;

        let res = generate(&have, &cand, target, limits);

        assert_eq!(res.len(), 9);
        assert_eq!(res[0], vec![83, 83, 85, 85, 85]);
        assert_eq!(res[1], vec![83, 84, 84, 85, 85]);
        assert_eq!(res[2], vec![83, 84, 85, 85, 85]);
        assert_eq!(res[3], vec![83, 85, 85, 85, 85]);
        assert_eq!(res[4], vec![84, 84, 84, 84, 85]);
        assert_eq!(res[5], vec![84, 84, 84, 85, 85]);
        assert_eq!(res[6], vec![84, 84, 85, 85, 85]);
        assert_eq!(res[7], vec![84, 85, 85, 85, 85]);
        assert_eq!(res[8], vec![85, 85, 85, 85, 85]);
    }
}