#比赛 #单循环 #顺序 #比赛 #生成 #模拟 #参赛者

tourney

一个用于生成和模拟各种结构比赛的的小型包

6 个版本

0.2.4 2022年10月2日
0.2.3 2022年10月2日
0.1.0 2022年10月1日

#5 in #排序

每月 25 次下载

MIT 许可证

26KB
389 行代码,不包括注释

Tourney

概述

Tourney 是一个小型 Rust 包,旨在帮助处理我多次遇到的具体、特定问题——通用编程和格式化比赛。

Tourney 无法创建任何格式的比赛——目前的限制是,比赛中每个比赛都必须有相同数量的参赛者,并且每个比赛在完成后必须提供其所有参赛者的特定顺序(尽管这不一定是按照胜者到败者的顺序)。

该包目前非常基础,但未来希望包含一些函数,用于在命令行中可视化比赛结构,以及创建一些常见比赛类型的默认版本(单/双败淘汰赛,单循环等)

基本示例

要开始使用 Tourney,您首先需要设计您的比赛结构——这里我们将设计一个简单的 8 人单败淘汰赛,只有一个认可的获胜者(没有第二名、第三名)。

//Tournaments need two generic usize parameters - first, the number of contestants
//in each match (Two, in our case), and the number of final recognised placement
//positions.
let mut tournament_template = Tourney::<2, 1>::new();

//Now we need to set up our matches - this is done by supplying a list of
//MatchOutcome enums - these can be one of three options:

//MatchOutcome::Match(m, c): Competitor proceeds on to match with ID m in
//                           position c.
//MatchOutcome::Position(p): Competitor places in final position p.
//MatchOutcome::Discard:     Competitor does not continue onwards.

//Let's start with the final match - we'll want to work backwards, as earlier
//matches will need access to the IDs of the matches they lead to.

let final_match_id = tournament_template.add_match([
    MatchOutcome::Position(0), //The competitor sorted into the first outcome by the
                                //match algorithm will place in final position 0.
    MatchOutcome::Discard      //The competitor sorted into the second outcome will not
                                //continue on, as we aren't recording second place here.
], None); //None here is an option for staging, which we'll discuss later.

//With this ID we can set up the two semi-final matches.

let semifinal_match_id_0 = tournament_template.add_match([
    MatchOutcome::Match(final_match_id, 0), //The competitor sorted into the first
                                            //outcome by the match algorithm will
                                            //go on to be competitor 0 in the final match.
    MatchOutcome::Discard
], None);

let semifinal_match_id_1 = tournament_template.add_match(
    [MatchOutcome::Match(final_match_id, 1), MatchOutcome::Discard], None
);

//The first four matches can now be set up the same way.
let match_id_0 = tournament_template.add_match(
    [MatchOutcome::Match(semifinal_match_id_0, 0), MatchOutcome::Discard], None
);
let match_id_1 = tournament_template.add_match(
    [MatchOutcome::Match(semifinal_match_id_0, 1), MatchOutcome::Discard], None
);
let match_id_2 = tournament_template.add_match(
    [MatchOutcome::Match(semifinal_match_id_1, 0), MatchOutcome::Discard], None
);
let match_id_3 = tournament_template.add_match(
    [MatchOutcome::Match(semifinal_match_id_1, 1), MatchOutcome::Discard], None
);

//And we're done!

//Now, we need an instance to run our tournament, and a register of competitors for it
//to draw on.

let mut competitors: Register<Competitor> = Register::new();
let mut instance = TourneyInstance::new(
    &tournament_template, 
    &mut competitors
);

//Now we add 8 competitors to our register and store their Ids.
let mut competitor_ids = Vec::new();
for _ in 0..8 { 
    competitor_ids.push(instance.competitors.insert(Competitor::new(vec![0.0]))); 
}
//Note - the vec parameter passed to a competitor should contain any stats (as f32) we 
//want to track. In this case, we're just going to track one statistic - win count.

//And now we initialise the instance - this checks there are an equal number of open
//tournament spots as there are competitor IDs supplied to the instance, and that the
//tournament template was formatted correctly - note that you can validate this 
//seperately on the template using tournament_template.validate_structure()

match instance.initialise(competitor_ids) {
    Err(e) => { eprintln!("{}", e); }
    Ok(()) => {},
}

//With all this done, our tournament is ready to run, but we haven't yet programmed
//any way to decide who wins a match. We'll do this by implementing the TournamentRunner
//trait onto a custom struct.

struct CoinFlipper;
impl CoinFlipper { pub fn new() -> Self { Self } }
impl TourneyRunner for CoinFlipper {
    //We just need to implement one function - this step function.

    //All this function does, essentially, is re-order a list of competitor IDs - 
    //So once it's done, the competitor at index 0 of the returned array will
    //be matched to the outcome at index 0 of the match they're in, and so on.

    //Here we'll just be dealing with two competitors per match, and we want a
    //coin flip on who wins - so we'll just swap the order of the competitors 
    //if a random float is higher than 0.5.
    fn step<const NP: usize>(
        &mut self, 
        competitor_ids: &[u32], 
        competitors: &mut Register<Competitor>
    ) -> [u32; NP] {
        //Note here we have to allow for the potential of more players (NP > 2),
        //as we can't specify the generic const for this specific implementation -
        //if you do know how to do this, please feel free to let me know
        let mut results = [0; NP];
        if rand::random::<f32>() > 0.5 {
            results[0] = competitor_ids[1];
            results[1] = competitor_ids[0];
        }
        else {
            results[0] = competitor_ids[0];
            results[1] = competitor_ids[1];
        }
        //we also need to register the 'winner' for the win count stat,
        //in this case defined as the competitor at index 0.
        competitors.get_mut(&results[0]).unwrap()._stats[0] += 1.0;

        results
    }
}

let mut runner = CoinFlipper::new();

//With all this done, we're now ready to run our tournament! The structure we've
//outlined should have 3 rounds, so let's step it forward 3 times, and then see
//who won.

for _ in 0..3 { instance.step(&mut runner); }
eprintln!("Winner was: Competitor Number {}", instance.tourney.positions[0].unwrap());

//This should come out different every time! And with that, you should have a fully
//functioning tournament - you can create new instances from the same template and
//register of competitors, and the competitor stats should be persistent, recording
//total wins over every tournament you run.

依赖项

~315KB