using System.Collections.Generic;
public static void Main()
Console.WriteLine("Welcome to Coin Toss Tournament!");
Console.WriteLine("64 Teams, 16 groups of 4, 4 rounds to find a winner");
Console.WriteLine("... Purpose is to highlight how different Swiss techniques result in players of similar skill playing each other");
Console.WriteLine(" We use the Team number in this case to represent the 'Skill' level. Lower number means more skill #1 is the highest skill.");
Console.WriteLine(" In this first case we are using a pre-computed matrix, at the end the lowest team numbers (highest skill) should be in the highest skill groups (lowest group numbers)");
int [] teams = Enumerable.Range(1,64).ToArray();
teams = SeedTeams(teams);
Console.WriteLine("Initial Seeding:");
Console.WriteLine(String.Join(",", teams));
List<Grouping> round1 = DrawRoundOne(teams);
List<Grouping> round2 = DrawNextRoundUsingMatrix(round1);
List<Grouping> round3 = DrawNextRoundUsingMatrix(round2);
List<Grouping> round4 = DrawNextRoundUsingMatrix(round3);
var overall = CollateAllResults(round1, round2, round3, round4);
PrintRoundByRound(overall);
Console.WriteLine("Now try brute force or dynamic SWISS draw");
Console.WriteLine("Same Seeding and Win Rules");
List<Grouping> round1D = DrawRoundOne(teams);
List<Grouping> round2D = DrawNextRoundDynamic(CollateAllResults(round1D));
List<Grouping> round3D = DrawNextRoundDynamic(CollateAllResults(round1D, round2D));
List<Grouping> round4D = DrawNextRoundDynamic(CollateAllResults(round1D, round2D, round3D));
var overallD = CollateAllResults(round1D, round2D, round3D, round4D);
PrintRoundByRound(overallD);
PrintComparison(CollateAllResults(round1, round2, round3, round4), CollateAllResults(round1D, round2D, round3D, round4D));
private static int[] SeedTeams(int[] teams)
return teams.OrderBy(x => r.NextDouble()).ToArray();
private static List<Grouping> DrawRoundOne(int[] teams)
List<Grouping> groups = new ();
for(int g = 0 ; g < 16 ; g++)
var groupTeams = teams.AsEnumerable().Skip(g*4).Take(4).ToArray();
grp.Matches = grp.BuildRoundRobin(groupTeams);
private static List<Grouping> DrawNextRoundUsingMatrix(List<Grouping> previousRound)
List<Grouping> groups = new ();
int roundNo = previousRound.First().Round + 1;
var results = previousRound.OrderBy(x => x.Group).Select(x => x.GetRanking()).ToArray();
for(int g = 0 ; g < 16 ; g++)
int to = ((g+1) * 4) % 16;
for(int x = from; x < to; x++)
teams.Add(results[x][rank-1]);
grp.Matches = grp.BuildRoundRobin(teams.ToArray());
private static List<Grouping> DrawNextRoundDynamic(Draw[] results)
List<Grouping> groups = new ();
int roundNo = results.Max(x => x.Round) + 1;
var rankedTeams = results.GroupBy(x => x.Team).Select(x => new { Team = x.Key, Score = x.Sum(y => (int)y.Result), Opps = x.Select(m => m.Opponent).ToList()}).OrderByDescending(x => x.Score).ToList();
var holding = rankedTeams.Take(0).ToList();
Console.WriteLine(String.Join(",", rankedTeams.Select(x => x.Team)));
var queue = CreateQueue(rankedTeams);
for(int g = 0 ; g < 16 ; g++)
int[] teams = new int [4];
var top4 = rankedTeams.Take(0).ToList();
while (top4.Count < 4 && queue.Any())
var team = queue.Dequeue();
if (team == opp) continue;
if (team.Opps.Contains(team.Team))
Console.WriteLine($"Team {team.Team} already played {opp.Team}");
holding.AddRange(queue.ToList());
queue = CreateQueue(holding);
while (top4.Count < 4 && queue.Any())
top4.Add(queue.Dequeue());
grp.Matches = grp.BuildRoundRobin(top4.Select(x => x.Team).ToArray());
public static Stack<T> CreateStack<T>(IEnumerable<T> items)
var s = new Stack<T>(items);
public static Queue<T> CreateQueue<T>(IEnumerable<T> items)
var q = new Queue<T>(items);
private static void RecordResults(List<Grouping> round)
foreach(var m in g.Matches)
var skillDelta = m.Team2.Team - m.Team1.Team;
if (Math.Abs(skillDelta) < 5)
m.Team1.Result = m.Result;
m.Team2.Result = (Result)(4 - (int)m.Result);
private static void PrintResults(List<Grouping> round)
Console.WriteLine($"Round {round.First().Round}");
for(var g = 0 ; g < 15 ; g += 4)
var g1Result = round[g].FormatResult().Split(Environment.NewLine);
var g2Result = round[g+1].FormatResult().Split(Environment.NewLine);
var g3Result = round[g+2].FormatResult().Split(Environment.NewLine);
var g4Result = round[g+3].FormatResult().Split(Environment.NewLine);
for(int l = 0 ; l < g1Result.Count() ; l ++)
Console.WriteLine(String.Join(" ", new string [] { g1Result[l].PadRight(50), g2Result[l].PadRight(50), g3Result[l].PadRight(50), g4Result[l].PadRight(50) }));
private static Draw[] CollateAllResults(params List<Grouping>[] rounds)
return rounds.SelectMany(round => round).SelectMany(x => x.Matches.Select(x => x.Team1))
.Concat(rounds.SelectMany(round => round).SelectMany(x => x.Matches.Select(x => x.Team2)))
private static void PrintRoundByRound(Draw[] results)
var round1 = results.Where(x => x.Round == 1).GroupBy(x => x.Team).Select(x => new { Team = x.Key, Score = x.Sum(y => (int)y.Result), Group = x.OrderBy(y=> y.Round).Last().Group, Round = x.Max(y=> y.Round)}).OrderByDescending(x => x.Score).ToArray();
var round2 = results.Where(x => x.Round <= 2).GroupBy(x => x.Team).Select(x => new { Team = x.Key, Score = x.Sum(y => (int)y.Result), Group = x.OrderBy(y=> y.Round).Last().Group, Round = x.Max(y=> y.Round)}).OrderByDescending(x => x.Score).ToArray();
var round3 = results.Where(x => x.Round <= 3).GroupBy(x => x.Team).Select(x => new { Team = x.Key, Score = x.Sum(y => (int)y.Result), Group = x.OrderBy(y=> y.Round).Last().Group, Round = x.Max(y=> y.Round)}).OrderByDescending(x => x.Score).ToArray();
var round4 = results.Where(x => x.Round <= 4).GroupBy(x => x.Team).Select(x => new { Team = x.Key, Score = x.Sum(y => (int)y.Result), Group = x.OrderBy(y=> y.Round).Last().Group, Round = x.Max(y=> y.Round)}).OrderByDescending(x => x.Score).ToArray();
Console.WriteLine("Key: Team (score) G[group]");
Console.WriteLine("Ranked Results:");
Console.WriteLine("RANK Round 1 Round 2 Round 3 Round 4");
Console.WriteLine(" ------------- ------------- ------------- -------------");
for(int l = 0 ; l < round1.Length ; l ++)
Console.WriteLine($"#{(l+1).ToString().PadRight(2)} {round1[l].Team.ToString().PadLeft(2)} ({round1[l].Score.ToString().PadLeft(2)}) G[{round1[l].Group.ToString().PadLeft(2)}] {round2[l].Team.ToString().PadLeft(2)} ({round2[l].Score.ToString().PadLeft(2)}) G[{round2[l].Group.ToString().PadLeft(2)}] {round3[l].Team.ToString().PadLeft(2)} ({round3[l].Score.ToString().PadLeft(2)}) G[{round3[l].Group.ToString().PadLeft(2)}] {round4[l].Team.ToString().PadLeft(2)} ({round4[l].Score.ToString().PadLeft(2)}) G[{round4[l].Group.ToString().PadLeft(2)}] ");
var teamsRound1 = round1.Select((result, index) => new { result.Round, result.Group, result.Team, result.Score, Rank = index}).OrderBy(x => x.Team).ToArray();
var teamsRound2 = round2.Select((result, index) => new { result.Round, result.Group, result.Team, result.Score, Rank = index}).OrderBy(x => x.Team).ToArray();
var teamsRound3 = round3.Select((result, index) => new { result.Round, result.Group, result.Team, result.Score, Rank = index}).OrderBy(x => x.Team).ToArray();
var teamsRound4 = round4.Select((result, index) => new { result.Round, result.Group, result.Team, result.Score, Rank = index}).OrderBy(x => x.Team).ToArray();
Console.WriteLine("By Teams:");
Console.WriteLine("TEAM Round 1 Round 2 Round 3 Round 4");
Console.WriteLine(" ------------- ------------- ------------- -------------");
for(int l = 0 ; l < round1.Length ; l ++)
Console.WriteLine($"#{(l+1).ToString().PadRight(2)} {teamsRound1[l].Team.ToString().PadLeft(2)} ({teamsRound1[l].Score.ToString().PadLeft(2)}) G[{teamsRound1[l].Group.ToString().PadLeft(2)}] {teamsRound2[l].Team.ToString().PadLeft(2)} ({teamsRound2[l].Score.ToString().PadLeft(2)}) G[{teamsRound2[l].Group.ToString().PadLeft(2)}] {teamsRound3[l].Team.ToString().PadLeft(2)} ({teamsRound3[l].Score.ToString().PadLeft(2)}) G[{teamsRound3[l].Group.ToString().PadLeft(2)}] {teamsRound4[l].Team.ToString().PadLeft(2)} ({teamsRound4[l].Score.ToString().PadLeft(2)}) G[{teamsRound4[l].Group.ToString().PadLeft(2)}] ");
Console.WriteLine("Matchups:");
Console.WriteLine("TEAM Round 1 Round 2 Round 3 Round 4");
Console.WriteLine(" ------------- ------------- ------------- -------------");
for(int l = 0 ; l < round1.Length ; l ++)
Console.Write($"#{(l+1).ToString().PadRight(2)} ");
Console.Write($"{(String.Join(",", results.Where(x => x.Round == 1 && x.Team == l+1).Select(x => $"{(x.Opponent).ToString().PadLeft(2)}{x.Result.ToString().ToCharArray()[0]}")))} ");
Console.Write($"{(String.Join(",", results.Where(x => x.Round == 2 && x.Team == l+1).Select(x => $"{(x.Opponent).ToString().PadLeft(2)}{x.Result.ToString().ToCharArray()[0]}")))} ");
Console.Write($"{(String.Join(",", results.Where(x => x.Round == 3 && x.Team == l+1).Select(x => $"{(x.Opponent).ToString().PadLeft(2)}{x.Result.ToString().ToCharArray()[0]}")))} ");
Console.Write($"{(String.Join(",", results.Where(x => x.Round == 4 && x.Team == l+1).Select(x => $"{(x.Opponent).ToString().PadLeft(2)}{x.Result.ToString().ToCharArray()[0]}")))} ");
private static void PrintComparison(Draw[] results, Draw[] resultsD)
var round4 = results.Where(x => x.Round <= 4).GroupBy(x => x.Team).Select(x => new { Team = x.Key, Score = x.Sum(y => (int)y.Result), Group = x.OrderBy(y=> y.Round).Last().Group, Round = x.Max(y=> y.Round)}).OrderByDescending(x => x.Score).ToArray();
var round4D = resultsD.Where(x => x.Round <= 4).GroupBy(x => x.Team).Select(x => new { Team = x.Key, Score = x.Sum(y => (int)y.Result), Group = x.OrderBy(y=> y.Round).Last().Group, Round = x.Max(y=> y.Round)}).OrderByDescending(x => x.Score).ToArray();
Console.WriteLine("Compare Ranked Results:");
Console.WriteLine("RANK Round 4 Matrix Round 4 Dynamic");
Console.WriteLine(" ------------- ------------- ");
for(int l = 0 ; l < round4.Length ; l ++)
Console.Write($"#{(l+1).ToString().PadRight(2)} ");
Console.Write($"{round4[l].Team.ToString().PadLeft(2)} ({round4[l].Score.ToString().PadLeft(2)}) G[{round4[l].Group.ToString().PadLeft(2)}] ");
Console.Write($"{round4D[l].Team.ToString().PadLeft(2)} ({round4D[l].Score.ToString().PadLeft(2)}) G[{round4D[l].Group.ToString().PadLeft(2)}] ");
public int Round { get; set; }
public int Group { get; set; }
public Matching[] Matches { get;set; }
public string FormatResult()
StringBuilder output = new();
output.AppendLine($"Group {Group}: {String.Join(", ", GetRanking())}");
output.AppendLine("--------------------------------------------------");
output.AppendLine($" {Matches[0].FormatResult()} {Matches[2].FormatResult()} {Matches[4].FormatResult()} ");
output.AppendLine($" {Matches[1].FormatResult()} {Matches[3].FormatResult()} {Matches[5].FormatResult()} ");
return output.ToString();
public int[] GetRanking()
return Matches.Select(x => x.Team1).Concat(Matches.Select(x => x.Team2)).GroupBy(x => x.Team).OrderByDescending(x => x.Sum(y => (int)y.Result)).Select(x => x.Key).ToArray();
public Matching[] BuildRoundRobin(int[] teams)
if (teams.Length != 4) throw new NotSupportedException("Hardcoded for 4 teams in a group only!");
new Matching(Round, Group, 1, 1, teams[0], teams[1]),
new Matching(Round, Group, 2, 1, teams[2], teams[3]),
new Matching(Round, Group, 3, 2, teams[0], teams[3]),
new Matching(Round, Group, 4, 2, teams[2], teams[1]),
new Matching(Round, Group, 5, 3, teams[0], teams[2]),
new Matching(Round, Group, 6, 3, teams[1], teams[3])
public Matching(int round, int group, int match, int draw, int team1, int team2)
public int Round { get;set; }
public int Group { get;set; }
public int Match { get;set; }
public int Draw { get;set; }
public Result Result { get;set;}
public Draw Team1 { get;set; }
public Draw Team2 { get;set; }
public string FormatResult()
return $"{Team1.Team.ToString().PadLeft(2)}({Team1.Result.ToString().First()}) v {Team2.Team.ToString().PadLeft(2)}({Team2.Result.ToString().First()})";
public int Round { get;set; }
public int Group { get;set; }
public int Team { get;set; }
public int Opponent { get;set; }
public Result Result { get;set;}