Here is my original solution and a sample run for 10 players.
I decided to follow this simple approach: for the first round, make a
normalized list of all players (by normalized I mean it must be a
potency of 2 - if not, the last slots should be filled with nils until
it is) and pair them picking from the head and the tail of the list.
After that, make a list from the matches (not the players) of the 1st
round and repeat until there are less than 2 matches.
# draw.rb
class Match
# All matches have an Id, a top player/team and bottom player/team
attr_reader :id, :top, :bottom
def initialize(id, top, bottom)
@id = id
@top = top
@bottom = bottom
end
end
# a Draw is composed of rounds and matches
class Draw
attr_reader :rounds
attr_reader :matches
# here is the generation of the match-ups
def initialize(players)
@matches = [] # the list of all matches
@rounds = {} # the hash of matches for each round
match = round = 1
# normalize players count into square potency
nsqrplayers = 2 ** Math.frexp(players.size - 1).last
# derive candidates for 1st round, setting byes for top players
candidates = (1..nsqrplayers).to_a.map { |c| c > players.size ? nil
: players[c-1] }
while (ncandidates = candidates.size) >= 2
while !candidates.empty?
@rounds[round] ||= []
# setup first x last matches from the candidates list
@rounds[round] << @matches[match] = Match.new(match,
candidates.shift,
candidates.pop)
match += 1
end
# derive candidates for remaining rounds, but now
# the candidates will appear in the form of match Ids
# so let's map the candidates from the winners of the previous
matches
candidates =
(((@rounds[round].first.id)..(@rounds[round].last.id))).to_a.map do |m|
# was it a bye?
@matches[m].bottom.nil? ? "#{@matches[m].top}" : "W#{m}"
end
round += 1
end
end
def to_s
buf = ""
for r in @rounds.keys.sort
buf << "R#{r}\n"
for m in @rounds[r]
buf << "M#{m.id}: #{m.top} x #{m.bottom.nil? ? 'bye' :
m.bottom}\n"
end
buf << "\n"
end
buf
end
end
players = (1..10).to_a
puts Draw.new(players).to_s
Here's the sample output:
R1
M1: 1 x bye
M2: 2 x bye
M3: 3 x bye
M4: 4 x bye
M5: 5 x bye
M6: 6 x bye
M7: 7 x 10
M8: 8 x 9
R2
M9: 1 x W8
M10: 2 x W7
M11: 3 x 6
M12: 4 x 5
R3
M13: W9 x W12
M14: W10 x W11
R4
M15: W13 x W14
Ruby Quiz wrote:
> The three rules of Ruby Quiz:
>
> 1. Please do not post any solutions or spoiler discussion for this quiz until
> 48 hours have passed from the time on this message.
>
> 2. Support Ruby Quiz by submitting ideas as often as you can:
>
> http://www.rubyquiz.com/
>
> 3. Enjoy!
>
> Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
> on Ruby Talk follow the discussion. Please reply to the original quiz message,
> if you can.
>
> -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
>
> by Demetrius Nunes
>
> In a single-elimination tournament, there is usually a previously established
> ranking for the participating players or teams, such as that the best players or
> teams are matched against the worst ones. This is done this way so there is a
> higher chance for the top players/teams to meet in the final.
>
> For example, in a small 8-player tournament, there would be 3 rounds. This first
> round would be setup like this:
>
> Round 1
> 1 x 8
> 2 x 7
> 3 x 6
> 4 x 5
>
> This is easy enough. The tough part is arranging the pairing for the following
> rounds respecting the best vs worst rule, so, imagining that all the favorites
> won their games, we would have 1x4 and 2x3 in round 2 and then finally 1x2 in
> the final. For this to happen, the tournament would have to be arranged this
> way:
>
> R1 R2 R3
> ============
> 1
> ---
> |---
> --- |
> 8 |
> |---
> 4 | |
> --- | |
> |--- |
> --- |
> 5 |
> |----
> 2 |
> --- |
> |--- |
> --- | |
> 7 | |
> |---
> 3 |
> --- |
> |---
> ---
> 6
>
> If the numbers of players/teams is not a potency of 2, then the top players
> would have a "bye" in the first round, so, a 6-player draw would go like this:
>
> R1 R2 R3
> ============
> 1
> ---
> |---
> --- |
> bye |
> |---
> 4 | |
> --- | |
> |--- |
> --- |
> 5 |
> |----
> 2 |
> --- |
> |--- |
> --- | |
> bye | |
> |---
> 3 |
> --- |
> |---
> ---
> 6
>
> So, this quiz is about writing a single-elimination tournament generator for any
> number of players/teams obeying the best vs worst rule in all rounds.
>
> For a quick look at correct answers see this "got-the-job-done" javascript/html
> implementation at:
>
> http://www.crowsdarts.com/brackets/playoff-chart.html
>
> We are looking for correct data modeling and calculation, not for correct
> presentation output of the tournament draw, but it would be nice to implement a
> #to_s output like the ones seen above (or even better: how about a beautiful
> RMagick generated image?!). In all cases, keep the model and presentation
> cleanly separated.