mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-02 18:33:54 +00:00
further approaching a solution for bug 961: added tests for excluding columns from total count if one fleet hasn't raced
This commit is contained in:
1 parent
e71e58649c
commit
f0dc58bde4
2 files changed
+146
-23
No files matched your search
+119
@@ -108,6 +108,125 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Regarding bug 961, test scoring in a leaderboard that has a qualification series with two unordered groups where for one
|
||||
* column only one group has raced (expressed by a mocked TrackedRace attached to the column). Ensure that the column doesn't
|
||||
* count for neither the total points sum nor the competitor ordering.
|
||||
*/
|
||||
@Test
|
||||
public void testUnorderedGroupsWithOneGroupNotHavingRacedInAColumn() throws NoWindException {
|
||||
List<Competitor> competitors = createCompetitors(10);
|
||||
Regatta regatta = createRegatta(/* qualifying */ 2, new String[] { "Yellow", "Blue" }, /* final */0,
|
||||
new String[] { "Default" },
|
||||
/* medal */false, "testUnorderedGroupsWithOneGroupNotHavingRacedInAcolumn",
|
||||
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
|
||||
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
|
||||
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
|
||||
Series qualificationSeries;
|
||||
Iterator<? extends Series> seriesIter = regatta.getSeries().iterator();
|
||||
qualificationSeries = seriesIter.next();
|
||||
TimePoint now = MillisecondsTimePoint.now();
|
||||
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
|
||||
TrackedRace q1Yellow = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(0, 5));
|
||||
TrackedRace q1Blue = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(5, 10));
|
||||
RaceColumn q1Column = qualificationSeries.getRaceColumnByName("Q1");
|
||||
q1Column.setTrackedRace(q1Column.getFleetByName("Yellow"), q1Yellow);
|
||||
q1Column.setTrackedRace(q1Column.getFleetByName("Blue"), q1Blue);
|
||||
List<Competitor> rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
|
||||
for (int i=0; i<5; i++) {
|
||||
assertTrue(competitors.get(i) == rankedCompetitors.get(2*i) || competitors.get(i) == rankedCompetitors.get(2*i+1));
|
||||
assertTrue(competitors.get(i+5) == rankedCompetitors.get(2*i) || competitors.get(i+5) == rankedCompetitors.get(2*i+1));
|
||||
assertEquals((double) (i+1), leaderboard.getTotalPoints(competitors.get(i), later), 0.000000001);
|
||||
assertEquals((double) (i+1), leaderboard.getTotalPoints(competitors.get(i+5), later), 0.0000000001);
|
||||
}
|
||||
// now add one race for yellow fleet and test that it doesn't count because blue fleet is still missing its race for Q2
|
||||
TrackedRace q2Yellow = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(3, 8));
|
||||
RaceColumn q2Column = qualificationSeries.getRaceColumnByName("Q2");
|
||||
q2Column.setTrackedRace(q2Column.getFleetByName("Yellow"), q2Yellow);
|
||||
List<Competitor> rankedCompetitorsWithOneRaceMissingInQ2 = leaderboard.getCompetitorsFromBestToWorst(later);
|
||||
for (int i=0; i<5; i++) {
|
||||
assertTrue(competitors.get(i) == rankedCompetitorsWithOneRaceMissingInQ2.get(2*i) ||
|
||||
competitors.get(i) == rankedCompetitorsWithOneRaceMissingInQ2.get(2*i+1));
|
||||
assertTrue(competitors.get(i+5) == rankedCompetitorsWithOneRaceMissingInQ2.get(2*i) ||
|
||||
competitors.get(i+5) == rankedCompetitorsWithOneRaceMissingInQ2.get(2*i+1));
|
||||
assertEquals((double) (i+1), leaderboard.getTotalPoints(competitors.get(i), later), 0.000000001);
|
||||
assertEquals((double) (i+1), leaderboard.getTotalPoints(competitors.get(i+5), later), 0.0000000001);
|
||||
}
|
||||
// now add a tracked race for the blue fleet for Q2 and assert that the Q2 scores count for the total points sum
|
||||
TrackedRace q2Blue = new MockedTrackedRaceWithStartTimeAndRanks(now,
|
||||
Arrays.asList(new Competitor[] { competitors.get(0), competitors.get(1), competitors.get(2),
|
||||
competitors.get(8), competitors.get(9) }));
|
||||
q2Column.setTrackedRace(q2Column.getFleetByName("Blue"), q2Blue);
|
||||
// the new order in Q2 expected to be { (0, 3), (1, 4), (2, 5), (8, 6), (9, 7) }
|
||||
// therefore the new total points, in ascending order, are expected to be:
|
||||
// { 0: 1+1=2; 5: 1+3=4; 1: 2+2=4; 3: 4+1=5; 2: 3+3=6; 6: 2+4=6; 4: 5+2=7; 7: 3+5=8; 8: 4+4=8; 9: 5+5=10 }
|
||||
double[] expectedTotalPoints = new double[] { 2, 4, 6, 5, 7, 4, 6, 8, 8, 10 };
|
||||
int[] expectedOrderAfterTwoFullRacesPlusMinusOne = new int[] { 0, 5, 1, 3, 2, 6, 4, 7, 8, 9 };
|
||||
List<Competitor> rankedCompetitorsWithAllRacesInQ2 = leaderboard.getCompetitorsFromBestToWorst(later);
|
||||
for (int i=0; i<5; i++) {
|
||||
assertTrue(competitors.get(expectedOrderAfterTwoFullRacesPlusMinusOne[2*i]) == rankedCompetitorsWithAllRacesInQ2.get(2*i) ||
|
||||
competitors.get(expectedOrderAfterTwoFullRacesPlusMinusOne[2*i]) == rankedCompetitorsWithAllRacesInQ2.get(2*i+1));
|
||||
assertTrue(competitors.get(expectedOrderAfterTwoFullRacesPlusMinusOne[2*i+1]) == rankedCompetitorsWithAllRacesInQ2.get(2*i) ||
|
||||
competitors.get(expectedOrderAfterTwoFullRacesPlusMinusOne[2*i+1]) == rankedCompetitorsWithAllRacesInQ2.get(2*i+1));
|
||||
assertEquals(expectedTotalPoints[2*i], leaderboard.getTotalPoints(competitors.get(2*i), later), 0.000000001);
|
||||
assertEquals(expectedTotalPoints[2*i+1], leaderboard.getTotalPoints(competitors.get(2*i+1), later), 0.000000001);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Regarding bug 961, test scoring in a leaderboard that has a qualification series with two unordered groups where for one
|
||||
* column only one group has raced (expressed by a mocked TrackedRace attached to the column). Then add the second tracked
|
||||
* race for the second column but such that it hasn't started yet at the time point of the query. Then add a score correction,
|
||||
* simulating the use of a proxy race (that doesn't ever start) and a manual score entry.
|
||||
*/
|
||||
@Test
|
||||
public void testUnorderedGroupsWithOneGroupNotHavingStartedRacedInAcolumnAndThenCorrectingScore() throws NoWindException {
|
||||
List<Competitor> competitors = createCompetitors(10);
|
||||
Regatta regatta = createRegatta(/* qualifying */ 2, new String[] { "Yellow", "Blue" }, /* final */0,
|
||||
new String[] { "Default" },
|
||||
/* medal */false, "testUnorderedGroupsWithOneGroupNotHavingRacedInAcolumn",
|
||||
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
|
||||
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
|
||||
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
|
||||
Series qualificationSeries;
|
||||
Iterator<? extends Series> seriesIter = regatta.getSeries().iterator();
|
||||
qualificationSeries = seriesIter.next();
|
||||
TimePoint now = MillisecondsTimePoint.now();
|
||||
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
|
||||
TrackedRace q1Yellow = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(0, 5));
|
||||
TrackedRace q1Blue = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(5, 10));
|
||||
RaceColumn q1Column = qualificationSeries.getRaceColumnByName("Q1");
|
||||
q1Column.setTrackedRace(q1Column.getFleetByName("Yellow"), q1Yellow);
|
||||
q1Column.setTrackedRace(q1Column.getFleetByName("Blue"), q1Blue);
|
||||
// now add one race for yellow fleet and test that it doesn't count because blue fleet is still missing its race for Q2
|
||||
TrackedRace q2Yellow = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(3, 8));
|
||||
RaceColumn q2Column = qualificationSeries.getRaceColumnByName("Q2");
|
||||
q2Column.setTrackedRace(q2Column.getFleetByName("Yellow"), q2Yellow);
|
||||
// now add a tracked race for the blue fleet for Q2 that hasn't started yet and assert that the Q2 scores still don't count for the total points sum
|
||||
TimePoint muchLater = later.plus(10000000000l);
|
||||
TrackedRace q2Blue = new MockedTrackedRaceWithStartTimeAndRanks(muchLater,
|
||||
Arrays.asList(new Competitor[] { competitors.get(0), competitors.get(1), competitors.get(2),
|
||||
competitors.get(8), competitors.get(9) }));
|
||||
q2Column.setTrackedRace(q2Column.getFleetByName("Blue"), q2Blue);
|
||||
List<Competitor> rankedCompetitorsWithOneRaceMissingInQ2 = leaderboard.getCompetitorsFromBestToWorst(later);
|
||||
for (int i=0; i<5; i++) {
|
||||
assertTrue(competitors.get(i) == rankedCompetitorsWithOneRaceMissingInQ2.get(2*i) ||
|
||||
competitors.get(i) == rankedCompetitorsWithOneRaceMissingInQ2.get(2*i+1));
|
||||
assertTrue(competitors.get(i+5) == rankedCompetitorsWithOneRaceMissingInQ2.get(2*i) ||
|
||||
competitors.get(i+5) == rankedCompetitorsWithOneRaceMissingInQ2.get(2*i+1));
|
||||
assertEquals((double) (i+1), leaderboard.getTotalPoints(competitors.get(i), later), 0.000000001);
|
||||
assertEquals((double) (i+1), leaderboard.getTotalPoints(competitors.get(i+5), later), 0.0000000001);
|
||||
}
|
||||
assertFalse(leaderboard.getScoringScheme().isValidInTotalScore(leaderboard, q2Column, later));
|
||||
// now add a score correction for Q2/Blue to make it count:
|
||||
leaderboard.getScoreCorrection().correctScore(competitors.get(9), q2Column, 42.);
|
||||
assertTrue(leaderboard.getScoringScheme().isValidInTotalScore(leaderboard, q2Column, later));
|
||||
// the new order in Q2 expected to be { (9, 3), ... } (we don't know about any competitor in q2Blue but #9
|
||||
// therefore the new total points for #9 are
|
||||
// { 9: 5+42=47 }
|
||||
assertEquals(47.0, leaderboard.getTotalPoints(competitors.get(9), later), 0.00000001);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the competitors ranking worse than the disqualified competitor advance by one in the
|
||||
|
||||
+27
-23
@@ -50,11 +50,13 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
private final ScoringScheme scoringScheme;
|
||||
private final Map<Pair<Competitor, RaceColumn>, Double> totalPointsCache;
|
||||
private final boolean nullScoresAreBetter;
|
||||
private final TimePoint timePoint;
|
||||
|
||||
public LeaderboardTotalRankComparator(Leaderboard leaderboard, TimePoint timePoint,
|
||||
ScoringScheme scoringScheme, boolean nullScoresAreBetter) throws NoWindException {
|
||||
super();
|
||||
this.leaderboard = leaderboard;
|
||||
this.timePoint = timePoint;
|
||||
this.scoringScheme = scoringScheme;
|
||||
this.nullScoresAreBetter = nullScoresAreBetter;
|
||||
totalPointsCache = new HashMap<Pair<Competitor, RaceColumn>, Double>();
|
||||
@@ -79,29 +81,31 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
Double o1MedalRaceScore = 0.0;
|
||||
Double o2MedalRaceScore = 0.0;
|
||||
for (RaceColumn raceColumn : getLeaderboard().getRaceColumns()) {
|
||||
int preemptiveColumnResult = 0;
|
||||
final Double o1Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o1, raceColumn));
|
||||
if (o1Score != null) {
|
||||
o1Scores.add(o1Score);
|
||||
o1ScoreSum += o1Score;
|
||||
}
|
||||
final Double o2Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o2, raceColumn));
|
||||
if (o2Score != null) {
|
||||
o2Scores.add(o2Score);
|
||||
o2ScoreSum += o2Score;
|
||||
}
|
||||
if (raceColumn.isMedalRace()) {
|
||||
o1MedalRaceScore = o1Score;
|
||||
o2MedalRaceScore = o2Score;
|
||||
// similar to compareByFleet, however, tracking is not required; having medal race column points
|
||||
// (tracked or manual) is sufficient
|
||||
preemptiveColumnResult = compareByMedalRaceParticipation(o1Score, o2Score);
|
||||
}
|
||||
if (preemptiveColumnResult == 0) {
|
||||
preemptiveColumnResult = compareByFleet(raceColumn, o1, o2);
|
||||
}
|
||||
if (preemptiveColumnResult != 0) {
|
||||
return preemptiveColumnResult;
|
||||
if (getLeaderboard().getScoringScheme().isValidInTotalScore(getLeaderboard(), raceColumn, timePoint)) {
|
||||
int preemptiveColumnResult = 0;
|
||||
final Double o1Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o1, raceColumn));
|
||||
if (o1Score != null) {
|
||||
o1Scores.add(o1Score);
|
||||
o1ScoreSum += o1Score;
|
||||
}
|
||||
final Double o2Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o2, raceColumn));
|
||||
if (o2Score != null) {
|
||||
o2Scores.add(o2Score);
|
||||
o2ScoreSum += o2Score;
|
||||
}
|
||||
if (raceColumn.isMedalRace()) {
|
||||
o1MedalRaceScore = o1Score;
|
||||
o2MedalRaceScore = o2Score;
|
||||
// similar to compareByFleet, however, tracking is not required; having medal race column points
|
||||
// (tracked or manual) is sufficient
|
||||
preemptiveColumnResult = compareByMedalRaceParticipation(o1Score, o2Score);
|
||||
}
|
||||
if (preemptiveColumnResult == 0) {
|
||||
preemptiveColumnResult = compareByFleet(raceColumn, o1, o2);
|
||||
}
|
||||
if (preemptiveColumnResult != 0) {
|
||||
return preemptiveColumnResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
// now count the races in which they scored; if they scored in a different number of races, prefer the
|
||||
|
||||
Reference in new issue
Block a user