简体   繁体   中英

Looking for approach to create seeded golf teams for a mini-tournement

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM