From 5a6443aede22552b9c1160fc767760759712ec05 Mon Sep 17 00:00:00 2001 From: Andrew Flockhart Date: Tue, 1 Oct 2013 10:11:15 -0400 Subject: [PATCH 1/7] new sorted tree method of pairing --- swiss.rb | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 2 deletions(-) diff --git a/swiss.rb b/swiss.rb index 0d93aef..efb6115 100644 --- a/swiss.rb +++ b/swiss.rb @@ -1,5 +1,6 @@ class Swiss attr_accessor :players + attr_accessor :used_pairs attr_accessor :current_pairs attr_accessor :current_round attr_accessor :number_of_rounds @@ -11,8 +12,9 @@ def initialize(opts = {}) puts "Name of player #{i + 1}?" name = gets.chomp name = nil if name.empty? - Player.new(:name => name) + Player.new(name: name, id: i) end + self.used_pairs = [] self.number_of_rounds = opts[:number_of_rounds] || calculate_rounds_required(players.size) end @@ -42,12 +44,22 @@ def announce_pairings if pair.length == 1 puts "#{pair[0].name} gets a bye" else + used_pairs << [pair[0].id, pair[1].id] puts "#{pair[0].name} VS #{pair[1].name}" end puts "----------------" end end + def pair_has_already_played?(pairing) + self.used_pairs.each do |used_pair| + if used_pair.sort == pairing.sort + return true + end + end + return false + end + def announce_scores puts_announcement "Scores after round #{current_round}:" players.each { |p| puts "#{p.name} :: #{p.match_points}" } @@ -83,6 +95,67 @@ def next_round! current_round == 1 ? initial_round : regular_round end + def valid_pairings(user_ids) + user_ids.combination(2).to_a.reject do |pairing| + self.pair_has_already_played(pairing) + end + end + + def swap_pairs(pairs, pairs_needing_swap) + user_ids = player_ids + valid_pairings = valid_pairings(user_ids) + raise 'no more valid pairs' if valid_pairings*2 < user_ids.length + end + + def player_ids + self.players.map(&:id) + end + + + def find_combos(valid_combinations, prior_path = []) + # right now just do even pairings + path_length_needed = self.players.count / 2 + valid_combinations.each do |combo| + prior_path << combo + if prior_path.length == path_length_needed + return prior_path + else + remaining_ids = player_ids.reject{ |n| prior_path.flatten.include?(n) } + further_allowed_combos = valid_pairings(remaining_ids) + if further_allowed_combos.length + return find_combos(further_allowed_combos, prior_path) + else + next + end + end + end + end + + def sort_by_best_match(combos) + combos.sort do |c1, c2| + combo1_p1 = self.players.select{ |p| p.id == c1[0] } + combo1_p2 = self.players.select{ |p| p.id == c1[1] } + combo1_score = 1.0/((combo1_p1.match_points - combo1_p2.match_points) + 0.1* (combo1_p1.game_points - combo1_p2.game_points)) + + combo2_p1 = self.players.select{ |p| p.id == c2[0] } + combo2_p2 = self.players.select{ |p| p.id == c2[1] } + combo2_score = 1.0/((combo2_p1.match_points - combo2_p2.match_points) + 0.1* (combo2_p1.game_points - combo2_p2.game_points)) + + combo2_score <=> combo1_score + end + end + + def tree_pair + potential_pairings = valid_pairings(player_ids) + sorted_potential_pairs = sort_by_best_match(potential_pairings) + one_true_path = find_combos(sorted_potential_pairs) + one_true_path.each do |combo| + player1 = self.players.select{ |p| p.id == combo[0] } + player2 = self.players.select{ |p| p.id == combo[0] } + self.current_pairs << [player1, player2] + end + end + def regular_round # pair players with the same match points against each other randomly # DCI says you do not use tiebreakers when pairing @@ -93,8 +166,43 @@ def regular_round rand(100) <=> rand(100) end end.reverse - self.current_pairs = sorted_players.each_slice(2).to_a + + self.current_pairs.each_with_index do |pair, i| + if pair_has_already_played(pair.map(&:id)) + pairs_needing_swap << i + end + end + if pairs_needing_swap.length > 0 + tree_pair() + end + + + # this method of pairing will be less likely to have rematches but its still possible + # self.current_pairs = [] + #groups = players.group_by(&:match_points).values + #groups.each_with_index do |group, i| + + #while group.length > 1 do + #potential_pair = group.sample(2) + #pair_ids = [pair[0].id, pair[1].id] + #unless pair_has_already_played(pair_ids) + #current_pairs << potential_pair + #group.delete(potential_pair[0]) + #group.delete(potential_pair[1]) + #end + #end + + #if remaining_player = group[0] #an extra player will be bumped to the next group or given a bye + #if next_group = groups[i+1] + #next_group << remaining_player + #else + #current_pairs << remaining_player + #end + #end + #end + + announce_pairings retrieve_scores end @@ -120,9 +228,11 @@ class Player attr_accessor :match_points attr_accessor :game_points attr_accessor :matches + attr_accessor :id def initialize(opts = {}) self.name = opts[:name] || "Player #{(0...4).map{(65+rand(26)).chr.upcase}.join}" # "Player XASB" + self.id = opts[:id] self.game_points = 0 self.match_points = 0 end From 8676fc03e09bfd1c0d7423f8e86ab13859946e3a Mon Sep 17 00:00:00 2001 From: Andrew Flockhart Date: Tue, 15 Oct 2013 21:19:25 -0400 Subject: [PATCH 2/7] WIP --- swiss.rb | 87 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 36 deletions(-) diff --git a/swiss.rb b/swiss.rb index efb6115..8047f6e 100644 --- a/swiss.rb +++ b/swiss.rb @@ -1,3 +1,4 @@ +DEBUG = true class Swiss attr_accessor :players attr_accessor :used_pairs @@ -6,13 +7,20 @@ class Swiss attr_accessor :number_of_rounds def initialize(opts = {}) + self.current_round = 1 puts "Number of players?" - self.players = gets.chomp.to_i.times.map do |i| - puts "Name of player #{i + 1}?" - name = gets.chomp - name = nil if name.empty? - Player.new(name: name, id: i) + if DEBUG + self.players = (0..6).to_a.map do |i| + Player.new(name: "p #{i}", id: i) + end + else + self.players = gets.chomp.to_i.times.map do |i| + puts "Name of player #{i + 1}?" + name = gets.chomp + name = nil if name.empty? + Player.new(name: name, id: i) + end end self.used_pairs = [] self.number_of_rounds = opts[:number_of_rounds] || calculate_rounds_required(players.size) @@ -27,8 +35,6 @@ def begin! exit end - private - def puts_announcement(text, options = {}) puts 80.times.map{ options[:character] || "#" }.to_a.join("") # magic number, terminal width announcement_string = (39 - (text.length / 2)).times.map{ options[:character] || "#" }.to_a.join("") @@ -44,6 +50,8 @@ def announce_pairings if pair.length == 1 puts "#{pair[0].name} gets a bye" else + puts "just #{pair[0]}" + puts "#{pair[0]} and #{pair[0].id}" used_pairs << [pair[0].id, pair[1].id] puts "#{pair[0].name} VS #{pair[1].name}" end @@ -97,7 +105,7 @@ def next_round! def valid_pairings(user_ids) user_ids.combination(2).to_a.reject do |pairing| - self.pair_has_already_played(pairing) + self.pair_has_already_played?(pairing) end end @@ -133,12 +141,12 @@ def find_combos(valid_combinations, prior_path = []) def sort_by_best_match(combos) combos.sort do |c1, c2| - combo1_p1 = self.players.select{ |p| p.id == c1[0] } - combo1_p2 = self.players.select{ |p| p.id == c1[1] } + combo1_p1 = self.players.select{ |p| p.id == c1[0] }[0] + combo1_p2 = self.players.select{ |p| p.id == c1[1] }[0] combo1_score = 1.0/((combo1_p1.match_points - combo1_p2.match_points) + 0.1* (combo1_p1.game_points - combo1_p2.game_points)) - combo2_p1 = self.players.select{ |p| p.id == c2[0] } - combo2_p2 = self.players.select{ |p| p.id == c2[1] } + combo2_p1 = self.players.select{ |p| p.id == c2[0] }[0] + combo2_p2 = self.players.select{ |p| p.id == c2[1] }[0] combo2_score = 1.0/((combo2_p1.match_points - combo2_p2.match_points) + 0.1* (combo2_p1.game_points - combo2_p2.game_points)) combo2_score <=> combo1_score @@ -168,12 +176,13 @@ def regular_round end.reverse self.current_pairs = sorted_players.each_slice(2).to_a + need_swap = false self.current_pairs.each_with_index do |pair, i| - if pair_has_already_played(pair.map(&:id)) - pairs_needing_swap << i + if pair_has_already_played?(pair.map(&:id)) + need_swap = true end end - if pairs_needing_swap.length > 0 + if need_swap tree_pair() end @@ -183,23 +192,23 @@ def regular_round #groups = players.group_by(&:match_points).values #groups.each_with_index do |group, i| - #while group.length > 1 do - #potential_pair = group.sample(2) - #pair_ids = [pair[0].id, pair[1].id] - #unless pair_has_already_played(pair_ids) - #current_pairs << potential_pair - #group.delete(potential_pair[0]) - #group.delete(potential_pair[1]) - #end - #end - - #if remaining_player = group[0] #an extra player will be bumped to the next group or given a bye - #if next_group = groups[i+1] - #next_group << remaining_player - #else - #current_pairs << remaining_player - #end - #end + #while group.length > 1 do + #potential_pair = group.sample(2) + #pair_ids = [pair[0].id, pair[1].id] + #unless pair_has_already_played(pair_ids) + #current_pairs << potential_pair + #group.delete(potential_pair[0]) + #group.delete(potential_pair[1]) + #end + #end + + #if remaining_player = group[0] #an extra player will be bumped to the next group or given a bye + #if next_group = groups[i+1] + #next_group << remaining_player + #else + #current_pairs << remaining_player + #end + #end #end @@ -243,11 +252,17 @@ def award_bye_points end def award_match_and_game_points - puts "Match score results for #{name} (3 for win, 1 for draw, 0 for loss):" - self.match_points += gets.chomp.to_i + if DEBUG + self.match_points += rand(10) % 3 + self.game_points += rand(10) % 6 + else + puts "Match score results for #{name} (3 for win, 1 for draw, 0 for loss):" + + self.match_points += gets.chomp.to_i - puts "Game score results for #{name} (3 for EACH win, 1 for EACH draw, 0 for loss):" - self.game_points += gets.chomp.to_i + puts "Game score results for #{name} (3 for EACH win, 1 for EACH draw, 0 for loss):" + self.game_points += gets.chomp.to_i + end end end From f9c2af6d4593f342d41056a57b6108648e811522 Mon Sep 17 00:00:00 2001 From: Andrew Flockhart Date: Fri, 29 Nov 2013 17:12:19 -0500 Subject: [PATCH 3/7] cleanup/ no repeat algorithm working --- swiss.rb | 49 ++++++++----------------------------------------- 1 file changed, 8 insertions(+), 41 deletions(-) diff --git a/swiss.rb b/swiss.rb index 8047f6e..0aad9ea 100644 --- a/swiss.rb +++ b/swiss.rb @@ -1,3 +1,4 @@ +require 'pry' DEBUG = true class Swiss attr_accessor :players @@ -11,7 +12,7 @@ def initialize(opts = {}) self.current_round = 1 puts "Number of players?" if DEBUG - self.players = (0..6).to_a.map do |i| + self.players = (0..3).to_a.map do |i| Player.new(name: "p #{i}", id: i) end else @@ -50,8 +51,6 @@ def announce_pairings if pair.length == 1 puts "#{pair[0].name} gets a bye" else - puts "just #{pair[0]}" - puts "#{pair[0]} and #{pair[0].id}" used_pairs << [pair[0].id, pair[1].id] puts "#{pair[0].name} VS #{pair[1].name}" end @@ -109,12 +108,6 @@ def valid_pairings(user_ids) end end - def swap_pairs(pairs, pairs_needing_swap) - user_ids = player_ids - valid_pairings = valid_pairings(user_ids) - raise 'no more valid pairs' if valid_pairings*2 < user_ids.length - end - def player_ids self.players.map(&:id) end @@ -154,13 +147,15 @@ def sort_by_best_match(combos) end def tree_pair + self.current_pairs = [] potential_pairings = valid_pairings(player_ids) sorted_potential_pairs = sort_by_best_match(potential_pairings) one_true_path = find_combos(sorted_potential_pairs) one_true_path.each do |combo| - player1 = self.players.select{ |p| p.id == combo[0] } - player2 = self.players.select{ |p| p.id == combo[0] } + player1 = self.players.find{ |p| p.id == combo[0] } + player2 = self.players.find{ |p| p.id == combo[1] } self.current_pairs << [player1, player2] + binding.pry if current_pairs.length > 4 end end @@ -179,38 +174,10 @@ def regular_round need_swap = false self.current_pairs.each_with_index do |pair, i| if pair_has_already_played?(pair.map(&:id)) - need_swap = true + tree_pair() + break end end - if need_swap - tree_pair() - end - - - # this method of pairing will be less likely to have rematches but its still possible - # self.current_pairs = [] - #groups = players.group_by(&:match_points).values - #groups.each_with_index do |group, i| - - #while group.length > 1 do - #potential_pair = group.sample(2) - #pair_ids = [pair[0].id, pair[1].id] - #unless pair_has_already_played(pair_ids) - #current_pairs << potential_pair - #group.delete(potential_pair[0]) - #group.delete(potential_pair[1]) - #end - #end - - #if remaining_player = group[0] #an extra player will be bumped to the next group or given a bye - #if next_group = groups[i+1] - #next_group << remaining_player - #else - #current_pairs << remaining_player - #end - #end - #end - announce_pairings retrieve_scores From d7abfe04a34ab4fe691c6641c7e56313afdd77f0 Mon Sep 17 00:00:00 2001 From: Andrew Flockhart Date: Fri, 29 Nov 2013 19:26:56 -0500 Subject: [PATCH 4/7] working with bye player --- swiss.rb | 76 +++++++++++++++++++++++++++----------------------------- 1 file changed, 36 insertions(+), 40 deletions(-) diff --git a/swiss.rb b/swiss.rb index 0aad9ea..0923018 100644 --- a/swiss.rb +++ b/swiss.rb @@ -1,5 +1,6 @@ require 'pry' DEBUG = true +DISPLAY_CHAR = '~' class Swiss attr_accessor :players attr_accessor :used_pairs @@ -8,11 +9,10 @@ class Swiss attr_accessor :number_of_rounds def initialize(opts = {}) - self.current_round = 1 puts "Number of players?" if DEBUG - self.players = (0..3).to_a.map do |i| + self.players = (0..6).to_a.map do |i| Player.new(name: "p #{i}", id: i) end else @@ -23,8 +23,11 @@ def initialize(opts = {}) Player.new(name: name, id: i) end end + if self.players.count.odd? # add bye player + self.players << Player.new(name: "Bye", id: -1, match_points: -1, game_points: -1) + end self.used_pairs = [] - self.number_of_rounds = opts[:number_of_rounds] || calculate_rounds_required(players.size) + self.number_of_rounds = opts[:number_of_rounds] || Math::log(self.players.count, 2).ceil end def begin! @@ -36,24 +39,27 @@ def begin! exit end - def puts_announcement(text, options = {}) - puts 80.times.map{ options[:character] || "#" }.to_a.join("") # magic number, terminal width - announcement_string = (39 - (text.length / 2)).times.map{ options[:character] || "#" }.to_a.join("") + def puts_announcement(text) + puts DISPLAY_CHAR * 80 # magic number, terminal width + announcement_string = DISPLAY_CHAR * (39 - (text.length / 2)) announcement_string += " #{text} " - announcement_string = announcement_string.ljust(80, '#') + announcement_string = announcement_string.ljust(80, DISPLAY_CHAR) puts announcement_string - puts 80.times.map{ options[:character] || "#" }.to_a.join("") + puts DISPLAY_CHAR * 80 # magic number, terminal width end def announce_pairings - puts_announcement("Pairings:") + binding.pry if current_pairs == [] + puts_announcement("Pairings for round #{self.current_round}:") current_pairs.each do |pair| - if pair.length == 1 + if pair[0].id == -1 + puts "#{pair[1].name} gets a bye" + elsif pair[1].id == -1 puts "#{pair[0].name} gets a bye" else - used_pairs << [pair[0].id, pair[1].id] puts "#{pair[0].name} VS #{pair[1].name}" end + used_pairs << [pair[0].id, pair[1].id] puts "----------------" end end @@ -69,26 +75,13 @@ def pair_has_already_played?(pairing) def announce_scores puts_announcement "Scores after round #{current_round}:" - players.each { |p| puts "#{p.name} :: #{p.match_points}" } + players.each{ |p| puts "#{p.name} :: #{p.match_points}" if p.id != -1 } end def announce_winners puts_announcement "Final Scores:" players.sort_by{ |p| p.match_points }.reverse.each_with_index do |player, index| - puts "#{index + 1}. #{player.name} :: (#{player.match_points})" - end - end - - def calculate_rounds_required(num_players) - case num_players - when 1..8 - 3 - when 9..16 - 4 - when 17..32 - 5 - else - raise 'Too many players!' + puts "#{index + 1}. #{player.name} :: (#{player.match_points})" if player.id != -1 end end @@ -113,8 +106,7 @@ def player_ids end - def find_combos(valid_combinations, prior_path = []) - # right now just do even pairings + def find_optimal_combos(valid_combinations, prior_path = []) path_length_needed = self.players.count / 2 valid_combinations.each do |combo| prior_path << combo @@ -122,9 +114,9 @@ def find_combos(valid_combinations, prior_path = []) return prior_path else remaining_ids = player_ids.reject{ |n| prior_path.flatten.include?(n) } - further_allowed_combos = valid_pairings(remaining_ids) + further_allowed_combos = sort_by_best_match(valid_pairings(remaining_ids)) if further_allowed_combos.length - return find_combos(further_allowed_combos, prior_path) + return find_optimal_combos(further_allowed_combos, prior_path) else next end @@ -134,28 +126,30 @@ def find_combos(valid_combinations, prior_path = []) def sort_by_best_match(combos) combos.sort do |c1, c2| - combo1_p1 = self.players.select{ |p| p.id == c1[0] }[0] - combo1_p2 = self.players.select{ |p| p.id == c1[1] }[0] + combo1_p1 = self.players.find{ |p| p.id == c1[0] } + combo1_p2 = self.players.find{ |p| p.id == c1[1] } combo1_score = 1.0/((combo1_p1.match_points - combo1_p2.match_points) + 0.1* (combo1_p1.game_points - combo1_p2.game_points)) - combo2_p1 = self.players.select{ |p| p.id == c2[0] }[0] - combo2_p2 = self.players.select{ |p| p.id == c2[1] }[0] + combo2_p1 = self.players.find{ |p| p.id == c2[0] } + combo2_p2 = self.players.find{ |p| p.id == c2[1] } combo2_score = 1.0/((combo2_p1.match_points - combo2_p2.match_points) + 0.1* (combo2_p1.game_points - combo2_p2.game_points)) combo2_score <=> combo1_score end end - def tree_pair + def smart_pair self.current_pairs = [] potential_pairings = valid_pairings(player_ids) sorted_potential_pairs = sort_by_best_match(potential_pairings) - one_true_path = find_combos(sorted_potential_pairs) - one_true_path.each do |combo| + optimal_combinations = find_optimal_combos(sorted_potential_pairs) + while optimal_combinations == [] # not sure why/how this happens but it does :( + optimal_combinations = find_optimal_combos(sorted_potential_pairs.shuffle!) + end + optimal_combinations.each do |combo| player1 = self.players.find{ |p| p.id == combo[0] } player2 = self.players.find{ |p| p.id == combo[1] } self.current_pairs << [player1, player2] - binding.pry if current_pairs.length > 4 end end @@ -174,7 +168,7 @@ def regular_round need_swap = false self.current_pairs.each_with_index do |pair, i| if pair_has_already_played?(pair.map(&:id)) - tree_pair() + smart_pair() break end end @@ -186,7 +180,9 @@ def regular_round def retrieve_scores puts_announcement("Scoring for Round #{current_round}:") current_pairs.each do |pair| - if pair.size == 1 #bye + if pair[0].id == -1 + pair[1].award_bye_points + elsif pair[1].id == -1 pair[0].award_bye_points else pair.each { |p| p.award_match_and_game_points } From e00047d45521a70ddf9d0f7d08a6501e6e16e046 Mon Sep 17 00:00:00 2001 From: Andrew Flockhart Date: Fri, 29 Nov 2013 21:58:43 -0500 Subject: [PATCH 5/7] cleanup --- swiss.rb | 115 ++++++++++++++++++++++++------------------------------- 1 file changed, 50 insertions(+), 65 deletions(-) diff --git a/swiss.rb b/swiss.rb index 0923018..0e2ece7 100644 --- a/swiss.rb +++ b/swiss.rb @@ -1,5 +1,3 @@ -require 'pry' -DEBUG = true DISPLAY_CHAR = '~' class Swiss attr_accessor :players @@ -11,20 +9,14 @@ class Swiss def initialize(opts = {}) self.current_round = 1 puts "Number of players?" - if DEBUG - self.players = (0..6).to_a.map do |i| - Player.new(name: "p #{i}", id: i) - end - else - self.players = gets.chomp.to_i.times.map do |i| - puts "Name of player #{i + 1}?" - name = gets.chomp - name = nil if name.empty? - Player.new(name: name, id: i) - end + self.players = gets.chomp.to_i.times.map do |i| + puts "Name of player #{i + 1}?" + name = gets.chomp + name = nil if name.empty? + Player.new(:name => name, :id => i) end if self.players.count.odd? # add bye player - self.players << Player.new(name: "Bye", id: -1, match_points: -1, game_points: -1) + self.players << Player.new(:name => "Bye", :id => -1, :match_points => -1, :game_points => -1) end self.used_pairs = [] self.number_of_rounds = opts[:number_of_rounds] || Math::log(self.players.count, 2).ceil @@ -39,6 +31,10 @@ def begin! exit end + def next_round! + current_round == 1 ? initial_round : regular_round + end + def puts_announcement(text) puts DISPLAY_CHAR * 80 # magic number, terminal width announcement_string = DISPLAY_CHAR * (39 - (text.length / 2)) @@ -49,7 +45,6 @@ def puts_announcement(text) end def announce_pairings - binding.pry if current_pairs == [] puts_announcement("Pairings for round #{self.current_round}:") current_pairs.each do |pair| if pair[0].id == -1 @@ -91,9 +86,6 @@ def initial_round retrieve_scores end - def next_round! - current_round == 1 ? initial_round : regular_round - end def valid_pairings(user_ids) user_ids.combination(2).to_a.reject do |pairing| @@ -106,23 +98,6 @@ def player_ids end - def find_optimal_combos(valid_combinations, prior_path = []) - path_length_needed = self.players.count / 2 - valid_combinations.each do |combo| - prior_path << combo - if prior_path.length == path_length_needed - return prior_path - else - remaining_ids = player_ids.reject{ |n| prior_path.flatten.include?(n) } - further_allowed_combos = sort_by_best_match(valid_pairings(remaining_ids)) - if further_allowed_combos.length - return find_optimal_combos(further_allowed_combos, prior_path) - else - next - end - end - end - end def sort_by_best_match(combos) combos.sort do |c1, c2| @@ -138,6 +113,27 @@ def sort_by_best_match(combos) end end + + def regular_round + # pair players with the same match points against each other randomly + # DCI says you do not use tiebreakers when pairing + sorted_players = players.sort do |p1,p2| + if p1.match_points != p2.match_points + p1.match_points <=> p2.match_points + else + rand(100) <=> rand(100) + end + end.reverse + self.current_pairs = sorted_players.each_slice(2).to_a + + if self.current_pairs.any?{ |pair| pair_has_already_played?(pair.map(&:id)) } + smart_pair() + end + + announce_pairings + retrieve_scores + end + def smart_pair self.current_pairs = [] potential_pairings = valid_pairings(player_ids) @@ -153,28 +149,22 @@ def smart_pair end end - def regular_round - # pair players with the same match points against each other randomly - # DCI says you do not use tiebreakers when pairing - sorted_players = players.sort do |p1,p2| - if p1.match_points != p2.match_points - p1.match_points <=> p2.match_points - else - rand(100) <=> rand(100) - end - end.reverse - self.current_pairs = sorted_players.each_slice(2).to_a + def find_optimal_combos(valid_combinations, prior_path = []) + path_length_needed = self.players.count / 2 + valid_combinations.each do |combo| + prior_path << combo - need_swap = false - self.current_pairs.each_with_index do |pair, i| - if pair_has_already_played?(pair.map(&:id)) - smart_pair() - break + return prior_path if prior_path.length == path_length_needed + + remaining_ids = player_ids.reject{ |n| prior_path.flatten.include?(n) } + further_allowed_combos = sort_by_best_match(valid_pairings(remaining_ids)) + + if further_allowed_combos.length + return find_optimal_combos(further_allowed_combos, prior_path) + else + next end end - - announce_pairings - retrieve_scores end def retrieve_scores @@ -205,8 +195,8 @@ class Player def initialize(opts = {}) self.name = opts[:name] || "Player #{(0...4).map{(65+rand(26)).chr.upcase}.join}" # "Player XASB" self.id = opts[:id] - self.game_points = 0 - self.match_points = 0 + self.game_points = opts[:game_points] || 0 + self.match_points = opts[:match_points] || 0 end def award_bye_points @@ -215,17 +205,12 @@ def award_bye_points end def award_match_and_game_points - if DEBUG - self.match_points += rand(10) % 3 - self.game_points += rand(10) % 6 - else - puts "Match score results for #{name} (3 for win, 1 for draw, 0 for loss):" + puts "Match score results for #{name} (3 for win, 1 for draw, 0 for loss):" - self.match_points += gets.chomp.to_i + self.match_points += gets.chomp.to_i - puts "Game score results for #{name} (3 for EACH win, 1 for EACH draw, 0 for loss):" - self.game_points += gets.chomp.to_i - end + puts "Game score results for #{name} (3 for EACH win, 1 for EACH draw, 0 for loss):" + self.game_points += gets.chomp.to_i end end From 32f75bcc3c04513bd27fff943fd5cb3384e77656 Mon Sep 17 00:00:00 2001 From: Andrew Flockhart Date: Fri, 29 Nov 2013 22:07:39 -0500 Subject: [PATCH 6/7] whitespace --- swiss.rb | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/swiss.rb b/swiss.rb index 0e2ece7..bc554ab 100644 --- a/swiss.rb +++ b/swiss.rb @@ -20,6 +20,7 @@ def initialize(opts = {}) end self.used_pairs = [] self.number_of_rounds = opts[:number_of_rounds] || Math::log(self.players.count, 2).ceil + puts_announcement("There will be #{self.number_of_rounds} rounds.") end def begin! @@ -41,11 +42,11 @@ def puts_announcement(text) announcement_string += " #{text} " announcement_string = announcement_string.ljust(80, DISPLAY_CHAR) puts announcement_string - puts DISPLAY_CHAR * 80 # magic number, terminal width + puts DISPLAY_CHAR * 80 end def announce_pairings - puts_announcement("Pairings for round #{self.current_round}:") + puts_announcement("Pairings for Round #{self.current_round}:") current_pairs.each do |pair| if pair[0].id == -1 puts "#{pair[1].name} gets a bye" @@ -60,12 +61,7 @@ def announce_pairings end def pair_has_already_played?(pairing) - self.used_pairs.each do |used_pair| - if used_pair.sort == pairing.sort - return true - end - end - return false + self.used_pairs.any?{ |used_pair| used_pair.sort == pairing.sort } end def announce_scores @@ -86,7 +82,6 @@ def initial_round retrieve_scores end - def valid_pairings(user_ids) user_ids.combination(2).to_a.reject do |pairing| self.pair_has_already_played?(pairing) @@ -97,8 +92,6 @@ def player_ids self.players.map(&:id) end - - def sort_by_best_match(combos) combos.sort do |c1, c2| combo1_p1 = self.players.find{ |p| p.id == c1[0] } @@ -113,7 +106,6 @@ def sort_by_best_match(combos) end end - def regular_round # pair players with the same match points against each other randomly # DCI says you do not use tiebreakers when pairing @@ -182,7 +174,6 @@ def retrieve_scores self.current_pairs = [] announce_scores end - end class Player From e496a04a13c2e4bebea463e8a5361b8e4959855e Mon Sep 17 00:00:00 2001 From: Andrew Flockhart Date: Fri, 29 Nov 2013 22:09:16 -0500 Subject: [PATCH 7/7] add readme --- readme.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 readme.md diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..60d9708 --- /dev/null +++ b/readme.md @@ -0,0 +1,9 @@ +provided you have a ruby installed, clone this and run + +``` +ruby swiss.rb +``` + +and enjoy + +pull requests welcome