I am trying to write a routine that basically does "seeding" for teams, much like the recent NCAA basketball tournament, applied to golf teams where there is often an odd number of players. Basketball is easy, there are 64 team and 4 brackets and the highest seed plays the lowest seed. Mine is a little more complicated.
If I have 13 players show up for game, there would be 4 teams, 1 foursome and 3 threesomes. The players would be sorted by there quota/handicap. Player 1 is the highest player and player 13 is the lowest. The goal is to have the skills on each team be distributed as evenly as possible. I actually have this working but it is really ugly code and I'm trying to re-factor it into something that looks like ruby instead of basic!
I've used several variation of arrays. If I start out with an array of 13 players:
[1,2,3,4,5,6,7,8,9,10,11,12,13]
I can break it up into seeds, first to take care of the foursome (usually called ABCD groups)
[[1,2,3,4],[5,6,7],[8,9,10],[11,12,13]]
To get the even distribution, the highest player in the A group would play with the lowest player in group B, the highest Player in group C and the lowest player in group D. After the foursome is formed, the remaining players would be group into 3 seeds, ABC and the some type of routine applied to the 3 groups. It ending teams would be:
[[1,7,8,13]] # foursome, 1st team
[[2,3,4],[5,6,9],[10,11,12]] # remaining players ofter first team formed and seeded into 3 group
With threesome, I'd put highest player in group A with lowest player in group B and the highest player in group C. The final results should be something like.
[[1,7,8,13],[2,9,10],[3,6,11],[4,5,12]]
If you had 15 players show up, there would be 3 foursomes and 1 threesome and you'd get teams like.
[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15]]
[[1,8,9,15],[2,7,10,14],[3,6,11,13],[4,5,9]]
My seeding method is complicated enough and all the shifting, popping and flattening to form the teams is worse. I just wonder if anyone has any suggestions on another approach.
def seed_sample(count,make_up='')
players = [] # init an empty array
1.upto(count) do |i|
players << i
end # => [1,2,3,4,5,6,...,count]
abcd = [] #init empty seed array
if make_up == 'foursomes'
4.times{abcd << players.shift(count / 4)}
elsif make_up == 'threesomes'
3.times{abcd << players.shift(count / 3)}
elsif make_up == 'twosomes'
2.times{abcd << players.shift(count / 2)}
else #mixed
a_pop = (count / 4) + 1 # number of a players to put in seed stack
abcd << players.shift(a_pop) # A players, always first
rem = players.count # s array is reduced by a_pop, so get count
if rem.modulo(3).zero?
3.times{abcd << players.shift(rem / 3)} # B & C & D players
elsif rem.modulo(3) == 1
abcd << players.shift(a_pop) # B players
2.times{abcd << players.shift(rem / 3)} # C & D players
else # rem.modulo(3) == 2
2.times{abcd << players.shift(a_pop)}# B & C players
abcd << players # remainder = D players
end
end
@seeds = abcd
return abcd
end
Taking suggestions from RyanK, I'm on my way. Below is a test seed method that also forms the teams. All the information is known from the Class, so I just have to replace about 100 lines of code with this - unless it can be improved.
def test_seed(count,fours=nil,threes=nil,twos=nil)
players = Array(1..count) # => [1,2,3,4,5,6,...,count]
abcd = [] #init empty seed array
fours.times{abcd << players.shift(4)} if fours.present?
threes.times{abcd << players.shift(3)} if threes.present?
twos.times{abcd << players.shift(2)} if twos.present?
abcd.each_index do |s| # reverse odd stacks to distribute skills
unless s.modulo(2).zero?
abcd[s].reverse!
end
end
# shuffle stacks by taking card off top of each stack
seeded = []
abcd.count.times do
abcd.each_index do |i|
seeded << abcd[i].shift if abcd[i].present?
end
end
# now lets put the teams together
@teams = []
fours.times{@teams << seeded.shift(4)} if fours.present?
threes.times{@teams << seeded.shift(3)} if threes.present?
twos.times{@teams << seeded.shift(2)} if twos.present?
return abcd,seeded, @teams
end
Ok, so here are a few ways to make your code (which works, you say) better:
def seed_sample(count,make_up='')
players = (1..count).to_a
abcd = [] #init empty seed array
num = 0
if make_up == 'foursomes'
num = 4
elsif make_up == 'threesomes'
num = 3
elsif make_up == 'twosomes'
num = 2
else
return
end
abcd << players.shift((count / num.to_f).ceil) while players.count > 0
abcd[1],abcd[3] = abcd[3],abcd[1] #Flip the second and fourth sub-arrays
#Of course, always assumes 4 groups and max of 16 people
@seeds = [[],[],[],[]]
abcd.each_with_index do |value, index|
4.times {|i| @seeds[i] = value[index]}
end
return @seeds
end
That should at least get you started. That last part I'm not sure about, so test it and make sure it works.
I took a different approach and sorted all of the players first (where #1 was the best and #13 is the worst) and then used slices to output the teams.
# Seed class
# Initialize with count of all players and make up of first team
# Sorts all of the players in pairs with the best and worst player
# as the first pair
class Seed
attr_accessor :count, :make_up
def initialize( count, make_up )
@count = count
@make_up = make_up
end
def run
sorted_array = sort
first_team( sorted_array )
other_teams( sorted_array )
end
def sort
arr = (1..@count).to_a
arr_new = []
sorting_iteration = (@count/2) + 1
sorting_iteration.times do
arr_new << arr.shift
arr_new << arr.pop unless arr.size == 0
end
arr_new
end
def first_team( sorted_array )
p sorted_array.slice(0, @make_up)
end
def other_teams( sorted_array )
all_others = sorted_array.slice(@make_up, @count)
all_others
other_make_up = @make_up - 1
p all_others.each_slice(other_make_up) { |a| p a }
end
end
round = Seed.new( 13, 4 )
round.run
There probably won't be any best answer, since I was just asking for an approach. I also left out a lot of information. Not only can you have mixed teams you can have all foursome, all threesome, all twosome and even individuals as a team. You can have as few as 3 or 4 players show up and I've seen it over 30.
Thanks for the suggestions. My edited test routine had problems, and after several days playing with a deck of card I came up with the following class. It is driven by options that are provided by a user in a Teams class and passed to Seed.
class Seed
# usage
# input a set of parameter build from selection of team formation options
# output an ordered array of seeds that match selection options
# params example
# {:team_makeup=>"mixed", :seed_method=>"seeded", :count=>13, :teams=>4, :teetimes=>4,
# :foursomes=>1, :threesomes=>3, :twosomes=>nil, :seed_size=>4, :first_size=>1}
attr_accessor :count, :params, :seeded
def initialize(seed_params)
@params = seed_params
@seeded = []
end
def build_seeded_array
players = Array(1..@params[:count]) # => [1,2,3,4,5,6,...,count]
seeds = seed_players(players)
@seeded = seeds_to_seeded(seeds,@params[:seed_size],@params[:first_size])
end
def seed_players(players,size=nil)
seeds = [] #init empty seed array
size = size ||= @params[:seed_size]
players_count = players.count
shift_size = players_count / size
remaining = players_count.modulo(size)
size.times{seeds << players.shift(shift_size)}
# with mixed teams, there will be players left, shift to last seed, then distribute all seeds
players.count.times{seeds[-1] << players.shift}
seeds = shift_seeds(seeds,remaining) unless remaining.zero?
seeds
end
def seeds_to_seeded(seeds,seed_size,numb)
seeded = seeded_seeds(seeds,numb)
players = seeds.flatten.sort
unless players.blank?
seed_size -= 1
numb = players.count / seed_size
seeds = seed_players(players,seed_size)
seeded << seeded_seeds(seeds,numb)
end
seeded.flatten
end
def seeded_seeds(seeds,numb)
seeds = distribution_swap(seeds)
seeded = []
numb.times do
seeds.each_index do |i|
seeded << seeds[i].shift if seeds[i].present?
end
end
seeded
end
def distribution_swap(seeds)
seeds.each_index do |s| # reverse odd stacks to distribute skills
unless s.modulo(2).zero?
seeds[s].reverse!
end
end
seeds
end
def shift_seeds(seeds,remaining)
# mixed team will stack last seed, need to shift them.
# [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16, 17, 18, 19]]
# [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19]]
return(seeds) if @params[:foursomes].blank?
if remaining == 3
3.times{seeds[-2] << seeds[-1].shift}
2.times{seeds[-3] << seeds[-2].shift}
1.times{seeds[-4] << seeds[-3].shift}
elsif remaining == 2
2.times{seeds[-2] << seeds[-1].shift}
2.times{seeds[-3] << seeds[-2].shift}
1.times{seeds[-4] << seeds[-3].shift}
else
1.times{seeds[-2] << seeds[-1].shift}
1.times{seeds[-3] << seeds[-2].shift}
1.times{seeds[-4] << seeds[-3].shift}
end
seeds
end
end
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.