mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-29 23:37:10 +00:00
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:
1 parent
3ffb2be543
commit
fc6d0b3e24
3 files changed
+118
-19
No files matched your search
+59
@@ -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.
|
||||
|
||||
+55
-12
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+4
-7
@@ -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;
|
||||
@@ -551,7 +548,7 @@ public class RaceContext {
|
||||
if (finishTime != null && now.after(finishTime)) {
|
||||
return RaceViewState.FINISHED;
|
||||
}
|
||||
if(raceLog != null) {
|
||||
if (raceLog != null) {
|
||||
RaceLogFlagEvent abortingFlagEvent = checkForAbortFlagEvent();
|
||||
if (abortingFlagEvent != null) {
|
||||
Flags upperFlag = abortingFlagEvent.getUpperFlag();
|
||||
@@ -567,10 +564,10 @@ public class RaceContext {
|
||||
}
|
||||
}
|
||||
ScoreCorrection scoreCorrection = leaderboard.getScoreCorrection();
|
||||
if(trackedRace == null && scoreCorrection != null && scoreCorrection.hasCorrectionForNonTrackedFleet(raceColumn)) {
|
||||
if (trackedRace == null && scoreCorrection != null && scoreCorrection.hasCorrectionForNonTrackedFleet(raceColumn)) {
|
||||
return RaceViewState.FINISHED;
|
||||
}
|
||||
if(startTime != null) {
|
||||
if (startTime != null) {
|
||||
return RaceViewState.RUNNING;
|
||||
}
|
||||
return RaceViewState.PLANNED;
|
||||
|
||||
Reference in new issue
Block a user