public static void AddFinalsRaces(this Tournament tournament)
        {
            if (!tournament.Races.All(r => r.IsFinished()) || tournament.Groups.Any(g => g.Name == "Finals"))
            {
                return;
            }
            var finalsCars = new List<Car>();
            var scoutGroup = tournament.Groups.First(g => g.Name == "Scouts");
            var resultsByClass = ResultsByClass(tournament, scoutGroup);
            foreach (var classGroup in resultsByClass)
            {
                var firstCar = GetFirstCar(tournament, classGroup);
                if (firstCar == null)
                {
                    return;
                }
                finalsCars.Add(firstCar.Copy());
            }
            for (int i = 0; i < finalsCars.Count; i++)
            {
                finalsCars[i].Number = i + 1;
            }

            var tournamentBuilder = new TournamentBuilder("finals", "");
            var laneStats = tournament.LaneStats;
            var finalsGroup = new Group {Name = "Finals", Round = "Finals", Cars = finalsCars.ToArray()};
            var raceDef = RaceDefinitionSource.RaceDefinitions(finalsCars.Count, 1, laneStats);
            tournamentBuilder.AddGroup(finalsGroup, raceDef);
            var finalsTournament = tournamentBuilder.Build("Finals");
            tournament.AddGroupsAndRaces(finalsTournament.Groups, finalsTournament.Races);
        }
        public static void AddTiebreakerRaces(this Tournament tournament)
        {
            int byeCounter = 0;

            if (!tournament.Races.All(r => r.IsFinished()))
            {
                return;
            }
            var tiebreakerRounds = tournament.Groups.Select(g => g.Round).Where(r => r != null && r.Contains("Tie"));
            var tiebreakerRound  = 0;

            foreach (var round in tiebreakerRounds)
            {
                var thisRound = round.Split('-').Last().ToInt();
                tiebreakerRound = Math.Max(thisRound, tiebreakerRound);
            }
            var roundName = "Tie - " + (tiebreakerRound + 1);
            var laneStats = tournament.LaneStats;

            foreach (var g in tournament.Groups)
            {
                var resultsByClass = ResultsByClass(tournament, g);

                foreach (var classGroup in resultsByClass)
                {
                    var take          = g.Round == "prelim" ? 3 : 1;
                    var maxPointList  = classGroup.OrderByDescending(r => r.Points).Take(take).Select(r => r.Points).ToArray();
                    var lastMaxPoints = 0;
                    for (var i = 0; i < maxPointList.Length; i++)
                    {
                        if (lastMaxPoints == maxPointList[i])
                        {
                            continue;
                        }
                        var tiebreakerTournamentBuilder = new TournamentBuilder(tournament.Name + " - tiebreakers", "");
                        var maxPoints       = lastMaxPoints = maxPointList[i];
                        var tieBreakerPlace = CalculateTiebreakerPlace(g.Name, i + 1);
                        byeCounter = BuildTiebreakerRacesForPointLevel(tournament, classGroup.Key, classGroup, maxPoints, g, laneStats,
                                                                       byeCounter, roundName, tiebreakerTournamentBuilder, tieBreakerPlace);
                        var tiebreakerTournament = tiebreakerTournamentBuilder.Build(roundName);
                        foreach (var race in tiebreakerTournament.Races)
                        {
                            foreach (var car in race.Cars())
                            {
                                if (car.IsBye())
                                {
                                    car.Place  = 4;
                                    car.Points = 1;
                                }
                            }
                        }

                        tournament.AddGroupsAndRaces(tiebreakerTournament.Groups, tiebreakerTournament.Races);
                    }
                }
            }
        }
        public static void AddFinalsRaces(this Tournament tournament)
        {
            if (!tournament.Races.All(r => r.IsFinished()) || tournament.Groups.Any(g => g.Name == "Finals"))
            {
                return;
            }
            var finalsCars     = new List <Car>();
            var scoutGroup     = tournament.Groups.First(g => g.Name == "Scouts");
            var resultsByClass = ResultsByClass(tournament, scoutGroup);

            foreach (var classGroup in resultsByClass)
            {
                var firstCar = GetFirstCar(tournament, classGroup);
                if (firstCar == null)
                {
                    return;
                }
                finalsCars.Add(firstCar.Copy());
            }
            for (int i = 0; i < finalsCars.Count; i++)
            {
                finalsCars[i].Number = i + 1;
            }

            var tournamentBuilder = new TournamentBuilder("finals", "");
            var laneStats         = tournament.LaneStats;
            var finalsGroup       = new Group {
                Name = "Finals", Round = "Finals", Cars = finalsCars.ToArray()
            };
            var raceDef = RaceDefinitionSource.RaceDefinitions(finalsCars.Count, 1, laneStats);

            tournamentBuilder.AddGroup(finalsGroup, raceDef);
            var finalsTournament = tournamentBuilder.Build("Finals");

            tournament.AddGroupsAndRaces(finalsTournament.Groups, finalsTournament.Races);
        }
 private static int BuildTiebreakerRacesForPointLevel(Tournament tournament, string classKey, IEnumerable<CarResult> classGroup, int maxPoints, Group g,
     LaneStat[] laneStats, int byeCounter, string roundName, TournamentBuilder tiebreakerTournamentBuilder, int tiebreakerPlace)
 {
     var carsWithMaxPoints = classGroup.Where(r => r.Points == maxPoints).ToArray();
     var baseGroup = g.ShowClassStandings ? classKey : g.Name;
     if (carsWithMaxPoints.Count() > 1 && !tournament.Groups.Any(_g => _g.TiebreakGroup == baseGroup))
     {
         int i = 1;
         var tiedCars =
             carsWithMaxPoints.Select(
                 c =>
                     new Car
                     {
                         Builder = c.Car.Builder,
                         Class = c.Car.Class,
                         ID = c.Car.ID,
                         Name = c.Car.Name,
                         Number = i++
                     }).ToList();
         var raceDef = RaceDefinitionSource.RaceDefinitions(tiedCars.Count(), 1, laneStats);
         while (tiedCars.Count() < 4)
         {
             byeCounter++;
             var carId = "BYE" + byeCounter;
             tiedCars.Add(new Car {Builder = carId, Name = carId, ID = carId, Number = i});
             i++;
         }
         var name = tiebreakerPlace == 1
             ? string.Join("-", baseGroup.Split('-').First(), roundName)
             : string.Join("-", baseGroup.Split('-').First()+":"+tiebreakerPlace, roundName);
         var tiedGroup = new Group {Name = name, Cars = tiedCars.ToArray(), Round = roundName, TiebreakGroup = baseGroup};
         tiebreakerTournamentBuilder.AddGroup(tiedGroup, raceDef);
     }
     return byeCounter;
 }
        public static void AddTiebreakerRaces(this Tournament tournament)
        {
            int byeCounter = 0;
            if (!tournament.Races.All(r => r.IsFinished()))
            {
                return;
            }
            var tiebreakerRounds = tournament.Groups.Select(g => g.Round).Where(r => r != null && r.Contains("Tiebreaker"));
            var tiebreakerRound = 0;
            foreach (var round in tiebreakerRounds)
            {
                var thisRound = round.Split('-').Last().ToInt();
                tiebreakerRound = Math.Max(thisRound, tiebreakerRound);
            }
            var roundName = "Tiebreaker-" + (tiebreakerRound + 1);
            var tiebreakerTournamentBuilder = new TournamentBuilder(tournament.Name + " - tiebreakers", "");
            var laneStats = tournament.LaneStats;
            foreach (var g in tournament.Groups)
            {
                var resultsByClass = ResultsByClass(tournament, g);

                foreach (var classGroup in resultsByClass)
                {
                    var take = g.Round == "prelim" ? 3 : 1;
                    var maxPointList = classGroup.OrderByDescending(r => r.Points).Take(take).Select(r => r.Points).Distinct().ToArray();
                    for (var i = 0; i < maxPointList.Length; i++)
                    {
                        var maxPoints = maxPointList[i];
                        byeCounter = BuildTiebreakerRacesForPointLevel(tournament, classGroup.Key, classGroup, maxPoints, g, laneStats,
                            byeCounter, roundName, tiebreakerTournamentBuilder, i + 1);
                    }
                }
            }
            var tiebreakerTournament = tiebreakerTournamentBuilder.Build(roundName);
            tournament.AddGroupsAndRaces(tiebreakerTournament.Groups, tiebreakerTournament.Races);
        }
        private static int BuildTiebreakerRacesForPointLevel(Tournament tournament, string classKey, IEnumerable <CarResult> classGroup, int maxPoints, Group g,
                                                             LaneStat[] laneStats, int byeCounter, string roundName, TournamentBuilder tiebreakerTournamentBuilder, int tiebreakerPlace)
        {
            var carsWithMaxPoints = classGroup.Where(r => r.Points == maxPoints).ToArray();
            var baseGroup         = g.ShowClassStandings ? classKey : g.Name;

            if (carsWithMaxPoints.Count() > 1 && !tournament.Groups.Any(_g => _g.TiebreakGroup == baseGroup && _g.TiebreakerPlace() == tiebreakerPlace))
            {
                int i        = 1;
                var tiedCars =
                    carsWithMaxPoints.Select(
                        c =>
                        new Car
                {
                    Builder = c.Car.Builder,
                    Class   = c.Car.Class,
                    ID      = c.Car.ID,
                    Name    = c.Car.Name,
                    Number  = i++
                }).ToList();
                var raceDef = RaceDefinitionSource.RaceDefinitions(tiedCars.Count(), 1, laneStats);
                while (tiedCars.Count() < 4)
                {
                    byeCounter++;
                    var carId = "BYE" + byeCounter;
                    tiedCars.Add(new Car {
                        Builder = carId, Name = carId, ID = carId, Number = i
                    });
                    i++;
                }
                var ordinal   = tiebreakerPlace == 1 ? "1st" : tiebreakerPlace == 2 ? "2nd" : "3rd";
                var name      = string.Join(" - ", baseGroup.Split('-').First().Trim(), ordinal, roundName);
                var tiedGroup = new Group {
                    Name = name, Cars = tiedCars.ToArray(), Round = roundName, TiebreakGroup = baseGroup
                };
                tiebreakerTournamentBuilder.AddGroup(tiedGroup, raceDef);
            }
            return(byeCounter);
        }