0

Im new here and into programming. Im not native english so please apologize if you dont understand me very well :-)

I`ve started learning Javascript and still doing. Now I want to achieve a simple project which gives me two Teams with equal skills.

Questions:

  1. It seems to work but I want to get random teams. This gives me always the same. How can I resolve this?
  2. I am able to print the values to see that its correct, but how can I print the player names (Variables like P1, P2, P3...)

Thank you

//Player skill values

let P1 = 2;
let P2 = 3;
let P3 = 1;
let P4 = 3;
let P5 = 4;
let P6 = 4;
let P7 = 5;
let P8 = 2;

//try to achieve two equal teams

function aufteilen(arr) {
    arr.sort().reverse()
    let a = [], b = [], sumA = 0, sumB = 0, i = 0

    while (i < arr.length) {
        if (!sumA && !sumB || sumA == sumB) {
            a.push(arr[i])
            sumA += arr[i]
        } else if (sumA < sumB) {
            a.push(arr[i])
            sumA += arr[i];
        } else if (sumB < sumA) {
            b.push(arr[i])
            sumB += arr[i];
        }
        i++
    }

    console.log(`Total: ${sumA} ${sumB}`)
    return [a, b]
}

console.log(aufteilen([P1, P2, P3, P4, P5, P6, P7, P8]))
Can O' Spam
  • 2,718
  • 4
  • 19
  • 45
v0id
  • 3
  • 2
  • 1
    The way the code works, is that it sorts the team into descending values, and then distributes the values, from largest to smallest. As such it makes total sense that it's pretty consistent. You might want to look for a different type of algorithm. – Keith Jan 17 '23 at 13:40
  • One idea.. is just brute force randomly the teams until a team is found that is equal. It might be the case that's it's impossible to make teams equal, so you might want to keep track of the best too and stop after a timeout period. For keeping track of team names, your structure would be best if the teams were placed into an array. Also do teams have to be equal in players, or just skills? – Keith Jan 17 '23 at 13:49

3 Answers3

0

1 - you need a shuffle method, something like below or from a library:

function shuffle(array) {
  array.sort(() => Math.random() - 0.5);
}

2 - you need an object to display player names. your players are just numbers.

// your player
let P1 = 2;

// what you would need to display a name
let P1 = { id: 2, name: "Mario" }

// then u can display the name with
P1.name // "Mario"
zangab
  • 428
  • 2
  • 5
  • Just FYI: [Is it correct to use JavaScript Array.sort() method for shuffling?](https://stackoverflow.com/q/962802) – 001 Jan 17 '23 at 13:59
  • See my comment to the other poster, taking out the `.sort().reverse()` make the code sub-optimal, and a good chance of not getting equal teams.. – Keith Jan 17 '23 at 13:59
  • yeah it might not be the best code, but it works. i just googled a simple `shuffle` and found this^^ you would also use `array.slice()` to get break an array into two and not a while-loop. but he's just starting so an easy solution might be the best for now ;) – zangab Jan 17 '23 at 14:34
0

Your array.sort().reverse() is the reason why you get this.

I would recommend to shuffle() before passing it to your function.

Here is my guess :

function shuffle(arr) {
    for (let i = arr.length - 1; i > 0; i--) {
        let j = Math.floor(Math.random() * (i + 1));
        [arr[i], arr[j]] = [arr[j], arr[i]];
    }
    return arr;
}

console.log(aufteilen(shuffle([P1, P2, P3, P4, P5, P6, P7, P8])));

You can then create an object Players, add all infos you want and do it like this :

let players = {
    P1: 2,
    P2: 3,
    P3: 1,
    P4: 3,
    P5: 4,
    P6: 4,
    P7: 5,
    P8: 2,
};

let playerSkills = Object.values(players);
console.log(aufteilen(shuffle(playerSkills)));

Hope it will help !

Johan
  • 2,088
  • 2
  • 9
  • 37
  • There is a reason for the `.sort().reverse()`, taking it out means there is a good chance you will not get equal teams. This is because it distributes from high to low,. aka distributing a value of 1 is more likely to work than 5.. – Keith Jan 17 '23 at 13:57
0

Creating a team selection based on skill and making it random using the OP's algorithm is not really possible.

It's also possible for the OP's code to make teams based on player count not equal, I would assume a team picker would try to make teams equal on players & skill.

So one approach is just a simple brute force one, basically randomly pick teams, and compare the skills. Stop comparing after a set amount of time, just in case a perfectly equal skill cannot be met, or when skills are equal.

Below is a working example doing the above, it will also handle making the teams equal if we don't have an even number of players.

const players = [
  {name: 'P1', skill: 2},
  {name: 'P2', skill: 3},
  {name: 'P3', skill: 1},
  {name: 'P4', skill: 3},
  {name: 'P5', skill: 4},
  {name: 'P6', skill: 4},
  {name: 'P7', skill: 5},
  {name: 'P8', skill: 2},
];

const score = team =>
  team.reduce((a, v) => {
    a += v.skill;
    return a;
  }, 0);

function createFairTeam(players) {
  function createTeams() {
    const team = {
      team1: [],
      team2: [],
      skillTot1: 0,
      skillTot2: 0,
      score: 0
    }
    const p = [...players];
    const t1size = Math.trunc(players.length / 2);
    //fill team1
    for (let l = 0; l < t1size; l++) {
      const pos = Math.trunc(Math.random() * p.length);
      const f = p.splice(pos, 1)[0];
      team.skillTot1 += f.skill;
      team.team1.push(f);
    }
    //fill team2
    for (let l = 0; l < p.length;) {
      const pos = Math.trunc(Math.random() * p.length);
      const f = p.splice(pos, 1)[0];
      team.team2.push(f);
      team.skillTot2 += f.skill; 
    }
    //calc score.
    team.score = Math.abs(
      score(team.team1) -
      score(team.team2)
    );
    return team;
  }
  const team = createTeams();
  let best = team;
  const start_time = Date.now();
  while (Date.now() - start_time < 200) {
    //if total skill is odd, the best score
    //would be 1, if even it would be 0
    //so lets check for less than equal to 1
    //for early termination.
    if (best.score <= 1) break;
    const newTeams = createTeams();
    if (newTeams.score < best.score) 
      best = newTeams;
  }
  return best;
}


function doIt() {
  const teams = createFairTeam(players);
  const table = document.querySelector('table');
  const tbody = table.querySelector('tbody');
  tbody.innerHTML = '';
  for (let l = 0; 
  l < Math.max(teams.team1.length, teams.team2.length); l ++) {        
    const p1 = teams.team1[l];
    const p2 = teams.team2[l];
    const tr = document.createElement('tr');
    ////
    let td = document.createElement('td');
    td.innerText = p1 ? p1.name : '';
    tr.appendChild(td);
    ////
    td = document.createElement('td');
    td.innerText = p1 ? p1.skill : '';   
    tr.appendChild(td);
    ////
    td = document.createElement('td');
    td.innerText = p2 ? p2.name : '';   
    tr.appendChild(td);
    tbody.appendChild(tr);
    ////
    td = document.createElement('td');
    td.innerText = p2 ? p2.skill : '';   
    tr.appendChild(td);
    tbody.appendChild(tr);
  }
  //// tots
  document.querySelector('#t1total').innerText =  teams.skillTot1;
  document.querySelector('#t2total').innerText = 
 teams.skillTot2;
}

doIt();

document.querySelector('button').addEventListener('click', doIt);
table {
  width: 100%;
}
<button>Another Team</button>
<table border="1">
  <thead>
    <tr>
      <th colspan="2">Team 1</th>
      <th colspan="2">Team 2</th>
    </tr>
    <tr>
      <th>Player</th>
      <th>Skill</th>
      <th>Player</th>
      <th>Skill</th>
    </tr>
  </thead>
  <tbody></tbody>
  <tfoot>
    <tr>
      <td>Total</td><td id="t1total"></td>
      <td>Total</td><td id="t2total"></td>
    </tr>
  </tfoot>
</table>
Keith
  • 22,005
  • 2
  • 27
  • 44