I'm about to create an algorithm for football match scheduling. Main scheduling rules (as far as I know) are:
- There is an even number of teams
- One season consists of 2 rounds
- In one match day every team plays exactly one match
- Home teams in previous matchday should be an away team in next matchday (if it's possible - I looked at Serie A fixture, and there's an exlusion for this rule - every team plays 2 times in a row as (home/guests - first round/ revenge round) in one season
- There are 2 rounds and the rule is as follows: first match in first round (matchday 1) team A plays with team B (A:B), so the revenge match B:A should take place in - matchday
allMatchDays/2 + i
, wherei = 1
Current approach:
private static Map<Integer, List<Match>> createSchedule(/*Set<Team> allTeams*/) {
Set <Team> allTeams = Set.of(
Team.builder().name("A").build(),
Team.builder().name("B").build(),
Team.builder().name("C").build(),
Team.builder().name("D").build(),
Team.builder().name("E").build(),
Team.builder().name("F").build(),
Team.builder().name("G").build(),
Team.builder().name("H").build(),
Team.builder().name("I").build(),
Team.builder().name("J").build()
);
int teamsNumber = allTeams.size();
int matchDays = (teamsNumber - 1) * 2;
int gamesNumberPerMatchDay = teamsNumber / 2;
List<Match> allMatches = allTeams.stream()
.map(team ->
allTeams.stream().filter(otherTeam -> !otherTeam.equals(team))
.map(otherTeam -> Match.builder().homeTeam(team)
.awayTeam(otherTeam).build()).collect(Collectors.toList())
)
.flatMap(Collection::stream)
.collect(Collectors.toList());
Map<Integer, List<Match>> schedule = IntStream.rangeClosed(1, matchDays)
.boxed()
.collect(Collectors.toMap(
matchDay -> (Integer) matchDay,
matchDay -> new ArrayList<>()));
Set<Team> homeTeamsInCurrentMatchDay = new HashSet<>();
List<Team> teamsScheduledForCurrentMatchDay = new ArrayList<>();
/*matchDay*/
for (int i = 1; i <= matchDays / 2; i++) {
List<Team> homeTeamsInPreviousMatchDay = new ArrayList<>(homeTeamsInCurrentMatchDay);
homeTeamsInCurrentMatchDay.clear();
/*match number in current Matchday*/
for (int j = 1; j <= gamesNumberPerMatchDay; j++) {
/*first match*/
Match match = drawMatch(allMatches, teamsScheduledForCurrentMatchDay, homeTeamsInPreviousMatchDay);
allMatches.remove(match);
schedule.get(i).add(match);
homeTeamsInCurrentMatchDay.add(match.getHomeTeam());
teamsScheduledForCurrentMatchDay.add(match.getHomeTeam());
teamsScheduledForCurrentMatchDay.add(match.getAwayTeam());
/*revenge match*/
Match revengeMatch = Match.builder().homeTeam(match.getAwayTeam()).awayTeam(match.getHomeTeam()).build();
schedule.get((matchDays / 2) + i).add(revengeMatch);
allMatches.remove(revengeMatch);
}
teamsScheduledForCurrentMatchDay.clear();
}
return schedule;
}
private static Match drawMatch(List<Match> matches, List<Team> alreadyScheduledTeamsInCurrentMatchDay, List<Team> homeTeamsInPreviousMatchDay) {
List<Match> possibleMatches = matches.stream()
.filter(match -> !alreadyScheduledTeamsInCurrentMatchDay.contains(match.getHomeTeam()) && !alreadyScheduledTeamsInCurrentMatchDay.contains(match.getAwayTeam()))
.collect(Collectors.toList());
List<Match> possibleMatchesWithExclusionOfHomeTeams = new ArrayList<>(possibleMatches);
possibleMatchesWithExclusionOfHomeTeams.removeIf(match -> homeTeamsInPreviousMatchDay.contains(match.getHomeTeam()));
if (possibleMatchesWithExclusionOfHomeTeams.size() != 0) {
possibleMatches = new ArrayList<>(possibleMatchesWithExclusionOfHomeTeams);
}
int randomNumber = new Random().nextInt(possibleMatches.size());
return possibleMatches.get(randomNumber);
}
Team and Match blueprints are as follows:
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
class Team {
String name;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class Match {
private Team homeTeam;
private Team awayTeam;
}
The algorithm doesn't work. The
Exception in thread "main" java.lang.IllegalArgumentException: bound must be positive
is thrown - it means that for some matchday for some team there are 0 possible matches, which satisfy those rules listed at the beginning. I'm not sure, what am I missing here and how the schedule algorithm should look like to reflect the real one algorithm. In other words, why possibleMatches.size()
ever happen to be 0
and what corrections should be done to make this algorithm work properly?