Reputation: 35
I'm trying to build a Javascript function that takes as an input:
["player1","player2","player3","player4"]
(probably only equal number of players)and dynamically creates an array for a tournament design based on the following rules:
the outputs would be an array containing arrays of matches with four entries each e.g. [player1, player2, player3, player4] stands for player1 and player2 vs. player3 and player4.
[["player1","player2","player3","player4"], ["player1","player3","player2","player4"], ...]
Currently I use something like the below example to do this hard-coded but unfortunately only for a pre-defined number of players.
const m = [];
const A = players[0];
const B = players[1];
const C = players[2];
const D = players[3];
const E = players[4];
const F = players[5];
const G = players[6];
const H = players[7];
const I = players[8];
const J = players[9];
const K = players[10];
const L = players[11];
const M = players[12];
const N = players[13];
const O = players[14];
const P = players[15];
m.push(A, B, C, P);
m.push(A, C, E, O);
m.push(B, C, D, A);
m.push(B, D, F, P);
m.push(C, D, E, B);
m.push(C, E, G, A);
m.push(D, E, F, C);
m.push(D, F, H, B);
m.push(E, F, G, D);
m.push(E, G, I, C);
m.push(F, G, H, E);
m.push(F, H, J, D);
m.push(G, H, I, F);
m.push(G, I, K, E);
m.push(H, I, J, G);
m.push(H, J, L, F);
m.push(I, J, K, H);
m.push(I, K, M, G);
m.push(J, K, L, I);
m.push(J, L, N, H);
m.push(K, L, M, J);
m.push(K, M, O, I);
m.push(L, M, N, K);
m.push(L, N, P, J);
m.push(M, N, O, L);
m.push(M, O, A, K);
m.push(N, O, P, M);
m.push(N, P, B, L);
m.push(O, P, A, N);
m.push(O, A, C, M);
m.push(P, A, B, O);
m.push(P, B, D, N);
return m;
Would be thankful for every hint!
Cheers
Upvotes: 1
Views: 5353
Reputation: 350365
You could use the round-robin tournament mechanism to pair players. At each iteration all players, except one, take the place of the next player. If the number of players is odd, there will be one player excluded from matching, but it will be a different one in each iteration. As a game needs 2 pairs, there might be a pair that is not participating either. Again, this will be a different pair in each iteration.
This method will make each player play as many games as the other players, except for when the number of players is 2 modulo 4 (i.e. 6, 10, 14, ...). In that case all players except one will play the same number of games. The exceptional player will play 2 more games.
The number of games found for n players, and the number of games per player, will follow this formula:
#players(n) modulo 4 | #games | #games per player
----------------------+----------------------+--------------------
0 | n(n-1)/4 | n-1
1 | n(n-1)/4 | n-1
2 | (n-1)(n-2)/4 | n-3 (one: n-1)
3 | floor((n-1)(n-2)/4) | n-3
Example: given 16 players, the algorithm will find 60 games, where each player gets to play in 15 games.
Here is an implementation:
function assignToGames(players) {
// Round the number of players up to nearest multiple of 2.
// The potential extra player is a dummy, and the games they play
// will not be included.
const numPlayers = players.length + players.length % 2, // potential dummy added
pairsPerRound = numPlayers / 2,
rotatingPlayers = numPlayers - 1,
firstRound = players.length % 2, // will make the dummy game being ignored
games = [];
for (let round = 0; round < rotatingPlayers; round++) {
for (let i = firstRound; i < pairsPerRound-1; i+=2) {
// The following formulas reflect a roundrobin scheme, where
// the last player (possibly a dummy) does not move.
games.push([
players[i ? (i+round-1) % rotatingPlayers : numPlayers - 1],
players[(numPlayers-i-2+round) % rotatingPlayers],
players[(i+round) % rotatingPlayers],
players[(numPlayers-i-3+round) % rotatingPlayers],
]);
}
}
return games;
}
// Optional function to test the correctness of the result,
// and count the number of games per player:
function getStatistics(players, games) {
const usedPairs = new Set(),
stats = Object.assign(...players.map( player => ({ [player]: 0 }) ));
for (let game of games) {
// verify uniqueness of pairs
for (let pairIndex = 0; pairIndex < 4; pairIndex += 2) {
let pair = JSON.stringify(game.slice(pairIndex,pairIndex+2).sort());
if (usedPairs.has(pair)) throw "Duplicate pair " + pair;
usedPairs.add(pair);
}
}
// Count the number of games each player plays:
for (let i = 0; i < games.length; i++) {
for (let j = 0; j < 4; j++) {
stats[games[i][j]]++;
}
}
return stats;
}
// Demo
// Create 16 players. Their values are the letters of the alphabet up to "p".
const players = Array.from("abcdefghijklmnop");
const games = assignToGames(players);
// Display results
console.log(JSON.stringify(games));
console.log("--- statistics ---");
console.log('#games: ', games.length);
const stats = getStatistics(players, games);
console.log(stats);
Upvotes: 1
Reputation: 952
Hi you can try with following code.
function getUniquePairArray(_arr) {
var _tArr = [];
for(var i = 0; i < _arr.length; i++) {
for(var j = i+1; j < _arr.length; j++) {
_tArr.push([_arr[i],_arr[j]]);
}
}
return _tArr;
}
above code will create unique pairs from array. e.g. input
[1,2,3,4]
output
[[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]
var findOne = function (haystack, arr) {
return arr.some(function (v) {
return haystack.indexOf(v) >= 0;
});
};
above function checks each element present in target array from given array.
function finalResult(_arr) {
var _tArr = [];
for(var i = 0; i < _arr.length; i++) {
for(var j = i+1; j < _arr.length; j++) {
if(!findOne(_arr[i],_arr[j]))
{
_tArr.push([_arr[i],_arr[j]]);
break;
}
}
}
return _tArr;
}
this function will give you your desired output.
INPUT == finalFun(getUniquePairArray(["p1","p2","p3","p4","p5"]))
OUTPUT = "[p1,p2,p3,p4][p1,p3,p2,p4][p1,p4,p2,p3][p1,p5,p2,p3][p2,p3,p4,p5][p2,p4,p3,p5][p2,p5,p3,p4]"
Upvotes: 0
Reputation: 526
premetting that I understood the problem correctly, this has no solution in case you will have an odd number of players.
n-1 + n-2 + ... + 2 + 1
couples that is n(n-1) / 2
n(n-1) / 4
here's the code,
const players = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L'];
let couples = [];
for (let i = 0; i < players.length; i++) {
for (let j = i + 1; j < players.length; j++) {
couples.push([players[i], players[j]]);
}
}
console.log(couples.length === (players.length * (players.length - 1)) / 2); // true
function findFirstAvailableCouple(couple, arr) {
for (let i = 0; i < arr.length; i++) {
if (couple.indexOf(arr[i][0]) === -1 && couple.indexOf(arr[i][1]) === -1) {
const ret = arr.splice(i, 1)[0];
return ret;
}
}
}
let matches = [];
const n = couples.length / 2;
for (let i = 0; i < n; i++) { const current = couples.splice(0, 1)[0];
matches.push([current, findFirstAvailableCouple(current, couples)]);
}
matches = matches.map(e => [...e[0], ...e[1]]);
console.log(matches);
you can check the console for the results.
This algorithm can be adapted to any number of entries for couples / players (with increase of cost)
Upvotes: 0
Reputation: 80197
There exists rather simple round-robin tournament algorithm
Set players in two rows. Fix the first player position. Write current pairs (between rows). If number of players is odd, one of them, has a rest in every round.
For next round shift players in circular way. Write pairs again. Repeat
A B
D C
----- pairs A-D, B-C
A D
C B
----
A C
B D
Upvotes: 0
Reputation: 386654
You could take a combination algorithm with checking the length of four for the wanted result.
function getTournament(array, size) {
function fork(i, t) {
if (t.length === size) {
result.push(t);
return;
}
if (i === array.length) {
return;
}
fork(i + 1, t.concat([array[i]]));
fork(i + 1, t);
}
var result = [];
fork(0, []);
return result;
}
console.log(getTournament([1, 2, 3, 4, 5, 6], 4).map(function (a) { return JSON.stringify(a); }));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 0