Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

478 senbatsu shiai #488

Merged
merged 18 commits into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/softwaremagico/KendoTournamentManager)](https://github.com/softwaremagico/KendoTournamentManager)
[![GitHub last commit](https://img.shields.io/github/last-commit/softwaremagico/KendoTournamentManager)](https://github.com/softwaremagico/KendoTournamentManager)
[![CircleCI](https://circleci.com/gh/softwaremagico/KendoTournamentManager.svg?style=shield)](https://circleci.com/gh/softwaremagico/KendoTournamentManager)
[![Time](https://img.shields.io/badge/development-643.5h-blueviolet.svg)]()
[![Time](https://img.shields.io/badge/development-651h-blueviolet.svg)]()

[![Powered by](https://img.shields.io/badge/powered%20by%20java-orange.svg?logo=OpenJDK&logoColor=white)]()
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=kendo-tournament-backend&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=kendo-tournament-backend)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import com.softwaremagico.kt.core.score.ScoreOfCompetitor;
import com.softwaremagico.kt.core.score.ScoreOfTeam;
import com.softwaremagico.kt.core.tournaments.BubbleSortTournamentHandler;
import com.softwaremagico.kt.core.tournaments.SenbatsuTournamentHandler;
import com.softwaremagico.kt.persistence.entities.Achievement;
import com.softwaremagico.kt.persistence.entities.Duel;
import com.softwaremagico.kt.persistence.entities.Fight;
Expand Down Expand Up @@ -125,6 +126,11 @@ public class AchievementController extends BasicInsertableController<Achievement
private static final int DETHRONE_THE_KING_SILVER = 5;
private static final int DETHRONE_THE_KING_GOLD = 7;

private static final int SENBATSU_RUNGS_NORMAL = 3;
private static final int SENBATSU_RUNGS_BRONZE = 4;
private static final int SENBATSU_RUNGS_SILVER = 5;
private static final int SENBATSU_RUNGS_GOLD = 7;

private static final int MAX_PREVIOUS_TOURNAMENTS = 100;
private static final int MIN_TOURNAMENT_FIGHTS = 5;

Expand Down Expand Up @@ -153,6 +159,8 @@ public class AchievementController extends BasicInsertableController<Achievement

private final BubbleSortTournamentHandler bubbleSortTournamentHandler;

private final SenbatsuTournamentHandler senbatsuTournamentHandler;

private Tournament tournament;

private List<Role> rolesFromTournament;
Expand Down Expand Up @@ -182,7 +190,8 @@ protected AchievementController(AchievementProvider provider, AchievementConvert
RoleProvider roleProvider, TeamProvider teamProvider, AchievementProvider achievementProvider,
FightProvider fightProvider, DuelProvider duelProvider,
RankingProvider rankingProvider, GroupProvider groupProvider,
BubbleSortTournamentHandler bubbleSortTournamentHandler) {
BubbleSortTournamentHandler bubbleSortTournamentHandler,
SenbatsuTournamentHandler senbatsuTournamentHandler) {
super(provider, converter);
this.tournamentConverter = tournamentConverter;
this.tournamentProvider = tournamentProvider;
Expand All @@ -196,6 +205,7 @@ protected AchievementController(AchievementProvider provider, AchievementConvert
this.rankingProvider = rankingProvider;
this.groupProvider = groupProvider;
this.bubbleSortTournamentHandler = bubbleSortTournamentHandler;
this.senbatsuTournamentHandler = senbatsuTournamentHandler;
}

public interface AchievementsGeneratedListener {
Expand Down Expand Up @@ -458,6 +468,7 @@ public List<AchievementDTO> generateAchievements(TournamentDTO tournamentDTO) {
achievementsGenerated.addAll(generateVendettaAchievement(tournament));
achievementsGenerated.addAll(generateSithApprenticesAlwaysKillTheirMasterAchievement(tournament));
achievementsGenerated.addAll(generateDethroneTheKingAchievement(tournament));
achievementsGenerated.addAll(generateClimbTheLadderAchievement(tournament));

// Now generate extra grades.
achievementsGenerated.addAll(generateBillyTheKidAchievementBronze(tournament));
Expand Down Expand Up @@ -2088,6 +2099,42 @@ private List<Achievement> generateDethroneTheKingAchievement(Tournament tourname
return new ArrayList<>();
}

/**
* Somebody wins a fight despite the opponent has scored first.
*
* @param tournament
*/
private List<Achievement> generateClimbTheLadderAchievement(Tournament tournament) {
final List<Achievement> achievements = new ArrayList<>();
if (tournament.getType() == TournamentType.SENBATSU) {

final List<Group> groups = groupProvider.getGroups(tournament);
final List<Team> startingRanking = groups.get(0).getTeams();
final List<Team> endingRanking = senbatsuTournamentHandler.getFinalRanking(tournament);

//Hay many rungs have you climbed?
startingRanking.forEach(team -> {
final int startingPosition = startingRanking.indexOf(team);
final int endingPosition = endingRanking.indexOf(team);
if (endingPosition - startingPosition >= SENBATSU_RUNGS_GOLD) {
achievements.addAll(generateAchievement(AchievementType.CLIMB_THE_LADDER, AchievementGrade.GOLD,
team.getMembers(), tournament));
} else if (endingPosition - startingPosition >= SENBATSU_RUNGS_SILVER) {
achievements.addAll(generateAchievement(AchievementType.CLIMB_THE_LADDER, AchievementGrade.SILVER,
team.getMembers(), tournament));
} else if (endingPosition - startingPosition >= SENBATSU_RUNGS_BRONZE) {
achievements.addAll(generateAchievement(AchievementType.CLIMB_THE_LADDER, AchievementGrade.BRONZE,
team.getMembers(), tournament));
} else if (endingPosition - startingPosition >= SENBATSU_RUNGS_NORMAL) {
achievements.addAll(generateAchievement(AchievementType.CLIMB_THE_LADDER, AchievementGrade.NORMAL,
team.getMembers(), tournament));

}
});
}
return achievements;
}


/**
* Achievement for the stormtrooper syndrome in two tournaments.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ protected List<DTO> convertAll(Collection<ENTITY> entities) {
.collect(Collectors.toCollection(ArrayList::new))));
}

protected List<DTO> convertAllNotSorted(Collection<ENTITY> entities) {
return new ArrayList<>(converter.convertAllNotSorted(entities.stream().map(this::createConverterRequest)
.collect(Collectors.toCollection(ArrayList::new))));
}

protected List<ENTITY> reverseAll(Collection<DTO> dtos) {
return converter.reverseAll(dtos);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import com.softwaremagico.kt.core.providers.TournamentExtraPropertyProvider;
import com.softwaremagico.kt.core.providers.TournamentProvider;
import com.softwaremagico.kt.core.tournaments.ITournamentManager;
import com.softwaremagico.kt.core.tournaments.SenbatsuTournamentHandler;
import com.softwaremagico.kt.core.tournaments.TournamentHandlerSelector;
import com.softwaremagico.kt.logger.ExceptionType;
import com.softwaremagico.kt.persistence.entities.Fight;
Expand All @@ -50,6 +51,7 @@
import com.softwaremagico.kt.persistence.repositories.FightRepository;
import com.softwaremagico.kt.persistence.values.TournamentExtraPropertyKey;
import com.softwaremagico.kt.persistence.values.TournamentType;
import jakarta.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

Expand All @@ -68,6 +70,7 @@ public class FightController extends BasicInsertableController<Fight, FightDTO,
private final Set<FightsAddedListener> fightsAddedListeners = new HashSet<>();
private final TournamentExtraPropertyProvider tournamentExtraPropertyProvider;
private final ParticipantProvider participantProvider;
private final SenbatsuTournamentHandler senbatsuTournamentHandler;

private final GroupProvider groupProvider;
private final TeamConverter teamConverter;
Expand All @@ -82,7 +85,7 @@ public FightController(FightProvider provider, FightConverter converter, Tournam
TournamentProvider tournamentProvider, TournamentHandlerSelector tournamentHandlerSelector,
TournamentExtraPropertyProvider tournamentExtraPropertyProvider,
ParticipantProvider participantProvider, GroupProvider groupProvider,
TeamConverter teamConverter, FightProvider fightProvider) {
TeamConverter teamConverter, SenbatsuTournamentHandler senbatsuTournamentHandler) {
super(provider, converter);
this.tournamentConverter = tournamentConverter;
this.tournamentProvider = tournamentProvider;
Expand All @@ -91,6 +94,7 @@ public FightController(FightProvider provider, FightConverter converter, Tournam
this.participantProvider = participantProvider;
this.groupProvider = groupProvider;
this.teamConverter = teamConverter;
this.senbatsuTournamentHandler = senbatsuTournamentHandler;
}

public void addFightsAddedListeners(FightsAddedListener listener) {
Expand All @@ -116,9 +120,24 @@ public List<FightDTO> getByTournamentId(Integer tournamentId) {
ExceptionType.INFO)))));
}


@Transactional
public FightDTO create(FightDTO dto, String username) {
if (dto.getTournament().getType() == TournamentType.SENBATSU) {
//Senbatsu only accepts fights on challenge distance.
senbatsuTournamentHandler.checkFight(reverse(dto));
}
return super.create(dto, username);
}


@Override
public FightDTO update(FightDTO dto, String username) {
try {
if (dto.getTournament().getType() == TournamentType.SENBATSU) {
//Senbatsu only accepts fights on challenge distance.
senbatsuTournamentHandler.checkFight(reverse(dto));
}
return super.update(dto, username);
} finally {
if (dto.getTournament().getType() == TournamentType.KING_OF_THE_MOUNTAIN) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@
import com.softwaremagico.kt.core.exceptions.ValidateBadRequestException;
import com.softwaremagico.kt.core.providers.TeamProvider;
import com.softwaremagico.kt.core.providers.TournamentProvider;
import com.softwaremagico.kt.core.tournaments.SenbatsuTournamentHandler;
import com.softwaremagico.kt.persistence.entities.Group;
import com.softwaremagico.kt.persistence.entities.Team;
import com.softwaremagico.kt.persistence.entities.Tournament;
import com.softwaremagico.kt.persistence.repositories.GroupRepository;
import com.softwaremagico.kt.persistence.repositories.TeamRepository;
import com.softwaremagico.kt.persistence.values.TournamentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

Expand All @@ -51,16 +53,19 @@ public class TeamController extends BasicInsertableController<Team, TeamDTO, Tea
private final TournamentConverter tournamentConverter;
private final ParticipantConverter participantConverter;
private final GroupRepository groupRepository;
private final SenbatsuTournamentHandler senbatsuTournamentHandler;


@Autowired
public TeamController(TeamProvider provider, TeamConverter converter, TournamentProvider tournamentProvider,
TournamentConverter tournamentConverter, ParticipantConverter participantConverter, GroupRepository groupRepository) {
TournamentConverter tournamentConverter, ParticipantConverter participantConverter, GroupRepository groupRepository,
SenbatsuTournamentHandler senbatsuTournamentHandler) {
super(provider, converter);
this.tournamentProvider = tournamentProvider;
this.tournamentConverter = tournamentConverter;
this.participantConverter = participantConverter;
this.groupRepository = groupRepository;
this.senbatsuTournamentHandler = senbatsuTournamentHandler;
}

@Override
Expand All @@ -81,11 +86,20 @@ public List<TeamDTO> getAllByTournament(Integer tournamentId, String createdBy)
.orElseThrow(() -> new TournamentNotFoundException(getClass(), "No tournament found with id '" + tournamentId + "'."));
final List<TeamDTO> teams = convertAll(getProvider().getAll(tournament));
if (teams.isEmpty()) {
return convertAll(getProvider().createDefaultTeams(tournament, createdBy));
return convertAllNotSorted(getProvider().createDefaultTeams(tournament, createdBy));
}
return teams;
}

public List<TeamDTO> getAllRemainingByTournament(Integer tournamentId, String createdBy) {
final Tournament tournament = tournamentProvider.get(tournamentId)
.orElseThrow(() -> new TournamentNotFoundException(getClass(), "No tournament found with id '" + tournamentId + "'."));
if (tournament.getType() != TournamentType.SENBATSU) {
return getAllByTournament(tournamentId, createdBy);
}
return convertAllNotSorted(senbatsuTournamentHandler.getNextTeamsOrderedByRanks(tournament, null));
}

public long countByTournament(Integer tournamentId) {
return getProvider().count(tournamentProvider.get(tournamentId)
.orElseThrow(() -> new TournamentNotFoundException(getClass(), "No tournament found with id '" + tournamentId + "'.")));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.softwaremagico.kt.core.providers.GroupProvider;
import com.softwaremagico.kt.core.providers.TournamentExtraPropertyProvider;
import com.softwaremagico.kt.core.providers.TournamentProvider;
import com.softwaremagico.kt.core.tournaments.SenbatsuTournamentHandler;
import com.softwaremagico.kt.persistence.entities.TournamentExtraProperty;
import com.softwaremagico.kt.persistence.repositories.TournamentExtraPropertyRepository;
import com.softwaremagico.kt.persistence.values.TournamentExtraPropertyKey;
Expand Down Expand Up @@ -77,6 +78,11 @@ public List<TournamentExtraPropertyDTO> getByTournamentId(Integer tournamentId)
}

public TournamentExtraPropertyDTO getByTournamentAndProperty(Integer tournamentId, TournamentExtraPropertyKey key) {
if (key == TournamentExtraPropertyKey.SENBATSU_CHALLENGE_DISTANCE) {
return convert(getProvider().getByTournamentAndProperty(tournamentProvider.get(tournamentId)
.orElseThrow(() -> new TournamentNotFoundException(getClass(), "No tournament found with id '" + tournamentId + "'.")), key,
SenbatsuTournamentHandler.DEFAULT_CHALLENGE_DISTANCE));
}
return convert(getProvider().getByTournamentAndProperty(tournamentProvider.get(tournamentId)
.orElseThrow(() -> new TournamentNotFoundException(getClass(), "No tournament found with id '" + tournamentId + "'.")), key));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,12 @@ public List<DTO> convertAll(Collection<REQUEST> from) {
return from.stream().map(this::convert).sorted(Comparator.comparing(ElementDTO::getCreatedAt,
Comparator.nullsFirst(Comparator.naturalOrder()))).collect(Collectors.toList());
}

public List<DTO> convertAllNotSorted(Collection<REQUEST> from) {
if (from == null) {
return new ArrayList<>();
}
//Returns the DTOs sorted by creation time by default
return from.stream().map(this::convert).collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.softwaremagico.kt.core.exceptions;

/*-
* #%L
* Kendo Tournament Manager (Core)
* %%
* Copyright (C) 2021 - 2024 Softwaremagico
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/


import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

import java.io.Serial;

@ResponseStatus(HttpStatus.BAD_REQUEST)
public class InvalidChallengeDistanceException extends InvalidFightException {

@Serial
private static final long serialVersionUID = -7502207287976639654L;


public InvalidChallengeDistanceException(Class<?> clazz, String message) {
super(clazz, message);
}

public InvalidChallengeDistanceException(Class<?> clazz) {
this(clazz, "Invalid!");
}

public InvalidChallengeDistanceException(Class<?> clazz, Throwable e) {
super(clazz, e);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.softwaremagico.kt.core.exceptions;

/*-
* #%L
* Kendo Tournament Manager (Core)
* %%
* Copyright (C) 2021 - 2024 Softwaremagico
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/

import com.softwaremagico.kt.logger.ExceptionType;
import com.softwaremagico.kt.logger.LoggedException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

import java.io.Serial;

@ResponseStatus(HttpStatus.BAD_REQUEST)
public class InvalidFightException extends LoggedException {

@Serial
private static final long serialVersionUID = -8154646993842600656L;

public InvalidFightException(Class<?> clazz, String message, ExceptionType type) {
super(clazz, message, type, HttpStatus.BAD_REQUEST);
}

public InvalidFightException(Class<?> clazz, String message) {
super(clazz, message, ExceptionType.SEVERE, HttpStatus.BAD_REQUEST);
}

public InvalidFightException(Class<?> clazz) {
this(clazz, "Invalid Fight");
}

public InvalidFightException(Class<?> clazz, Throwable e) {
super(clazz, e);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,6 @@ public List<Fight> createFights(Tournament tournament, TeamsOrder teamsOrder, In
//Reset the counter.
tournamentExtraPropertyProvider.save(new TournamentExtraProperty(tournament,
TournamentExtraPropertyKey.KING_INDEX, "1"));
tournamentExtraPropertyProvider.save(new TournamentExtraProperty(tournament,
TournamentExtraPropertyKey.BUBBLE_SORT_ITERATION, "0"));
return fights;
}

Expand Down Expand Up @@ -250,7 +248,7 @@ public List<Team> getTeamsOrderedByRanks(Tournament tournament, Group group, Dra
teams.add(0, group.getFights().get(group.getFights().size() - 1).getTeam2());
break;
case OLDEST_ELIMINATED, BOTH_ELIMINATED:
//Both cannot be on bubble sort!
//Both_eliminated cannot be on bubble sort!
default:
//Oldest is Team1 always.
teams.add(0, group.getFights().get(group.getFights().size() - 1).getTeam2());
Expand Down
Loading
Loading