Search code examples
javascriptchessround-robintournament

How to create this round robin chess tournament generator


I have list of players and want to create a round robin generator for chess tournaments with JavaScript. The round robin should be like in this image, shown for 10 teams and 6 teams:

enter image description here

I tried to create separate logic for even and odd round numbers, but I failed to find the best algorithm for that:

var list = [1,2,3,4,5,6,7,8,9,10];
var pairing = []
var size = list.length;
var halfSize = size/2;
var evenOrOdd
if (halfSize % 2 != 1) {
    list.push("BYE")
}
for (var i=0 ; i <= size-1 ; i++) {
    var lastOne = list[size]
    if (isEven(i)) {
        var games = []
        for (var j = 0; j <= halfSize-1; j++) {
            if (j == 0) {
                games.push({"home": list[size-1], "away": list[(i+2)/2-1+j]})
          } else {
                if(list[size-j-i/2]==list[size-2] && i>0){
                    games.push({"home": list[j+i/2], "away": list[0]})
                } else {
                    games.push({"home": list[j+i/2], "away": list[(i+2)/2]})
            }
            }
        }
        pairing.push({"round":i+1,"games":games})
    } else {
    }
}
console.log(pairing)

function isEven(num) {
    if (num % 2 == 0)
        return true;
    return false;
}

The result I get is not like the pairing in the image above, it only produces the first round correctly.

There are some other similar questions that have answers, like Scheduling algorithm for a round-robin tournament?, but they are for football tournaments and I want it for chess tournaments: it has some differences compared with football.

How can I correct my chess tournament generator so it works for any number of players?


Solution

  • You could use the remainder operator (%) to use modular logic, as clearly one number increases, while the other decreases. The first game is an exception, where the last player is always involved, and the increasing counter is left the same as in the previously generated pair. It also has a swap of sides depending on the parity of the round.

    Here is a way to do it using some map and Array.from calls to create the arrays on the fly. The incrementing and decrementing variables are updated while they are used as index:

    function generatePairings(players) {
        let n = players.length;
        let mod = n - 1;
        let decr = 0;
        let incr = -1;
        let arr = {length: n / 2 - 1}; // n is assumed to be even
        let props = ["away", "home"];
        let template = { home: players[mod], away: players[mod] };
        return players.slice(1).map((_, i) => ({
            round: i + 1,
            games: [
                { ...template, ...{ [props[i % 2]]: players[incr = (incr + 1) % mod] }}, 
                ...Array.from(arr, () => ({
                    home: players[incr = (incr + 1) % mod], 
                    away: players[decr = (decr + mod - 1) % mod] 
                }))
            ]
        }));
    }
    
    console.log(generatePairings([1,2,3,4,5,6]));

    Or with for loops:

    function generatePairings(players) {
        let n = players.length;
        let mod = n - 1;
        let decr = 0;
        let incr = -1;
        let gameCount = n / 2; // n is assumed to be even
        let props = ["away", "home"];
        let template = { home: players[mod], away: players[mod] };
        let pairings = [];
        for (let round = 1; round < n; round++) {
            let games = [{ ...template, 
                ...{ [props[round % 2]]: players[incr = (incr + 1) % mod] }
            }];
            for (let k = 1; k < gameCount; k++) {
                games.push({
                    home: players[incr = (incr + 1) % mod], 
                    away: players[decr = (decr + mod - 1) % mod] 
                });
            }
            pairings.push({ round, games });
        }
        return pairings;
    }
    
    console.log(generatePairings([1,2,3,4,5,6]));