I am building a plugin for a LAN party website that I wrote that would allow the use of a Round Robin tournament.
All is going well, but I have some questions about
Assuming you have a List<Result>
structure where the Result
object has the following parameters...
Pesron - string
Rank - int
Wins - double
TotalScore - int
You could write a custom comparer, and then pass that to List.Sort(Comparison<Result> comparison)
Alternative, you could just make your Result
object implement IComparable<Result>
and stick this in your class.
#region IComparable Members
public int CompareTo(Result obj)
{
if (this.Rank.CompareTo(obj.Rank) != 0)
return this.Rank.CompareTo(obj.Rank);
if (this.Wins.CompareTo(obj.Wins) != 0)
return (this.Wins.CompareTo(obj.Wins);
return (this.TotalScore.CompareTo(obj.TotalScore) ;
}
#endregion
Then you can just call List<Result>.Sort()
;
I realize I'm late to the party, but I wanted to take a shot anyhow.
Here is a version which uses LINQ exclusively:
private IEnumerable<TeamRank> GetRankings(Dictionary<TournamentTeam, double> wins, Dictionary<TournamentTeam, double> scores)
{
var overallRank = 1;
return
from team in wins.Keys
group team by new { Wins = wins[team], TotalScore = scores[team] } into rankGroup
orderby rankGroup.Key.Wins descending, rankGroup.Key.TotalScore descending
let currentRank = overallRank++
from team in rankGroup
select new TeamRank(team, currentRank, rankGroup.Key.Wins, rankGroup.Key.TotalScore);
}
The return type:
public class TeamRank
{
public TeamRank(TournamentTeam team, int rank, double wins, double totalScore)
{
this.Team = team;
this.Rank = rank;
this.Wins = wins;
this.TotalScore = totalScore;
}
public TournamentTeam Team { get; private set; }
public int Rank { get; private set; }
public double Wins { get; private set; }
public double TotalScore { get; private set; }
}
This should work for a non-dense rank:
static class Program
{
static IEnumerable<Result> GetResults(Dictionary<TournamentTeam, double> wins, Dictionary<TournamentTeam, double> scores)
{
int r = 1;
double lastWin = -1;
double lastScore = -1;
int lastRank = 1;
foreach (var rank in from name in wins.Keys
let score = scores[name]
let win = wins[name]
orderby win descending, score descending
select new Result { Name = name, Rank = r++, Score = score, Win = win })
{
if (lastWin == rank.Win && lastScore == rank.Score)
{
rank.Rank = lastRank;
}
lastWin = rank.Win;
lastScore = rank.Score;
lastRank = rank.Rank;
yield return rank;
}
}
}
class Result
{
public TournamentTeam Name;
public int Rank;
public double Score;
public double Win;
}
Ranking isn't too hard. Just mishmash OrderBy and Select implementation patterns together and you can have an easy to use Ranking extension method. Like this:
public static IEnumerable<U> Rank<T, TKey, U>
(
this IEnumerable<T> source,
Func<T, TKey> keySelector,
Func<T, int, U> selector
)
{
if (!source.Any())
{
yield break;
}
int itemCount = 0;
T[] ordered = source.OrderBy(keySelector).ToArray();
TKey previous = keySelector(ordered[0]);
int rank = 1;
foreach (T t in ordered)
{
itemCount += 1;
TKey current = keySelector(t);
if (!current.Equals(previous))
{
rank = itemCount;
}
yield return selector(t, rank);
previous = current;
}
}
Here's some test code
string[] myNames = new string[]
{ "Bob", "Mark", "John", "Jim", "Lisa", "Dave" };
//
var query = myNames.Rank(s => s.Length, (s, r) => new { s, r });
//
foreach (var x in query)
{
Console.WriteLine("{0} {1}", x.r, x.s);
}
Which yields these results:
1 Bob
1 Jim
3 Mark
3 John
3 Lisa
3 Dave
This could be a start:
Dictionary<TournamentTeam, double> wins = new Dictionary<TournamentTeam, double>();
Dictionary<TournamentTeam, double> score = new Dictionary<TournamentTeam, double>();
Dictionary<TournamentTeam, int> ranks = new Dictionary<TournamentTeam, int>();
int r = 1;
ranks = (
from name
in wins.Keys
orderby wins[name] descending, scores[name] descending
select new { Name = name, Rank = r++ })
.ToDictionary(item => item.Name, item => item.Rank);