diff --git a/src/main/java/net/theevilreaper/xerus/api/team/DefaultTeam.java b/src/main/java/net/theevilreaper/xerus/api/team/DefaultTeam.java index 6e55e41..6c5c27d 100644 --- a/src/main/java/net/theevilreaper/xerus/api/team/DefaultTeam.java +++ b/src/main/java/net/theevilreaper/xerus/api/team/DefaultTeam.java @@ -90,7 +90,7 @@ public void setCapacity(int capacity) { */ @Override public boolean canJoin() { - return capacity == DEFAULT_CAPACITY || players.size() != capacity; + return capacity == DEFAULT_CAPACITY || players.size() < capacity; } @Override diff --git a/src/main/java/net/theevilreaper/xerus/api/team/distribution/Splitter.java b/src/main/java/net/theevilreaper/xerus/api/team/distribution/Splitter.java index 241983c..f63d0f8 100644 --- a/src/main/java/net/theevilreaper/xerus/api/team/distribution/Splitter.java +++ b/src/main/java/net/theevilreaper/xerus/api/team/distribution/Splitter.java @@ -37,8 +37,12 @@ public class Splitter { * @param evenTeams if teams should be forced to be even * @param lowVariance if low variance is preferred * @return optimal desired teams constellation + * @throws IllegalArgumentException if {@code evenTeams} is set without {@code lowVariance} and the players don't fit into the teams */ public DistributionTeam[] compute(DistributionTeam[] ts, DistributionPlayer[] ps, List is, int teamSize, boolean evenTeams, boolean lowVariance) { + if (evenTeams && !lowVariance) { + validateCapacity(ts, ps, is, teamSize); + } //initialize this.ps = ps; this.evenTeams = evenTeams; @@ -54,6 +58,31 @@ public DistributionTeam[] compute(DistributionTeam[] ts, DistributionPlayer[] ps return this.ts; } + /** + * Checks if all players fit into the teams without exceeding the team size. + * + * @param ts outline for teams + * @param ps players + * @param is used player array indices + * @param teamSize size of each team + * @throws IllegalArgumentException if a team already exceeds the team size or not all players fit into the teams + */ + private void validateCapacity(DistributionTeam[] ts, DistributionPlayer[] ps, List is, int teamSize) { + int assignedPlayers = 0; + for (DistributionTeam team : ts) { + if (team.length() > teamSize) { + throw new IllegalArgumentException("The team " + team.name().asString() + " already exceeds the team size of " + teamSize); + } + assignedPlayers += team.length(); + } + + int totalPlayers = assignedPlayers + ps.length - is.size(); + int capacity = ts.length * teamSize; + if (totalPlayers > capacity) { + throw new IllegalArgumentException("Unable to distribute " + totalPlayers + " players into " + ts.length + " teams with a size of " + teamSize); + } + } + /** * @param i current position in players array * @param ts team array in development diff --git a/src/test/java/net/theevilreaper/xerus/api/team/TeamTest.java b/src/test/java/net/theevilreaper/xerus/api/team/TeamTest.java index 1d023be..7f0277f 100644 --- a/src/test/java/net/theevilreaper/xerus/api/team/TeamTest.java +++ b/src/test/java/net/theevilreaper/xerus/api/team/TeamTest.java @@ -1,9 +1,12 @@ package net.theevilreaper.xerus.api.team; import net.kyori.adventure.key.Key; +import net.minestom.server.entity.Player; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrowsExactly; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -44,4 +47,19 @@ void testTeamEquality() { assertEquals(team1, team2); assertEquals(team1.hashCode(), team2.hashCode()); } + + @Test + void testCanJoinWhenOverCapacity() { + Team team = Team.of(Key.key("xerus", "test"), 5); + for (int i = 0; i < 3; i++) { + team.getPlayers().add(Mockito.mock(Player.class)); + } + assertTrue(team.canJoin()); + + team.setCapacity(3); + assertFalse(team.canJoin()); + + team.setCapacity(2); + assertFalse(team.canJoin()); + } } diff --git a/src/test/java/net/theevilreaper/xerus/api/team/distribution/SplitterTest.java b/src/test/java/net/theevilreaper/xerus/api/team/distribution/SplitterTest.java new file mode 100644 index 0000000..6de8ae2 --- /dev/null +++ b/src/test/java/net/theevilreaper/xerus/api/team/distribution/SplitterTest.java @@ -0,0 +1,81 @@ +package net.theevilreaper.xerus.api.team.distribution; + +import net.kyori.adventure.key.Key; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.ArrayList; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +class SplitterTest { + + private static DistributionTeam[] createTeams(int amount) { + DistributionTeam[] teams = new DistributionTeam[amount]; + for (int i = 0; i < amount; i++) { + teams[i] = new DistributionTeam(Key.key("xerus", "team" + i)); + } + return teams; + } + + private static DistributionPlayer[] createPlayers(int amount) { + DistributionPlayer[] players = new DistributionPlayer[amount]; + for (int i = 0; i < amount; i++) { + players[i] = new DistributionPlayer(UUID.randomUUID(), 100 + i); + } + return players; + } + + @Test + void testTooManyPlayersForEvenTeams() { + DistributionTeam[] teams = createTeams(2); + DistributionPlayer[] players = createPlayers(5); + Splitter splitter = new Splitter(); + + assertThrowsExactly( + IllegalArgumentException.class, + () -> splitter.compute(teams, players, new ArrayList<>(), 2, true, false) + ); + } + + @Test + void testTooManyPlayersWithExistingTeamMembers() { + DistributionTeam[] teams = createTeams(2); + teams[0].add(new DistributionPlayer(UUID.randomUUID(), 100)); + DistributionPlayer[] players = createPlayers(4); + Splitter splitter = new Splitter(); + + assertThrowsExactly( + IllegalArgumentException.class, + () -> splitter.compute(teams, players, new ArrayList<>(), 2, true, false) + ); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testPlayersFitIntoEvenTeams(boolean lowVariance) { + DistributionTeam[] teams = createTeams(2); + DistributionPlayer[] players = createPlayers(4); + + DistributionTeam[] result = new Splitter().compute(teams, players, new ArrayList<>(), 2, true, lowVariance); + + assertNotNull(result); + for (DistributionTeam team : result) { + assertEquals(2, team.length()); + } + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testUnevenTeamsIgnoreTeamSize(boolean lowVariance) { + DistributionTeam[] teams = createTeams(2); + DistributionPlayer[] players = createPlayers(5); + + DistributionTeam[] result = new Splitter().compute(teams, players, new ArrayList<>(), 2, false, lowVariance); + + assertNotNull(result); + assertEquals(5, result[0].length() + result[1].length()); + } +}