3

I have a mathematical problem in my JavaScript code. I need to divide a given number of players into 2 teams randomly so that each time – if players want to play again – the teams are formed again and they should be different until all the combinations are formed.

Let's say I have 4 players, so all the combinations are as follows:
[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]

However, because the team side doesn't count, there are only 3 different combinations:

[1,2] vs [3,4]
[1,3] vs [2,4]
[1,4] vs [2,3]

When the number of games played exceeds the number of combinations, it should start all over again... i.e. randomly selecting one of the three combination, randomly selecting next one and so on...

But there's a twist... and mathematical skills of mine go pretty hard south when the number of players is odd, and one of the player needs to rest one game. So, with 5 players all the match-up combinations are (last number being the player resting):

[1,2] vs [3,4] [5]
[1,2] vs [3,5] [4]
[1,2] vs [4,5] [3]

[1,3] vs [2,4] [5]
[1,3] vs [2,5] [4]
[1,3] vs [4,5] [2]

[1,4] vs [2,3] [5]
[1,4] vs [2,5] [3]
[1,4] vs [3,5] [2]

[1,5] vs [2,3] [4]
[1,5] vs [2,4] [3]
[1,5] vs [3,4] [2]

[2,3] vs [4,5] [1]
[2,4] vs [3,5] [1]
[2,5] vs [3,4] [1]

How is it possible in JavaScript to get those teams formed?

One thing that came into mind was to give each player an unique value (10^x), e.g.:

player1.value = 10;
player2.value = 100;
player3.value = 1000;
player4.value = 10000;

...and then when looping to form teams check if one the team's total value is equal to last values.

Could someone mathematically/JavaScriptly more talented please help me with this coding problem. Thanks!

micadelli
  • 2,482
  • 6
  • 26
  • 38

2 Answers2

3

Using values is a good idea, but if you make them bit masks it's easier to check which players s have been matched. e.g.

player1.value = 1 
player2.value = 2
player3.value = 4
//(dynamically player n would have value 1 << (n-1) )

By checking the mask with another player, it can be checked if they have been coupled already, since they already have their own mask value, they can never match themselves. As for the random factor, I think the easiest way is to create all combinations first, as you did in your example, and use an array of those combinations as the base of picking a random match. If you feel this approach is a good way to go, but have trouble implementing, I can try and cook up some example code.

Edit here's the example code, hope the comments help sort out their meaning Edit2 changed team combinations

    var playercount = 5; 

    //add players
    var players = new Array();
    for (var i = 0; i < playercount; i++) {
        players[i] = { Index: i, Mask: 1 << i, Name: "Player" + (i + 1), toString: function () { return this.Name; } };
        //about the 1 << i:  "<<" is a so called bit wise shift to the left.
        //1 << i has the same outcome as 2 to the power of i
    }

    //the line below would print all players
    //for (var pi in players) { var p = players[pi]; document.write(p + " (Mask:" + p.Mask + ")<br>"); } document.writeln("<br>"); 

    //create all possible team combinations
    var teams = new Array();

    var playersPerTeam = Math.floor(playercount / 2);
    function Team(){
        this.list = new Array();
        this.mask = 0;
        this.count = 0;
        this.full  =false;

        this.Add = function (i) {
            this.list.push(players[i]);
            this.mask |= players[i].Mask;
            this.full = ++this.count === playersPerTeam;
        }

        this.toString = function () {
            var res = "", cnt = this.list.length;
            for (var i = 0; i < cnt; i++) {
                if (i > 0)
                    res += i == cnt - 1 ? " and " : ",";
                res += this.list[i].Name;
            }
            return res;
        }
    }


    function addplayers() {
        var indices = new Array(playersPerTeam);
        for (var p = 0; p < playersPerTeam; p++) indices[p] = p;
        var l = playersPerTeam - 1;

        function addteam() {
            var team = new Team();
            for (var p = 0; p < playersPerTeam; p++) team.Add(indices[p]);
            teams.push(team);
        }

        function addplayer(start, depth) {
            var target = players.length - playersPerTeam + depth + 1;
            var team;
            for (var i = start; i < target; i++) {
                indices[depth] = i;
                if (depth == l)
                    addteam();
                else
                    addplayer(i + 1, depth + 1);
            }
        }

        addplayer(0, 0);

    }
    addplayers();




    //the line below would print the team combinations
    //for (var te in teams) { var t = teams[te]; document.write(t + " (mask:" + t.mask + ")<br>"); } document.writeln("<br>");

    //create matches
    var matches = new Array();
    //the matches can be created in the same way as the teams, only the first team cannot have players of the second team
    for (var i = 0; i < teams.length; i++) {
        for (var j = i + 1; j < teams.length; j++) {
            var t1 = teams[i], t2 = teams[j];
            if ((t1.mask & t2.mask) === 0) //this is where the masks come in, 
                matches.push({ Team1: t1, Team2: t2, toString: function () { return this.Team1 + " versus " + this.Team2 } });
        }
    }

    //randomize matches. Instead of picking a random match per turn, we can randomize at the
    //start, so we know all the matches in advance.
    //this can be done by using a sort on the array with a random index
    //NB, this isn't the most random randomness (or whatever you call it LOL). For better shuffling
    //there are several alternatives, but perhaps this one is enough
    matches.sort(function() { return (parseInt(Math.random() * 100) % 2);});


    //the line below prints the matches
    for (var mi in matches) { document.write(matches[mi] + "<br>"); } document.writeln("<br>");
Me.Name
  • 12,259
  • 3
  • 31
  • 48
  • sounds good – creating all combinations and then picking randomly – but understood nothing of 'bit mask' nor "1 << (n-1)". Some example code would be great! – micadelli Jun 02 '12 at 11:37
  • Sorry for the late response, I've been out for coffee and dinner :) I've added the sample to the post above – Me.Name Jun 02 '12 at 19:59
  • great stuff! Tried to run this and it works although i still don't got those "mask: players[i].Mask | players[j].Mask" and "if ((t1.mask & t2.mask) === 0)"... but I will. However, I don't know if it was due to my bad english but this divides players into a team of 2 players, and I only wanted to divide all the players into 2 separate teams. So, 9 players will be divided 5 vs 4. How the code should be changed. – micadelli Jun 02 '12 at 22:54
  • Ah sorry, did not get that. I changed the team creation. Funny thing is, the kind of recursion involved with that is relatively harder than the masking :D Anyway, about the masking, the & returns a value with the bits set on the position where both masks have a bit on. So if it's 0, no bits correspond, which is what we're after. More on bitmasking here: http://en.wikipedia.org/wiki/Mask_(computing) – Me.Name Jun 03 '12 at 09:51
  • Hi, I created a follow-up question here: http://stackoverflow.com/questions/12046888/how-to-use-bitwise-operators-to-return-bits-that-have-0 if you could take a look, thank you. – micadelli Aug 21 '12 at 00:27
1

For the even cases, just pick numbers you haven't used randomly and make teams.

For the odd cases, pick a number randomly to sit out. Then the remaining numbers are like an even case.

josh shadowfax
  • 304
  • 1
  • 5
  • By "pick numbers you haven't used randomly and make teams" you mean array index of all players, right? How not to form the same team again? And more importantly, how do I make those teams? Could you provide a real/pseudo code example. ty – micadelli Jun 02 '12 at 10:38