package nl.connectedit.swiss.service; import lombok.RequiredArgsConstructor; import lombok.extern.java.Log; import nl.connectedit.swiss.domain.EventType; import nl.connectedit.swiss.domain.StandingsEntry; import nl.connectedit.swiss.domain.Status; import nl.connectedit.swiss.domain.TournamentStatus; import nl.connectedit.swiss.domain.entity.*; import nl.connectedit.swiss.dto.ResultDto; import nl.connectedit.swiss.repository.TournamentRepository; import org.springframework.stereotype.Service; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import static nl.connectedit.swiss.service.ServiceUtil.*; @Service @RequiredArgsConstructor @Log public class TournamentPlayService { private final TournamentRepository tournamentRepository; private final StandingsService standingsService; public Tournament startRound(Tournament tournament, Long roundId) { getRound(tournament, roundId).setStatus(Status.IN_PROGRESS); tournament.setStatus(TournamentStatus.ONGOING); tournamentRepository.save(tournament); return tournament; } public Tournament finishRound(Tournament tournament, Long roundId) { getRound(tournament, roundId).setStatus(Status.FINISHED); tournamentRepository.save(tournament); return tournament; } public Tournament finishGroup(Tournament tournament, Long groupId) { var group = getGroup(tournament, groupId); group.setStatus(Status.FINISHED); checkForEventCompletion(tournament, group.getType()); tournamentRepository.save(tournament); return tournament; } private void checkForEventCompletion(Tournament tournament, EventType type) { Group group1 = null; Group group2 = null; Event event = null; for (var e : tournament.getEvents()) { if (e.getType() == type) { if (e.getGroups().size() != 2) { return; } group1 = e.getGroups().getFirst(); if (group1.getStatus() != Status.FINISHED) { return; } group2 = e.getGroups().getLast(); if (group2.getStatus() != Status.FINISHED) { return; } event = e; } } var finalsGroup = new Group(); finalsGroup.setName(type.getText()); finalsGroup.setType(type); finalsGroup.setStatus(Status.IN_PROGRESS); finalsGroup.setEvent(group1.getEvent()); var standings1 = standingsService.getStandings(group1.getRounds(), group1.getTeams()); var standings2 = standingsService.getStandings(group2.getRounds(), group2.getTeams()); var team1_1 = standings1.get(0).getTeam(); var team1_2 = standings1.get(1).getTeam(); var team2_1 = standings2.get(0).getTeam(); var team2_2 = standings2.get(1).getTeam(); team1_1.getGroups().add(finalsGroup); team1_2.getGroups().add(finalsGroup); team2_1.getGroups().add(finalsGroup); team2_2.getGroups().add(finalsGroup); finalsGroup.setTeams(List.of(team1_1, team1_2, team2_1, team2_2)); var finalsRound = new Round(); finalsRound.setGroup(finalsGroup); finalsRound.setStatus(Status.NOT_STARTED); finalsRound.setName("Finales"); finalsRound.setIsFinalsRound(Boolean.TRUE); var match1 = new Match(); match1.setRound(finalsRound); match1.setType(type); match1.setStatus(Status.NOT_STARTED); match1.setPlayed(false); match1.setTeam1(standings1.get(0).getTeam()); match1.setTeam2(standings2.get(0).getTeam()); var match2 = new Match(); match2.setRound(finalsRound); match2.setType(type); match2.setStatus(Status.NOT_STARTED); match2.setPlayed(false); match2.setTeam1(standings1.get(1).getTeam()); match2.setTeam2(standings2.get(1).getTeam()); finalsRound.setMatches(List.of(match1, match2)); finalsGroup.setRounds(List.of(finalsRound)); event.getGroups().add(finalsGroup); } public Tournament reopenGroup(Tournament tournament, Long groupId) { getGroup(tournament, groupId).setStatus(Status.IN_PROGRESS); tournamentRepository.save(tournament); return tournament; } public Tournament newRound(Tournament tournament, Long groupId) { var group = getGroup(tournament, groupId); var standings = standingsService.getStandings(group.getRounds(), group.getTeams()); var remainingTeams = standings.stream() .map(StandingsEntry::getTeam) .collect(Collectors.toCollection(ArrayList::new)); var round = new Round(); round.setName("Ronde " + (group.getRounds().size() + 1)); round.setGroup(group); round.setStatus(Status.NOT_STARTED); if (remainingTeams.size() % 2 == 1) { var playersNotToBeDrawnOut = tournament.getDrawnOutPlayers(); playersNotToBeDrawnOut.addAll(tournament.getPlayersPlayingOnlyOneEvent()); var tryPosition = remainingTeams.size() - 1; do { var tryDrawOutTeam = remainingTeams.get(tryPosition); if (!playersNotToBeDrawnOut.contains(tryDrawOutTeam.getPlayer1()) && (tryDrawOutTeam.getPlayer2() == null || !playersNotToBeDrawnOut.contains(tryDrawOutTeam.getPlayer2()))) { remainingTeams.remove(tryDrawOutTeam); round.setDrawnOut(tryDrawOutTeam); break; } tryPosition--; } while(true); } var matches = tryMatches(remainingTeams, group).reversed(); for (var match : matches) { match.setType(group.getType()); match.setStatus(Status.NOT_STARTED); match.setPlayed(false); match.setRound(round); } round.setMatches(matches); printRound(round, standings); group.getRounds().add(round); tournamentRepository.save(tournament); return tournament; } private void printRound(Round round, List standings) { for (var match: round.getMatches()) { log.info("%s - %s".formatted( String.valueOf(standings.stream().filter(entry -> entry.getTeam().equals(match.getTeam1())).map(StandingsEntry::getPosition).findFirst().get()), String.valueOf(standings.stream().filter(entry -> entry.getTeam().equals(match.getTeam2())).map(StandingsEntry::getPosition).findFirst().get()))); } } private static class ConflictInDrawException extends RuntimeException {} private List tryMatches(List remainingTeams, Group group) { var newMatches = new ArrayList(); if (remainingTeams.isEmpty()) { return newMatches; } var newMatch = new Match(); newMatch.setTeam1(remainingTeams.getFirst()); for (var opponentIndex = 1; opponentIndex < remainingTeams.size(); opponentIndex++) { if (!findPreviousMatchOccurence(newMatch.getTeam1(), remainingTeams.get(opponentIndex), group)) { newMatch.setTeam2(remainingTeams.get(opponentIndex)); } else { log.info("Wedstrijd %s - %s kwam al eerder voor.".formatted(newMatch.getTeam1().toString(), remainingTeams.get(opponentIndex).toString())); continue; } var newRemainingTeams = getRemainingTeams(remainingTeams, opponentIndex); if (newRemainingTeams.size() == 1) { newMatches.add(newMatch); break; } else { try { newMatches.addAll(tryMatches(newRemainingTeams, group)); newMatches.add(newMatch); break; } catch (ConflictInDrawException ignored) { } } } if (newMatch.getTeam2() == null) { log.info("Geen tegenstander gevonden voor %s.".formatted(newMatch.getTeam1().toString())); throw new ConflictInDrawException(); } return newMatches; } private List getRemainingTeams(List remainingTeams, int opponentIndex) { var newRemainingTeams = new ArrayList(); for (var remainingTeamIndex = 1; remainingTeamIndex < remainingTeams.size(); remainingTeamIndex++) { if (remainingTeamIndex == opponentIndex) continue; newRemainingTeams.add(remainingTeams.get(remainingTeamIndex)); } return newRemainingTeams; } private boolean findPreviousMatchOccurence(Team team1, Team team2, Group group) { for (var round : group.getRounds()) { for (var match : round.getMatches()) { if ((Objects.equals(match.getTeam1(), team1) && Objects.equals(match.getTeam2(), team2)) || (Objects.equals(match.getTeam1(), team2) && Objects.equals(match.getTeam2(), team1))) { return true; } } } return false; } public Tournament startMatch(Tournament tournament, Long matchId, Long court, Long counter) { var match = getMatch(tournament, matchId); match.setStatus(Status.IN_PROGRESS); match.setStartTime(LocalDateTime.now()); match.setCourt(court); var countingPlayer = getPlayer(tournament, counter); countingPlayer.setCounting(true); match.setCounter(countingPlayer.getPlayer()); tournamentRepository.save(tournament); return tournament; } private TournamentPlayer getPlayer(Tournament tournament, Long playerId) { for (var tournamentPlayer : tournament.getTournamentPlayers()) { if (playerId.equals(tournamentPlayer.getPlayer().getId())) { return tournamentPlayer; } } return null; } public Tournament stopMatch(Tournament tournament, Long matchId) { var match = getMatch(tournament, matchId); match.setStatus(Status.NOT_STARTED); match.setStartTime(null); match.setCourt(null); var countingPlayer = getPlayer(tournament, match.getCounter().getId()); countingPlayer.setCounting(false); match.setCounter(null); tournamentRepository.save(tournament); return tournament; } public Tournament saveResult(Tournament tournament, Long matchId, ResultDto result) { var match = getMatch(tournament, matchId); if (match.getGames().isEmpty()) { match.setEndTime(LocalDateTime.now()); match.setStatus(Status.FINISHED); match.setPlayed(true); } match.getGames().clear(); match.getGames().addAll(resultToGames(result, match)); var counter = match.getCounter(); if (counter != null) { // match was already saved, this is a correction var countingPlayer = getPlayer(tournament, counter.getId()); countingPlayer.setCounting(false); countingPlayer.incrementCounts(); match.setCounter(null); } tournamentRepository.save(tournament); return tournament; } private List resultToGames(ResultDto result, Match match) { var games = new ArrayList(); for (var game : result.getGames()) { if (game.getScore1() != null) { games.add(Game.builder() .score1(game.getScore1()) .score2(game.getScore2()) .match(match) .build()); } } return games; } public void updatePaid(Tournament tournament, Long playerId, Boolean paid) { var tournamentPlayer = tournament.getTournamentPlayers() .stream() .filter(player -> player.getPlayer().getId().equals(playerId)) .findFirst() .get(); tournamentPlayer.setPaid(paid); tournamentRepository.save(tournament); } public void updatePresent(Tournament tournament, Long playerId, Boolean present) { var tournamentPlayer = tournament.getTournamentPlayers() .stream() .filter(player -> player.getPlayer().getId().equals(playerId)) .findFirst() .get(); tournamentPlayer.setPresent(present); tournamentRepository.save(tournament); } }