Peter Nguyen
Peter Nguyen

Reputation: 359

How to divide all members into individual fair teams based on their ratings?

I want to divide players into individual fair teams based on their rating. For example, I have a list of players like this:

players = [{
    name: "Qasim",
    rating: 1
  }, {
    name: "Mahsam",
    rating: 7
  }, {
    name: "Aj",
    rating: 3
  }, {
    name: "Osman",
    rating: 6
  }, {
    name: "Usama",
    rating: 2
  }, {
    name: "Bilal",
    rating: 8
  }, {
    name: "Kaka",
    rating: 20
  }, {
    name: "Owen",
    rating: 15
  }
]

I want to divide them into 4 teams with the best equally total scores and also equally in members like this:

Team A       Team B       Team C     Team D
=======      =======      =======    =======
Kaka: 20     Owen: 15     Bilal: 8   Mahsam: 7
Qasim: 1     Usama: 2     Aj: 3      Osman: 6

I have found a way to solve this problem, but it's hard to turn it into ruby code. Assume we can have more than 8 players and the number of teams can vary from 2 to 4 teams.

1. Sort all players by their ratings descendingly.
2. Assign team A the best player.
3. Assign team B the next best player.
4. Assign team C the next best player.
5. Assign team D the next best player.
6. Assign team D the next best player.
7. Assign team C the next best player.
8. Assign team B the next best player.
9. Assign team A the next best player.
10. Go to 2
11. End when we're out of players.

Actually the team can vary from 2 to 4 teams, all the players in each team must be equal and the total ratings of each team must be equal too or as close to equal as possible.

The players can be any numbers and must be divisible by teams.

For example, if 2 teams, the total players must be even. If 3 teams, the total players must be divisible by 3 and if 4 teams, the total players must be divisible by 4.

Upvotes: 1

Views: 1900

Answers (2)

Cary Swoveland
Cary Swoveland

Reputation: 110725

You can implement the assignment algorithm you describe as follows.

def assign(nbr_teams, players)
  flip = [true, false].cycle
  players.
    sort_by { |p| p[:rating] }.
    each_slice(nbr_teams).
    map { |a| flip.next ? a.reverse : a }.
    transpose
end

assign(4, players)
  #=> [[{:name=>"Osman",  :rating=>6}, {:name=>"Mahsam", :rating=>7}],
  #    [{:name=>"Aj",     :rating=>3}, {:name=>"Bilal",  :rating=>8}],
  #    [{:name=>"Usama",  :rating=>2}, {:name=>"Owen",   :rating=>15}],
  #    [{:name=>"Qasim",  :rating=>1}, {:name=>"Kaka",   :rating=>20}]] 

The assignments would be as follows if there were 2 teams.

assign(2, players)
  #=> [[{:name=>"Usama",  :rating=>2}, {:name=>"Aj",     :rating=>3},
  #     {:name=>"Bilal",  :rating=>8}, {:name=>"Owen",   :rating=>15}],
  #    [{:name=>"Qasim",  :rating=>1}, {:name=>"Osman",  :rating=>6},
  #     {:name=>"Mahsam", :rating=>7}, {:name=>"Kaka",   :rating=>20}]]

The steps are as follows.

nbr_teams = 4
flip = [true, false].cycle
  #=> #<Enumerator: [true, false]:cycle>

Array#cycle works like this: flip.next #=> true, flip.next #=> false, flip.next #=> true, and so on. Continuing,

a = players.sort_by { |p| p[:rating] }
  #=> [{:name=>"Qasim",  :rating=>1},  {:name=>"Usama", :rating=>2},
  #    {:name=>"Aj",     :rating=>3},  {:name=>"Osman", :rating=>6},
  #    {:name=>"Mahsam", :rating=>7},  {:name=>"Bilal", :rating=>8},
  #    {:name=>"Owen",   :rating=>15}, {:name=>"Kaka", :rating=>20}] 
b = a.each_slice(nbr_teams)
  #=> #<Enumerator:
  #     [{:name=>"Qasim",  :rating=>1},  {:name=>"Usama", :rating=>2},
  #      {:name=>"Aj",     :rating=>3},  {:name=>"Osman", :rating=>6},
  #      {:name=>"Mahsam", :rating=>7},  {:name=>"Bilal", :rating=>8},
  #      {:name=>"Owen",   :rating=>15}, {:name=>"Kaka",  :rating=>20}]
  #     :each_slice(4)> 

We can convert this enumerator to an array to see the objects it will generate and pass to map.

b.to_a
  #=> [[{:name=>"Qasim",  :rating=>1},  {:name=>"Usama", :rating=>2},
  #     {:name=>"Aj",     :rating=>3},  {:name=>"Osman", :rating=>6}], 
  #    [{:name=>"Mahsam", :rating=>7},  {:name=>"Bilal", :rating=>8}, 
  #     {:name=>"Owen",   :rating=>15}, {:name=>"Kaka",  :rating=>20}]] 

Continuing,

c = b.map { |a| flip.next ? a.reverse : a }
  #=> [[{:name=>"Osman",  :rating=>6},  {:name=>"Aj",    :rating=>3},
  #     {:name=>"Usama",  :rating=>2},  {:name=>"Qasim", :rating=>1}],
  #    [{:name=>"Mahsam", :rating=>7},  {:name=>"Bilal", :rating=>8},
  #     {:name=>"Owen",   :rating=>15}, {:name=>"Kaka",  :rating=>20}]] 
c.transpose
  #=> [[{:name=>"Osman", :rating=>6}, {:name=>"Mahsam", :rating=>7}],
  #    [{:name=>"Aj",    :rating=>3}, {:name=>"Bilal",  :rating=>8}],
  #    [{:name=>"Usama", :rating=>2}, {:name=>"Owen",   :rating=>15}],
  #    [{:name=>"Qasim", :rating=>1}, {:name=>"Kaka",   :rating=>20}]] 

It may be desirable to convert the results to an array of hashes.

assign(4, players).map { |a| a.map { |h| [h[:name], h[:rating]] }.to_h }
  #=> [{"Osman"=>6, "Mahsam"=>7},
  #    {"Aj"   =>3, "Bilal" =>8},
  #    {"Usama"=>2, "Owen"  =>15},
  #    {"Qasim"=>1, "Kaka"  =>20}] 

Upvotes: 3

Pravesh Khatri
Pravesh Khatri

Reputation: 2264

We can solve it sorting the hash by rating as key

players.sort_by { |k| k[:rating] }

Now when you have sorted array.

You can iterate upto half of length of the array, and push i element and length-i element in one team in this case you have 4 teams.

def divide_teams players
   players = players.sort_by { |k| k[:rating] } # sorted
   len = players.length
   teams = Hash.new(0)
   (len/2).times do |i|
      teams["team#{i+1}"] = [players[i], players[len-i-1]]
   end
   teams
end

divide_teams players

=> {"team1"=>[{:name=>"Qasim", :rating=>1}, {:name=>"Kaka", :rating=>20}],
 "team2"=>[{:name=>"Usama", :rating=>2}, {:name=>"Owen", :rating=>15}],
 "team3"=>[{:name=>"Aj", :rating=>3}, {:name=>"Bilal", :rating=>8}],
 "team4"=>[{:name=>"Osman", :rating=>6}, {:name=>"Mahsam", :rating=>7}]}

Right now, I have assumed, 4 teams and in each team there are 2 member.

you can change the function according to your need, if function teams is a dynamic variable.

Thanks.

Upvotes: 1

Related Questions