Skip to content

Commit

Permalink
Merge pull request #526 from Kernel360/521-feat-doubles-tournament-le…
Browse files Browse the repository at this point in the history
…ague-plus-byematch

feat: 토너먼트 복식 부전승 로직 구현
  • Loading branch information
I-migi authored Dec 5, 2024
2 parents 4baf4f7 + 90fe8ed commit 899ac6e
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public enum ErrorCode {
CLUB_OWNER_CANT_WITHDRAW(412, "동호회원이 2명 이상인 동호회 회장은 동호회를 탈퇴할 수 없습니다."),
NOT_LEAGUE_OWNER(412, "경기를 만든 사용자만 수정 및 삭제를 할 수 있습니다"),
CLUB_MEMBER_EXPEL_EXCEPTION(412, "해당 동호회에서 제제를 받아 가입 신청을 할 수 없습니다."),
BYE_MATCH(412, "해당 매치는 부전승 매치 입니다"),
BYE_MATCH_ACTION_NOT_ALLOWED(412, "해당 매치는 부전승 매치이기 때문에 점수를 수정할 수 없습니다"),

// 500 Errors
INTERNAL_SERVER_ERROR(500, "서버 내부 오류가 발생했습니다."),
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.badminton.domain.common.exception.match;

import org.badminton.domain.common.enums.MatchType;
import org.badminton.domain.common.error.ErrorCode;
import org.badminton.domain.common.exception.BadmintonException;

public class RegisterScoreInByeMatchException extends BadmintonException {

public RegisterScoreInByeMatchException(Long matchId, MatchType matchType) {
super(ErrorCode.BYE_MATCH_ACTION_NOT_ALLOWED, "[매치 아이디 : " + matchId + "매치 유형: " + matchType + "]");
}

public RegisterScoreInByeMatchException(Long matchId, MatchType matchType, Exception e) {
super(ErrorCode.BYE_MATCH_ACTION_NOT_ALLOWED, "[매치 아이디 : " + matchId + "매치 유형: " + matchType + "]", e);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,8 @@ public Integer getSetNumberInProgress() {
}
return getSetInProgress().get().getSetNumber();
}

public void byeMatch() {
this.matchStatus = MatchStatus.BYE;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.badminton.infrastructure.match.reader;

import java.util.Arrays;
import java.util.List;

import org.badminton.domain.common.enums.MatchStatus;
Expand Down Expand Up @@ -64,12 +65,13 @@ public boolean allMatchesFinishedForLeague(Long leagueId) {

@Override
public boolean allMatchesNotStartedForLeague(Long leagueId) {
return doublesMatchRepository.allMatchesNotStartedForLeague(leagueId);
List<MatchStatus> UnPlayedMatchStatuses = Arrays.asList(MatchStatus.NOT_STARTED, MatchStatus.BYE);
return doublesMatchRepository.allMatchesNotStartedForLeague(leagueId, UnPlayedMatchStatuses);
}

@Override
public boolean allRoundMatchesDone(Long leagueId, int roundNumber) {
List<MatchStatus> statuses = List.of(MatchStatus.FINISHED, MatchStatus.BYE);
return doublesMatchRepository.areAllMatchesFinishedOrBye(leagueId, roundNumber, statuses);
List<MatchStatus> CompletedOrBypassedStatuses = List.of(MatchStatus.FINISHED, MatchStatus.BYE);
return doublesMatchRepository.areAllMatchesFinishedOrBye(leagueId, roundNumber, CompletedOrBypassedStatuses);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ boolean areAllMatchesFinishedOrBye(@Param("leagueId") Long leagueId, @Param("rou
@Query("SELECT COUNT(m) = 0 FROM DoublesMatch m WHERE m.league.leagueId = :leagueId AND m.matchStatus != 'FINISHED'")
boolean allMatchesFinishedForLeague(@Param("leagueId") Long leagueId);

@Query("SELECT COUNT(m) = 0 FROM DoublesMatch m WHERE m.league.leagueId = :leagueId AND m.matchStatus != 'NOT_STARTED'")
boolean allMatchesNotStartedForLeague(@Param("leagueId") Long leagueId);
@Query("SELECT NOT EXISTS (SELECT 1 FROM DoublesMatch match WHERE match.league.leagueId = :leagueId AND match.matchStatus NOT IN (:statuses))")
boolean allMatchesNotStartedForLeague(@Param("leagueId") Long leagueId,
@Param("statuses") List<MatchStatus> statuses);

}

Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
import java.util.List;

import org.badminton.domain.common.enums.MatchResult;
import org.badminton.domain.common.enums.MatchStatus;
import org.badminton.domain.common.enums.MatchType;
import org.badminton.domain.common.enums.SetStatus;
import org.badminton.domain.common.exception.match.AlreadyWinnerDeterminedException;
import org.badminton.domain.common.exception.match.LeagueParticipantNotDeterminedException;
import org.badminton.domain.common.exception.match.PreviousDetNotFinishedException;
import org.badminton.domain.common.exception.match.RegisterScoreInByeMatchException;
import org.badminton.domain.common.exception.match.RoundNotFinishedException;
import org.badminton.domain.common.exception.match.SetFinishedException;
import org.badminton.domain.domain.league.LeagueParticipantReader;
Expand Down Expand Up @@ -57,6 +60,14 @@ private static boolean isMatchWinnerDetermined(DoublesMatch doublesMatch) {
|| doublesMatch.getTeam2MatchResult() == MatchResult.WIN;
}

private static boolean isParticipantOddSize(List<LeagueParticipant> participants) {
return participants.size() % 4 != 0;
}

private static boolean isDoubleMatchOddSize(List<DoublesMatch> doublesMatches) {
return doublesMatches.size() % 2 != 0;
}

@Override
public BracketInfo makeBracket(League league, List<LeagueParticipant> leagueParticipantList) {
List<DoublesMatch> allMatches = new ArrayList<>();
Expand All @@ -69,6 +80,10 @@ public BracketInfo makeBracket(League league, List<LeagueParticipant> leaguePart
allMatches.addAll(createFirstRoundMatches(league, currentParticipants));
allMatches.addAll(createSubsequentRoundsMatches(league, totalRounds));

allMatches.stream()
.filter(doublesMatch -> doublesMatch.getMatchStatus() == MatchStatus.BYE && doublesMatch.getTeam1() != null)
.forEach(this::updateNextRoundMatch);

return BracketInfo.fromDoubles(totalRounds, allMatches);
}

Expand All @@ -82,6 +97,10 @@ public SetInfo.Main registerSetScoreInMatch(Long matchId, Integer setNumber,

validatePreviousRoundCompletion(doublesMatch.getLeague().getLeagueId(), doublesMatch.getRoundNumber());

if (doublesMatch.getMatchStatus() == MatchStatus.BYE) {
throw new RegisterScoreInByeMatchException(doublesMatch.getId(), MatchType.DOUBLES);
}

if (isMatchWinnerDetermined(doublesMatch)) {
throw new AlreadyWinnerDeterminedException(doublesMatch.getId());
}
Expand All @@ -101,8 +120,7 @@ public SetInfo.Main registerSetScoreInMatch(Long matchId, Integer setNumber,
}

if (isMatchWinnerDetermined(doublesMatch)) {
doublesMatchStore.store(doublesMatch);
updateNextRoundMatch(doublesMatch);
processMatchAndNextRound(doublesMatch);
}
if (isAllMatchFinished(doublesMatch)) {
leagueReader.readLeagueById(doublesMatch.getLeague().getLeagueId()).finishLeague();
Expand Down Expand Up @@ -155,36 +173,92 @@ public Main retrieveSet(Long matchId, int setNumber) {
return SetInfo.fromDoublesSet(matchId, setNumber, doublesSet);
}

private void processMatchAndNextRound(DoublesMatch doublesMatch) {
doublesMatchStore.store(doublesMatch);
updateNextRoundMatch(doublesMatch);
boolean allRoundMatchesDone = doublesMatchReader.allRoundMatchesDone(doublesMatch.getLeague().getLeagueId(),
doublesMatch.getRoundNumber());
boolean currentRoundInTotalRound =
doublesMatch.getRoundNumber() < doublesMatch.getLeague().getTotalRounds();

if (allRoundMatchesDone && currentRoundInTotalRound) {
List<DoublesMatch> nextRoundMatches = doublesMatchReader.findMatchesByLeagueAndRound(
doublesMatch.getLeague().getLeagueId(), doublesMatch.getRoundNumber() + 1
);
nextRoundMatches.stream()
.filter(match -> match.getMatchStatus() == MatchStatus.BYE)
.forEach(this::updateNextRoundMatch);
}
}

private List<DoublesMatch> createFirstRoundMatches(League league, List<LeagueParticipant> participants) {
List<DoublesMatch> matches = new ArrayList<>();
for (int i = 0; i < participants.size(); i += PARTICIPANTS_PER_TEAM * TEAMS_PER_MATCH) {

for (int i = 0; i < participants.size() - 2; i += PARTICIPANTS_PER_TEAM * TEAMS_PER_MATCH) {
Team team1 = new Team(participants.get(i), participants.get(i + 1));
Team team2 = new Team(participants.get(i + 2), participants.get(i + 3));
DoublesMatch match = new DoublesMatch(league, team1, team2, 1);
makeSetsInMatch(match);
doublesMatchStore.store(match);
matches.add(match);
}

if (isParticipantOddSize(participants)) {
Team byeTeam = new Team(participants.remove(participants.size() - 1),
participants.get(participants.size() - 2));
DoublesMatch byeMatch = new DoublesMatch(league, byeTeam, null, 1);
byeMatch.byeMatch();
doublesMatchStore.store(byeMatch);
matches.add(byeMatch);
}
return matches;
}

private List<DoublesMatch> createRoundMatches(League league, List<DoublesMatch> previousMatches, int roundNumber) {
private List<DoublesMatch> createMatchesRound(League league, List<DoublesMatch> previousRoundMatches,
int roundNumber) {
List<DoublesMatch> currentRoundMatches = new ArrayList<>();
for (int i = 0; i < previousMatches.size(); i += TEAMS_PER_MATCH) {

currentRoundMatches.addAll(createRegularMatchesForRound(league, previousRoundMatches, roundNumber));

if (isDoubleMatchOddSize(previousRoundMatches)) {
currentRoundMatches.add(createByeRoundMatch(league, previousRoundMatches, roundNumber));
}

return currentRoundMatches;
}

private List<DoublesMatch> createRegularMatchesForRound(League league, List<DoublesMatch> previousRoundMatches,
int roundNumber) {
List<DoublesMatch> regularRoundMatches = new ArrayList<>();

for (int i = 0; i < previousRoundMatches.size() - 1; i += TEAMS_PER_MATCH) {
DoublesMatch match = new DoublesMatch(league, null, null, roundNumber);
makeSetsInMatch(match);
doublesMatchStore.store(match);
currentRoundMatches.add(match);
regularRoundMatches.add(match);
}
return currentRoundMatches;

return regularRoundMatches;
}

private DoublesMatch createByeRoundMatch(League league, List<DoublesMatch> previousRoundMatches,
int roundNumber) {
DoublesMatch byeMatch = previousRoundMatches.get(previousRoundMatches.size() - 1);
Team winner = determineWinner(byeMatch);

DoublesMatch nextByeMatch = new DoublesMatch(league, winner, null, roundNumber);
nextByeMatch.byeMatch();
doublesMatchStore.store(nextByeMatch);

return nextByeMatch;
}

private List<DoublesMatch> createSubsequentRoundsMatches(League league, int totalRounds) {
List<DoublesMatch> matches = new ArrayList<>();
List<DoublesMatch> previousMatches = doublesMatchReader.findMatchesByLeagueAndRound(league.getLeagueId(), 1);

for (int roundNumber = 2; roundNumber <= totalRounds; roundNumber++) {
List<DoublesMatch> currentRoundMatches = createRoundMatches(league, previousMatches, roundNumber);
List<DoublesMatch> currentRoundMatches = createMatchesRound(league, previousMatches, roundNumber);
matches.addAll(currentRoundMatches);
previousMatches = currentRoundMatches;
}
Expand All @@ -200,9 +274,9 @@ private void makeSetsInMatch(DoublesMatch doublesMatch) {
doublesMatchStore.store(doublesMatch);
}

private void updateSetScore(DoublesMatch doublesMatch, int setIndex,
private void updateSetScore(DoublesMatch doublesMatch, int setNumber,
MatchCommand.UpdateSetScore updateSetScoreCommand) {
DoublesSet set = doublesMatch.getDoublesSet(setIndex);
DoublesSet set = doublesMatch.getDoublesSet(setNumber);
set.endSetScore(updateSetScoreCommand.getScore1(), updateSetScoreCommand.getScore2());

if (updateSetScoreCommand.getScore1() > updateSetScoreCommand.getScore2()) {
Expand Down Expand Up @@ -231,25 +305,37 @@ private void updateNextRoundMatch(DoublesMatch doublesMatch) {
Math.toIntExact(startMatch.getId()));

DoublesMatch nextRoundMatch = doublesMatchReader.getDoublesMatch((long)nextRoundMatchId);
if (nextRoundMatch != null) {
assignWinnerToNextRoundMatch(nextRoundMatch, winner);

if (nextRoundMatch == null) {
return;
}

if (doublesMatch.getMatchStatus() == MatchStatus.BYE) {
nextRoundMatch.defineTeam1(winner);
doublesMatchStore.store(nextRoundMatch);
return;
}

assignWinnerToNextRoundMatch(nextRoundMatch, winner);
doublesMatchStore.store(nextRoundMatch);
}

private void assignWinnerToNextRoundMatch(DoublesMatch nextRoundMatch, Team winner) {
if (nextRoundMatch.getTeam1() == null) {
nextRoundMatch.defineTeam1(winner);
} else {
return;
}
if (nextRoundMatch.getTeam2() == null) {
nextRoundMatch.defineTeam2(winner);
}

}

private Team determineWinner(DoublesMatch match) {
if (match.getTeam1MatchResult() == MatchResult.WIN) {
if (match.getMatchStatus() == MatchStatus.BYE || match.getTeam1MatchResult() == MatchResult.WIN) {
return match.getTeam1();
}

if (match.getTeam2MatchResult() == MatchResult.WIN) {
return match.getTeam2();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@

import org.badminton.domain.common.enums.MatchResult;
import org.badminton.domain.common.enums.MatchStatus;
import org.badminton.domain.common.enums.MatchType;
import org.badminton.domain.common.enums.SetStatus;
import org.badminton.domain.common.exception.match.AlreadyWinnerDeterminedException;
import org.badminton.domain.common.exception.match.ByeMatchException;
import org.badminton.domain.common.exception.match.LeagueParticipantNotDeterminedException;
import org.badminton.domain.common.exception.match.PreviousDetNotFinishedException;
import org.badminton.domain.common.exception.match.RegisterScoreInByeMatchException;
import org.badminton.domain.common.exception.match.RoundNotFinishedException;
import org.badminton.domain.common.exception.match.SetFinishedException;
import org.badminton.domain.domain.league.LeagueParticipantReader;
Expand Down Expand Up @@ -95,7 +96,7 @@ public SetInfo.Main registerSetScoreInMatch(Long matchId, Integer setNumber,
validatePreviousRoundCompletion(singlesMatch.getLeague().getLeagueId(), singlesMatch.getRoundNumber());

if (singlesMatch.getMatchStatus() == MatchStatus.BYE) {
throw new ByeMatchException(singlesMatch.getId());
throw new RegisterScoreInByeMatchException(singlesMatch.getId(), MatchType.SINGLES);
}

if (isMatchWinnerDetermined(singlesMatch)) {
Expand Down

0 comments on commit 899ac6e

Please sign in to comment.