Skip to content

Commit

Permalink
add hybrid and full bestOf approach
Browse files Browse the repository at this point in the history
  • Loading branch information
cp-sidhdhi-p committed Nov 21, 2024
1 parent a76ce79 commit 48ad1b6
Show file tree
Hide file tree
Showing 2 changed files with 214 additions and 1 deletion.
18 changes: 18 additions & 0 deletions data/lib/api/match/match_model.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// ignore_for_file: non_constant_identifier_names

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:collection/collection.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

import '../../converter/timestamp_json_converter.dart';
Expand Down Expand Up @@ -210,6 +211,23 @@ extension DataMatchModel on MatchModel {
revised_target == null &&
match_type == MatchType.limitedOvers;
}

double getRunRate(String teamId) {
if (!team_ids.contains(teamId)) return 0;

final team = teams.firstWhereOrNull((team) => team.team_id == teamId);
if (team == null) return 0;

final runs = team.run;
final overs = team.over;

if (overs == 0) {
return 0.0;
}

final runRate = runs / overs;
return double.parse(runRate.toStringAsFixed(2));
}
}

enum WinnerByType {
Expand Down
197 changes: 196 additions & 1 deletion khelo/lib/ui/flow/tournament/match_selection/match_scheduler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ class MatchScheduler {
case TournamentType.doubleOut:
return scheduleDoubleOutMatches();
case TournamentType.bestOf:
return scheduleBestOfMatches();
// return scheduleBestOfMatchesLoopingApproach();
return scheduleBestOfMatchesHybridApproach();
case TournamentType.custom:
return scheduledMatches;
}
Expand Down Expand Up @@ -340,6 +341,80 @@ class MatchScheduler {
return additionalScheduledMatches;
}

GroupedMatchMap scheduleBestOfMatchesLoopingApproach() {
final GroupedMatchMap additionalScheduledMatches =
Map.from(scheduledMatches);
final List<TeamModel> teamPool = List.of(teams);

var currentGroup = MatchGroup.round;
var currentRound = 1;

while (teamPool.length > 1) {
final group =
additionalScheduledMatches.putIfAbsent(currentGroup, () => {1: []});
final matches = group[currentRound] ?? [];
final expectedQualifiers = handleSingleBestOfThreePhase(
matches, teamPool, currentGroup, currentRound);
group[currentRound] = matches;

if (expectedQualifiers > 8) {
currentRound++;
} else if (expectedQualifiers > 4) {
currentRound = 1;
currentGroup = MatchGroup.quarterfinal;
} else if (expectedQualifiers > 2) {
currentRound = 1;
currentGroup = MatchGroup.semifinal;
} else if (expectedQualifiers == 2) {
currentRound = 1;
currentGroup = MatchGroup.finals;
}
}

return additionalScheduledMatches;
}

GroupedMatchMap scheduleBestOfMatchesHybridApproach() {
final GroupedMatchMap additionalScheduledMatches =
Map.from(scheduledMatches);
final List<TeamModel> teamPool = List.of(teams);

var currentGroup = MatchGroup.round;
var currentRound = 1;

while (teamPool.length > 1) {
final group =
additionalScheduledMatches.putIfAbsent(currentGroup, () => {1: []});
final matches = group[currentRound] ?? [];
var expectedQualifiers = 0;

if (currentGroup == MatchGroup.round) {
expectedQualifiers = handleSingleBestOfThreePhase(
matches, teamPool, currentGroup, currentRound);
} else {
expectedQualifiers =
handleSingleKnockoutPhase(matches, teamPool, currentGroup, currentRound);
}

group[currentRound] = matches;

if (expectedQualifiers > 8) {
currentRound++;
} else if (expectedQualifiers > 4) {
currentRound = 1;
currentGroup = MatchGroup.quarterfinal;
} else if (expectedQualifiers > 2) {
currentRound = 1;
currentGroup = MatchGroup.semifinal;
} else if (expectedQualifiers == 2) {
currentRound = 1;
currentGroup = MatchGroup.finals;
}
}

return additionalScheduledMatches;
}

List<List<TeamModel>> createTeamPairsForBestOfThree(List<TeamModel> teams) {
final List<List<TeamModel>> pairs = [];
for (int i = 0; i < teams.length; i++) {
Expand Down Expand Up @@ -714,4 +789,124 @@ class MatchScheduler {
}
return teamToReturn;
}

int handleSingleKnockoutPhase(List<MatchModel> matches, List<TeamModel> teamPool,
MatchGroup group, int number) {
removeAlreadyScheduledTeams(matches, teamPool);

final teamPairs = createKnockoutTeamPairs(teamPool);
addMatches(matches, teamPairs, group, number);

teamPool.removeWhere((team) => teamPairs
.any((element) => element.length == 2 && element.contains(team)));

addWinnerTeamsBackToTeam(matches, teamPool);

return matches.length;
}

int handleSingleBestOfThreePhase(
List<MatchModel> matches, List<TeamModel> teamPool, group, number) {
final existingPairs = getExistingTeamPairs(matches);
final remainedTeams = teamPool
.where((team) => !existingPairs.expand((pair) => pair).contains(team))
.toList();

final newPairs = createKnockoutTeamPairs(remainedTeams);
final allPairs = [...existingPairs, ...newPairs];

final winners =
handleBestOfThreeMatchesAndItsWinner(matches, allPairs, group, number);

teamPool.removeWhere((team) => allPairs.any((pair) =>
pair.length == 2 && pair.contains(team) && !winners.contains(team.id)));
return allPairs.length;
}

List<List<TeamModel>> getExistingTeamPairs(List<MatchModel> matches) {
final List<List<TeamModel>> pairs = [];
for (var match in matches) {
final pair = match.teams.map((e) => e.team).toList();
if (!pairs.map((e) => e.map((e) => e.id)).any((element) =>
element.contains(pair.first.id) && element.contains(pair.last.id))) {
pairs.add(pair);
}
}

return pairs;
}

List<String> handleBestOfThreeMatchesAndItsWinner(
List<MatchModel> groupMatches,
List<List<TeamModel>> pair,
MatchGroup group,
int number,
) {
final List<String> winnerTeams = [];
for (var p in pair) {
final matchesForPair = groupMatches
.where(
(element) =>
element.team_ids.contains(p.first.id) &&
element.team_ids.contains(p.last.id),
)
.toList();
if (matchesForPair.isEmpty) {
addMatches(groupMatches, [p], group, number);
addMatches(groupMatches, [p], group, number);
} else if (matchesForPair.length == 1) {
addMatches(groupMatches, [p], group, number);
} else {
final finishedMatches =
matchesForPair.where((element) => element.matchResult != null);
if (finishedMatches.length >= 2) {
final winner = getWinnerTeam(
matchesForPair.toList(),
p.first.id,
p.last.id,
);
if (winner == null && matchesForPair.length == 2) {
addMatches(groupMatches, [p], group, number);
} else if (winner != null) {
winnerTeams.add(winner);
}
}
}
}
return winnerTeams;
}

String? getWinnerTeam(
List<MatchModel> matches,
String teamOne,
String teamTwo,
) {
Map<String, int> winner = {teamOne: 0, teamTwo: 0};
for (var element in matches) {
final result = element.matchResult;
if (result == null) continue;

if (result.winType != WinnerByType.tie &&
winner.containsKey(result.teamId)) {
winner[result.teamId] = winner[result.teamId]! + 1;
} else if (result.winType == WinnerByType.tie) {
final runRateOne = element.getRunRate(teamOne);
final runRateTwo = element.getRunRate(teamTwo);
final win = (runRateOne > runRateTwo ? teamOne : teamTwo);
if (winner.containsKey(win)) {
winner[win] = winner[win]! + 1;
}
}
}
final maxWin = winner.values.max;

final maxWinners =
winner.entries.where((element) => element.value == maxWin);

if (maxWinners.length == 1) {
return maxWinners.first.key;
} else {
return null;
}
}
}

0 comments on commit 48ad1b6

Please sign in to comment.