#!/usr/bin/perl # The first argument ($ARGV[0]) is the name of the player ratings file. # Format is one player record per line: username rating maxGames # If any second argument is provided then additional info is # displayed. ####################################### # Read in the input and create an array of records open(FH, "<$ARGV[0]"); @f = ; close FH; foreach $f (@f){ chomp $f; my($p); ($u, $r, $m) = split(/ +/, $f); if ($m eq ''){ $m = 2; } if ($r eq ''){ next; } $p->{u} = $u; $p->{r} = $r; $p->{m} = $m; push(@r, $p); } ####################################### # Sort the records by rating from highest rated to lowest # 9 8 7 6 5 4 3 2 1 @f = sort {$b->{r} <=> $a->{r}} @r; ####################################### # Now re-order so there is no sharp difference in rating between # the first and last as well as any other adjacent elements # basically a smooth ring # 9 7 5 3 1 2 4 6 8 $d = 1; $i = 0; $k = 0; foreach $f (@f){ $r[$i] = $f; if ($d == 1){ $d = -1; $i = $#f - $k; } else{ $d = 1; $k += 1; $i = $k; } } ####################################### # Show the ordering of the players if second arg given if ($ARGV[1]){ print "\n"; foreach $p (@r){ print "$p->{u} $p->{r} $p->{m}\n"; } } ####################################### # Pick games between unique pairs of players # Go through each player in the array and if the player still # needs more games try to find an opponent that is as close as # possible and also needs more games and has not already been # paired against. # Reverse the array repeat the attempt to find pairs. # If no pairs were found for any players then stop. @r = reverse(@r); $anyMatch = 1; while($anyMatch){ @r = reverse(@r); $anyMatch = 0; $i = -1; $g += 2; foreach $p (@r){ $i += 1; if (($p->{g} >= $g) || ($p->{g} >= $p->{m})){ next; } # for($j=$i+1; $j<=$#r; $j++){ for($j=$i+1; $j!=$i; $j=($j>=$#r)?0:($j+1)){ my($gr); $o = $r[$j]; if (($o->{g} >= $g) || ($o->{g} >= $o->{m})){ next; } if ($p->{u} lt $o->{u}){ $p1 = $p; $p2 = $o; } else{ $p2 = $p; $p1 = $o; } if ($m{"$p1->{u} $p2->{u}"}){ next; } $m{"$p1->{u} $p2->{u}"} = 1; if ($goldr{$p1->{u}} > $goldr{$p2->{u}}){ #print "$p2->{u} $p1->{u}\n"; $gr->{p2} = $p1->{u}; $gr->{r2} = $p1->{r}; $gr->{p1} = $p2->{u}; $gr->{r1} = $p2->{r}; push(@ga, $gr); $gold{$p2->{u}} += 1; $g{"$p1->{u} $p2->{u}"} = 2; } else{ #print "$p1->{u} $p2->{u}\n"; $gr->{p1} = $p1->{u}; $gr->{r1} = $p1->{r}; $gr->{p2} = $p2->{u}; $gr->{r2} = $p2->{r}; push(@ga, $gr); $gold{$p1->{u}} += 1; $g{"$p1->{u} $p2->{u}"} = 1; } $games{$p1->{u}} += 1; $games{$p2->{u}} += 1; $goldr{$p1->{u}} = $gold{$p1->{u}}/$games{$p1->{u}}; $goldr{$p2->{u}} = $gold{$p2->{u}}/$games{$p2->{u}}; $p->{g} += 1; $o->{g} += 1; $anyMatch = 1; last; } } } ####################################### # Make another pass to find a second games between players # who still want more games. $anyMatch = 1; $g -= 2; while($anyMatch){ @r = reverse(@r); $anyMatch = 0; $i = -1; $g += 2; foreach $p (@r){ $i += 1; if (($p->{g} >= $g) || ($p->{g} >= $p->{m})){ next; } # for($j=$i+1; $j<=$#r; $j++){ for($j=$i+1; $j!=$i; $j=($j>=$#r)?0:($j+1)){ my($gr); $o = $r[$j]; if (($o->{g} >= $g) || ($o->{g} >= $o->{m})){ next; } if ($p->{u} lt $o->{u}){ $p1 = $p; $p2 = $o; } else{ $p2 = $p; $p1 = $o; } if ($sm{"$p1->{u} $p2->{u}"}){ next; } $m{"$p1->{u} $p2->{u}"} = 1; $sm{"$p1->{u} $p2->{u}"} = 1; if ($g{"$p1->{u} $p2->{u}"} == 1){ #print "$p2->{u} $p1->{u}\n"; $gr->{p2} = $p1->{u}; $gr->{r2} = $p1->{r}; $gr->{p1} = $p2->{u}; $gr->{r1} = $p2->{r}; push(@ga, $gr); } else{ #print "$p1->{u} $p2->{u}\n"; $gr->{p1} = $p1->{u}; $gr->{r1} = $p1->{r}; $gr->{p2} = $p2->{u}; $gr->{r2} = $p2->{r}; push(@ga, $gr); } $p->{g} += 1; $o->{g} += 1; $anyMatch = 1; last; } } } ####################################### # if a second arg was given the show how many games the # players wanted and how many games they got. if ($ARGV[1]){ print "\n"; print "player rating games_wanted games_paired\n"; foreach $p (@r){ print "$p->{u} $p->{r} $p->{m} $p->{g}\n"; } } ####################################### # print out the pairing for all the games we have found # also compute a score for this pairing print "\n"; foreach $g (@ga){ print "[$g->{p1} $g->{r1} vs $g->{p2} $g->{r2}]\n"; $sc += abs($g->{r1} - $g->{r2}); $ng += 1; } $sca = $sc/$ng; if ($ARGV[1]){ print "\n"; print "numGames: $ng matchDiff: $sca\n"; } exit;