Merge remote-tracking branch 'origin/master'

This commit is contained in:
fmittag
2012-10-02 12:01:04 +02:00
6 changed files with 110 additions and 27 deletions
@@ -250,16 +250,19 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
}
List<Integer> ranksOfNonMedalStartedRaces = new ArrayList<Integer>();
TimePoint now = MillisecondsTimePoint.now();
int numberOfRacesToCompareToDiscardThresholds = 0;
for (RaceColumn column : raceColumnsInLeaderboard.values()) {
if (!column.isMedalRace() && column.getTrackedRace(defaultFleet) != null && column.getTrackedRace(defaultFleet).hasStarted(now)) {
ranksOfNonMedalStartedRaces.add(column.getTrackedRace(defaultFleet).getRank(competitor, now));
if (column.getTrackedRace(defaultFleet) != null && column.getTrackedRace(defaultFleet).hasStarted(now)) {
numberOfRacesToCompareToDiscardThresholds++;
if (!column.isMedalRace()) {
ranksOfNonMedalStartedRaces.add(column.getTrackedRace(defaultFleet).getRank(competitor, now));
}
}
}
Collections.sort(ranksOfNonMedalStartedRaces);
int carryInt = (carry == null ? 0 : carry);
int totalPoints = carryInt;
int medalRacePoints = getMedalRacePoints(competitor, now, defaultFleet);
int numberOfRacesFromWhichToDiscard = ranksOfNonMedalStartedRaces.size();
for (TrackedRace race : testRaces) {
RaceColumn raceColumn = raceColumnsInLeaderboard.get(race);
Pair<Competitor, RaceColumn> key = new Pair<Competitor, RaceColumn>(competitor, raceColumn);
@@ -274,8 +277,8 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
// One race is discarded because four races were started, and for [3-6) one race can be discarded.
// The discarded race is the worst of those started, so the one with rank 4.
int expectedNumberOfDiscardedRaces =
numberOfRacesFromWhichToDiscard < firstDiscardingThreshold ? 0 :
numberOfRacesFromWhichToDiscard < secondDiscardingThreshold ? 1 : 2;
numberOfRacesToCompareToDiscardThresholds < firstDiscardingThreshold ? 0 :
numberOfRacesToCompareToDiscardThresholds < secondDiscardingThreshold ? 1 : 2;
boolean discarded = ranksOfNonMedalStartedRaces.indexOf(rank) >= ranksOfNonMedalStartedRaces.size()-expectedNumberOfDiscardedRaces;
int expected = discarded ? 0 : rank==medalRacePoints?2*rank:rank;
assertEquals(expected, leaderboard.getTotalPoints(competitor, raceColumn, now), 0.000000001);
@@ -228,6 +228,90 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
assertEquals(47.0, leaderboard.getTotalPoints(competitors.get(9), later), 0.00000001);
}
/**
* 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 computing the number of discards.
*/
@Test
public void testDiscardsForUnorderedGroupsWithOneGroupNotHavingRacedInAColumn() throws NoWindException {
List<Competitor> competitors = createCompetitors(10);
Regatta regatta = createRegatta(/* qualifying */ 2, new String[] { "Yellow", "Blue" }, /* final */0,
new String[] { "Default" },
/* medal */false, "testDiscardsForUnorderedGroupsWithOneGroupNotHavingRacedInAColumn",
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[]{2}); // one discard for two or more races
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 there are no discards still 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);
for (Competitor competitor : competitors) {
assertFalse("Competitor "+competitor+" has a discard in Q1 but shouldn't", leaderboard.isDiscarded(competitor, q1Column, later));
assertFalse("Competitor "+competitor+" has a discard in Q2 but shouldn't", leaderboard.isDiscarded(competitor, q2Column, later));
}
// now add a tracked race for the blue fleet for Q2 and assert that all competitors have one discard
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);
for (Competitor competitor : competitors) {
assertTrue("Competitor "+competitor+" has no discard but should",
leaderboard.isDiscarded(competitor, q1Column, later) || leaderboard.isDiscarded(competitor, q2Column, later));
}
}
/**
* Regarding bug 961, test scoring in a leaderboard that has a qualification series with two ordered groups where for one
* column only one group has raced (expressed by a mocked TrackedRace attached to the column). Ensure that the competitors in
* that column get their discard.
*/
@Test
public void testDiscardsForOrderedGroupsWithOneGroupNotHavingRacedInAColumn() throws NoWindException {
List<Competitor> competitors = createCompetitors(10);
Regatta regatta = createRegatta(/* qualifying */ 0, new String[] { "Default" }, /* final */2,
new String[] { "Gold", "Silver" },
/* medal */false, "testDiscardsForOrderedGroupsWithOneGroupNotHavingRacedInAColumn",
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[]{2}); // one discard for two or more races
Series finalSeries;
Iterator<? extends Series> seriesIter = regatta.getSeries().iterator();
seriesIter.next();
finalSeries = seriesIter.next();
TimePoint now = MillisecondsTimePoint.now();
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
TrackedRace f1Gold = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(0, 5));
TrackedRace f1Silver = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(5, 10));
RaceColumn f1Column = finalSeries.getRaceColumnByName("F1");
f1Column.setTrackedRace(f1Column.getFleetByName("Gold"), f1Gold);
f1Column.setTrackedRace(f1Column.getFleetByName("Silver"), f1Silver);
// now add one race for yellow fleet and test that there are no discards still because blue fleet is still missing its race for Q2
TrackedRace f2Gold = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors.subList(0, 5));
RaceColumn f2Column = finalSeries.getRaceColumnByName("F2");
f2Column.setTrackedRace(f2Column.getFleetByName("Gold"), f2Gold);
for (int i=0; i<5; i++) {
assertTrue("Competitor "+competitors.get(i)+" has no discard in F1 or F2 but should",
leaderboard.isDiscarded(competitors.get(i), f1Column, later) ||
leaderboard.isDiscarded(competitors.get(i), f2Column, later));
}
for (int i=5; i<10; i++) {
assertFalse("Competitor "+competitors.get(i)+" has a discard in F1 or F2 but shouldn't",
leaderboard.isDiscarded(competitors.get(i), f1Column, later) ||
leaderboard.isDiscarded(competitors.get(i), f2Column, later));
}
}
/**
* Asserts that the competitors ranking worse than the disqualified competitor advance by one in the
* {@link Leaderboard#getCompetitorsFromBestToWorst(TimePoint)} ordering. Note that this does not test
@@ -238,7 +322,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
List<Competitor> competitors = createCompetitors(10);
Regatta regatta = createRegatta(/* qualifying */0, new String[] { "Default" }, /* final */1,
new String[] { "Default" },
/* medal */false, "testOneStartedRaceWithDifferentScores",
/* medal */false, "testOneStartedRaceWithDifferentScoresAndDisqualification",
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true), DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
Series finalSeries;
@@ -277,7 +361,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
}
Regatta regatta = createRegatta(/* qualifying */1, new String[] { "Yellow", "Blue" }, /* final */0,
new String[] { "Default" },
/* medal */false, "testAllTrackedAndStartedWithDifferentScores",
/* medal */false, "testDistributionAcrossQualifyingFleetsWithDifferentScores",
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true), DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
TimePoint now = MillisecondsTimePoint.now();
@@ -261,11 +261,13 @@ public interface Leaderboard extends Named {
* Tells if the column represented by <code>raceColumn</code> shall be considered when counting the number of "races
* so far" for discarding. Although medal races are never discarded themselves, they still count in determining the
* number of "races so far" which is then the basis for deciding how many races may be discarded. If a leaderboard
* has corrections for a column then that column shall be considered for discarding and counts for determining the
* number of races so far. Also, if a tracked race is connected to the column and has started already, the column is
* to be considered for discarding.
* has corrections for a column for the <code>competitor</code> then that column shall be considered for discarding
* and counts for determining the number of races so far. Also, if a tracked race is connected to the column and has
* started already, the column is to be considered for discarding unless the column has several unordered fleets and
* not all fleets have started their race yet (see
* {@link ScoringScheme#isValidInTotalScore(Leaderboard, RaceColumn, TimePoint)}).
*/
boolean considerForDiscarding(RaceColumn raceColumn, TimePoint timePoint);
boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint);
public void setResultDiscardingRule(ThresholdBasedResultDiscardingRule discardingRule);
@@ -94,7 +94,7 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
Fleet fleet = raceColumn.getFleetOfCompetitor(competitor);
if (fleetsForWhichNoScoreWasFound.contains(fleet)) {
try {
if (leaderboard.getTotalPoints(competitor, raceColumn, at) != null) {
if (leaderboard.getNetPoints(competitor, raceColumn, at) != null) {
fleetsForWhichNoScoreWasFound.remove(fleet);
}
} catch (NoWindException nwe) {
@@ -336,18 +336,12 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
}
@Override
public boolean considerForDiscarding(RaceColumn raceColumn, TimePoint timePoint) {
boolean result = getScoreCorrection().hasCorrectionFor(raceColumn);
if (!result && !raceColumn.isMedalRace()) {
for (Fleet fleet : raceColumn.getFleets()) {
TrackedRace trackedRace = raceColumn.getTrackedRace(fleet);
if (trackedRace != null && trackedRace.hasStarted(timePoint)) {
result = true;
break;
}
}
}
return result;
public boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
TrackedRace trackedRaceForCompetitorInColumn;
return getScoreCorrection().isScoreCorrected(competitor, raceColumn) ||
(getScoringScheme().isValidInTotalScore(this, raceColumn, timePoint) &&
(trackedRaceForCompetitorInColumn=raceColumn.getTrackedRace(competitor)) != null &&
trackedRaceForCompetitorInColumn.hasStarted(timePoint));
}
@Override
@@ -51,7 +51,7 @@ public class ResultDiscardingRuleImpl implements ThresholdBasedResultDiscardingR
@Override
public Set<RaceColumn> getDiscardedRaceColumns(final Competitor competitor, final Leaderboard leaderboard, final TimePoint timePoint) {
int resultsToDiscard = getNumberOfResultsToDiscard(leaderboard.getRaceColumns(), leaderboard, timePoint);
int resultsToDiscard = getNumberOfResultsToDiscard(competitor, leaderboard.getRaceColumns(), leaderboard, timePoint);
Set<RaceColumn> result;
if (resultsToDiscard > 0) {
result = new HashSet<RaceColumn>();
@@ -94,11 +94,11 @@ public class ResultDiscardingRuleImpl implements ThresholdBasedResultDiscardingR
return result;
}
private int getNumberOfResultsToDiscard(Iterable<RaceColumn> raceColumns, Leaderboard leaderboard, TimePoint timePoint) {
private int getNumberOfResultsToDiscard(Competitor competitor, Iterable<RaceColumn> raceColumns, Leaderboard leaderboard, TimePoint timePoint) {
int numberOfResultsToDiscard;
int numberOfStartedRaces = 0;
for (RaceColumn raceInLeaderboard : raceColumns) {
if (leaderboard.considerForDiscarding(raceInLeaderboard, timePoint)) {
if (leaderboard.countRaceForComparisonWithDiscardingThresholds(competitor, raceInLeaderboard, timePoint)) {
numberOfStartedRaces++;
}
}