fixing bug in ScoreCorrectionImpl regarding the delayed application of MaxPointsReason and scores; added test case; use live time point for winner determination

Change-Id: I15606fe99c52b32580bf9191e1a592828642c346
This commit is contained in:
Axel Uhl committed 2016-04-29 17:34:02 +02:00
1 parent 3ffb2be543
commit fc6d0b3e24
3 files changed
+115 -16

No files matched your search

@@ -1605,6 +1605,65 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
assertEquals(0., leaderboard1.getTotalPoints(c[0], beforeStartOfR1), 0.00000001); // not even the R1 scores apply before the start time of R1
}
/**
* When a DNS score correction is made then both, the MaxPointsReason and the score shall apply at the start of the race.
*/
@Test
public void testDNSScoreAppliesBeforeEndOfRace() throws NoWindException {
Competitor[] c = createCompetitors(4).toArray(new Competitor[0]);
final TimePoint endOfR1 = MillisecondsTimePoint.now();
final TimePoint withinR1 = endOfR1.minus(1000);
final TimePoint startOfR1 = withinR1.minus(10000);
final TimePoint beforeStartOfR1 = startOfR1.minus(10000);
final TimePoint afterEndOfR1 = endOfR1.plus(1000);
FlexibleLeaderboard leaderboard1 = new FlexibleLeaderboardImpl("Leaderboard 1", new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]),
new LowPoint(), null);
leaderboard1.addRace(new MockedTrackedRaceWithStartTimeAndRanks(startOfR1, Arrays.asList(c)) {
private static final long serialVersionUID = 8705622361027154428L;
public TimePoint getEndOfRace() {
return endOfR1;
}
}, "R1", /* medalRace */false);
leaderboard1.getScoreCorrection().correctScore(c[0], leaderboard1.getRaceColumnByName("R1"), 123.);
leaderboard1.getScoreCorrection().setMaxPointsReason(c[0], leaderboard1.getRaceColumnByName("R1"), MaxPointsReason.DNS);
assertEquals(MaxPointsReason.NONE, leaderboard1.getMaxPointsReason(c[0], leaderboard1.getRaceColumnByName("R1"), beforeStartOfR1));
assertEquals(MaxPointsReason.DNS, leaderboard1.getMaxPointsReason(c[0], leaderboard1.getRaceColumnByName("R1"), withinR1));
assertEquals(MaxPointsReason.DNS, leaderboard1.getMaxPointsReason(c[0], leaderboard1.getRaceColumnByName("R1"), afterEndOfR1));
assertNull(leaderboard1.getNetPoints(c[0], leaderboard1.getRaceColumnByName("R1"), beforeStartOfR1));
assertEquals(123., leaderboard1.getNetPoints(c[0], leaderboard1.getRaceColumnByName("R1"), withinR1), 0.00000001);
assertEquals(123., leaderboard1.getNetPoints(c[0], leaderboard1.getRaceColumnByName("R1"), afterEndOfR1), 0.00000001);
}
/**
* When a DNS score correction is made then both, the MaxPointsReason and the score shall apply at the start of the race.
*/
@Test
public void testDNFScoreDoesNotApplyBeforeEndOfRace() throws NoWindException {
Competitor[] c = createCompetitors(4).toArray(new Competitor[0]);
final TimePoint endOfR1 = MillisecondsTimePoint.now();
final TimePoint withinR1 = endOfR1.minus(1000);
final TimePoint startOfR1 = withinR1.minus(10000);
final TimePoint beforeStartOfR1 = startOfR1.minus(10000);
final TimePoint afterEndOfR1 = endOfR1.plus(1000);
FlexibleLeaderboard leaderboard1 = new FlexibleLeaderboardImpl("Leaderboard 1", new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]),
new LowPoint(), null);
leaderboard1.addRace(new MockedTrackedRaceWithStartTimeAndRanks(startOfR1, Arrays.asList(c)) {
private static final long serialVersionUID = 8705622361027154428L;
public TimePoint getEndOfRace() {
return endOfR1;
}
}, "R1", /* medalRace */false);
leaderboard1.getScoreCorrection().correctScore(c[0], leaderboard1.getRaceColumnByName("R1"), 123.);
leaderboard1.getScoreCorrection().setMaxPointsReason(c[0], leaderboard1.getRaceColumnByName("R1"), MaxPointsReason.DNF);
assertEquals(MaxPointsReason.NONE, leaderboard1.getMaxPointsReason(c[0], leaderboard1.getRaceColumnByName("R1"), beforeStartOfR1));
assertEquals(MaxPointsReason.NONE, leaderboard1.getMaxPointsReason(c[0], leaderboard1.getRaceColumnByName("R1"), withinR1));
assertEquals(MaxPointsReason.DNF, leaderboard1.getMaxPointsReason(c[0], leaderboard1.getRaceColumnByName("R1"), afterEndOfR1));
assertNull(leaderboard1.getNetPoints(c[0], leaderboard1.getRaceColumnByName("R1"), beforeStartOfR1));
assertEquals(1.0, leaderboard1.getNetPoints(c[0], leaderboard1.getRaceColumnByName("R1"), withinR1), 0.00000001); // tracked rank is 1
assertEquals(123., leaderboard1.getNetPoints(c[0], leaderboard1.getRaceColumnByName("R1"), afterEndOfR1), 0.00000001);
}
/**
* A test case for bug 1802. Competitors that didn't have a fleet (null fleet) were sorted to the end of the column even
* if they had a score correction assigned.
@@ -324,11 +324,39 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
@Override
public MaxPointsReason getMaxPointsReason(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
MaxPointsReason result = maxPointsReasons.get(raceColumn.getKey(competitor));
if (result == null || !isMaxPointsReasonApplicable(result, timePoint, raceColumn, competitor)) {
result = MaxPointsReason.NONE;
return getAnnotatedMaxPointsReason(competitor, raceColumn, timePoint).getMaxPointsReason();
}
return result;
private static class AnnotatedMaxPointsReason {
private final MaxPointsReason maxPointsReason;
private final boolean maxPointsReasonExistsButIsNotApplicableForTimePoint;
public AnnotatedMaxPointsReason(MaxPointsReason maxPointsReason,
boolean maxPointsReasonExistsButIsNotApplicableForTimePoint) {
super();
this.maxPointsReason = maxPointsReason;
this.maxPointsReasonExistsButIsNotApplicableForTimePoint = maxPointsReasonExistsButIsNotApplicableForTimePoint;
}
public MaxPointsReason getMaxPointsReason() {
return maxPointsReason;
}
public boolean isMaxPointsReasonExistsButIsNotApplicableForTimePoint() {
return maxPointsReasonExistsButIsNotApplicableForTimePoint;
}
}
private AnnotatedMaxPointsReason getAnnotatedMaxPointsReason(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
MaxPointsReason maxPointsReason = maxPointsReasons.get(raceColumn.getKey(competitor));
boolean maxPointsReasonExistsButIsNotApplicableForTimePoint;
if (maxPointsReason == null) {
maxPointsReason = MaxPointsReason.NONE;
maxPointsReasonExistsButIsNotApplicableForTimePoint = false;
} else if (!isMaxPointsReasonApplicable(maxPointsReason, timePoint, raceColumn, competitor)) {
maxPointsReason = MaxPointsReason.NONE;
maxPointsReasonExistsButIsNotApplicableForTimePoint = true;
} else {
maxPointsReasonExistsButIsNotApplicableForTimePoint = false;
}
return new AnnotatedMaxPointsReason(maxPointsReason, maxPointsReasonExistsButIsNotApplicableForTimePoint);
}
private boolean isMaxPointsReasonApplicable(MaxPointsReason maxPointsReason, TimePoint timePoint, RaceColumn raceColumn, Competitor competitor) {
@@ -360,15 +388,21 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
final NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher,
final ScoringScheme scoringScheme) {
Double result;
final MaxPointsReason maxPointsReason = getMaxPointsReason(competitor, raceColumn, timePoint);
if (maxPointsReason == MaxPointsReason.NONE) {
final AnnotatedMaxPointsReason maxPointsReason = getAnnotatedMaxPointsReason(competitor, raceColumn, timePoint);
if (maxPointsReason.getMaxPointsReason() == MaxPointsReason.NONE) {
// could be that there is a MaxPointsReason that just doesn't apply yet at timePoint; in this
// case ignore the score correction and deliver the uncorrected result
if (maxPointsReason.isMaxPointsReasonExistsButIsNotApplicableForTimePoint()) {
result = getUncorrectedScore(competitor, raceColumn, trackedRankProvider, scoringScheme, numberOfCompetitorsInLeaderboardFetcher, timePoint);
} else {
result = getCorrectedNonMaxedScore(competitor, raceColumn, trackedRankProvider, scoringScheme, numberOfCompetitorsInLeaderboardFetcher, timePoint);
}
} else {
// allow explicit override even when max points reason is specified; calculation may be wrong,
// e.g., in case we have an untracked race and the number of competitors is estimated incorrectly
Double correctedNonMaxedScore = correctedScores.get(raceColumn.getKey(competitor));
if (correctedNonMaxedScore == null) {
result = scoringScheme.getPenaltyScore(raceColumn, competitor, maxPointsReason,
result = scoringScheme.getPenaltyScore(raceColumn, competitor, maxPointsReason.getMaxPointsReason(),
getNumberOfCompetitorsInRace(raceColumn, competitor, numberOfCompetitorsInLeaderboardFetcher),
numberOfCompetitorsInLeaderboardFetcher);
} else {
@@ -379,7 +413,7 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
return new Result() {
@Override
public MaxPointsReason getMaxPointsReason() {
return maxPointsReason;
return maxPointsReason.getMaxPointsReason();
}
@Override
@@ -446,6 +480,18 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
Double correctedNonMaxedScore = correctedScores.get(raceColumn.getKey(competitor));
Double result;
if (correctedNonMaxedScore == null || isCertainlyBeforeRaceFinish(timePoint, raceColumn, competitor)) {
result = getUncorrectedScore(competitor, raceColumn, trackedRankProvider, scoringScheme,
numberOfCompetitorsInLeaderboardFetcher, timePoint);
} else {
result = correctedNonMaxedScore;
}
return result;
}
private Double getUncorrectedScore(final Competitor competitor, final RaceColumn raceColumn,
Callable<Integer> trackedRankProvider, ScoringScheme scoringScheme,
final NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) {
final Double result;
try {
int trackedRank = trackedRankProvider.call();
result = scoringScheme.getScoreForRank(leaderboard, raceColumn, competitor,
@@ -456,12 +502,9 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
}
}, numberOfCompetitorsInLeaderboardFetcher, timePoint);
} catch (Exception e) {
logger.log(Level.SEVERE, "Exception while computing corrected non maxed score", e);
logger.log(Level.SEVERE, "Exception while computing uncorrected score", e);
throw new RuntimeException(e);
}
} else {
result = correctedNonMaxedScore;
}
return result;
}
@@ -445,10 +445,7 @@ public class RaceContext {
}
// TODO do not calculate the winner if the blue flag is currently shown.
try {
TimePoint finishTime = trackedRace.getEndOfRace();
if (finishTime == null) {
finishTime = getLiveTimePoint();
}
TimePoint finishTime = getLiveTimePoint();
List<Competitor> competitors = leaderboard.getCompetitorsFromBestToWorst(raceColumn, finishTime);
if (competitors == null || competitors.isEmpty()) {
return null;