mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-25 06:58:39 +00:00
bug5877: interims commit, using distinct medal *series* for quarter/semi/grand final
This commit is contained in:
+110
-12
@@ -1,39 +1,137 @@
|
||||
package com.sap.sailing.domain.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.RaceColumnInSeries;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.Series;
|
||||
import com.sap.sailing.domain.leaderboard.impl.LowPointWithEliminatingMedalSeriesPromotingOneToFinalAndTwoToSemifinal;
|
||||
|
||||
public class LowPointWithEliminatingMedalSeriesPromotingOneToFinalAndTwoToSemifinalTest {
|
||||
private Regatta regatta;
|
||||
private LowPointWithEliminatingMedalSeriesPromotingOneToFinalAndTwoToSemifinal scoringScheme;
|
||||
private RaceColumnInSeries quarterFinal;
|
||||
private Series quarterFinalSeries;
|
||||
private RaceColumnInSeries semiFinal;
|
||||
private Series semiFinalSeries;
|
||||
private RaceColumnInSeries grandFinal;
|
||||
private Series grandFinalSeries;
|
||||
private HashMap<Competitor, Integer> competitorsRankedByOpeningSeries;
|
||||
private Competitor c1, c2, c3, c4, c5;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
regatta = mock(Regatta.class);
|
||||
scoringScheme = new LowPointWithEliminatingMedalSeriesPromotingOneToFinalAndTwoToSemifinal();
|
||||
quarterFinal = mock(RaceColumnInSeries.class);
|
||||
quarterFinalSeries = mockMedalSeries(regatta, quarterFinal);
|
||||
semiFinal = mock(RaceColumnInSeries.class);
|
||||
semiFinalSeries = mockMedalSeries(regatta, semiFinal);
|
||||
grandFinal = mock(RaceColumnInSeries.class);
|
||||
grandFinalSeries = mockMedalSeries(regatta, grandFinal);
|
||||
when(regatta.getSeries()).thenAnswer(new Answer<Iterable<Series>>() {
|
||||
@Override
|
||||
public Iterable<Series> answer(InvocationOnMock invocation) throws Throwable {
|
||||
return Arrays.asList(quarterFinalSeries, semiFinalSeries, grandFinalSeries);
|
||||
}
|
||||
});
|
||||
when(quarterFinal.getSeries()).thenReturn(quarterFinalSeries);
|
||||
when(semiFinal.getSeries()).thenReturn(semiFinalSeries);
|
||||
when(grandFinal.getSeries()).thenReturn(grandFinalSeries);
|
||||
c1 = mock(Competitor.class);
|
||||
c2 = mock(Competitor.class);
|
||||
c3 = mock(Competitor.class);
|
||||
c4 = mock(Competitor.class);
|
||||
c5 = mock(Competitor.class);
|
||||
competitorsRankedByOpeningSeries = new HashMap<>();
|
||||
competitorsRankedByOpeningSeries.put(c1, 1);
|
||||
competitorsRankedByOpeningSeries.put(c2, 2);
|
||||
competitorsRankedByOpeningSeries.put(c3, 3);
|
||||
competitorsRankedByOpeningSeries.put(c4, 4);
|
||||
competitorsRankedByOpeningSeries.put(c5, 5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCountingPromotedCompetitors() {
|
||||
final LowPointWithEliminatingMedalSeriesPromotingOneToFinalAndTwoToSemifinal scoringScheme = new LowPointWithEliminatingMedalSeriesPromotingOneToFinalAndTwoToSemifinal();
|
||||
final RaceColumnInSeries quarterFinal = mock(RaceColumnInSeries.class);
|
||||
final RaceColumnInSeries semiFinal = mock(RaceColumnInSeries.class);
|
||||
final RaceColumnInSeries grandFinal = mock(RaceColumnInSeries.class);
|
||||
public void testCountingCompetitorsBetterThanThoseSailingInSeries() {
|
||||
assertEquals(0, scoringScheme.getNumberOfCompetitorsBetterThanThoseSailingInSeries(grandFinalSeries));
|
||||
assertEquals(1, scoringScheme.getNumberOfCompetitorsBetterThanThoseSailingInSeries(semiFinalSeries));
|
||||
assertEquals(3, scoringScheme.getNumberOfCompetitorsBetterThanThoseSailingInSeries(quarterFinalSeries));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCountingCompetitorsAdvancingFromOpeningSeries() {
|
||||
assertEquals(1, scoringScheme.getNumberOfCompetitorsAdvancingFromOpeningSeriesToOrThroughSeries(grandFinalSeries));
|
||||
assertEquals(3, scoringScheme.getNumberOfCompetitorsAdvancingFromOpeningSeriesToOrThroughSeries(semiFinalSeries));
|
||||
assertEquals(3, scoringScheme.getNumberOfCompetitorsAdvancingFromOpeningSeriesToOrThroughSeries(quarterFinalSeries));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuarterFinalParticipationForPromotedCompetitors() {
|
||||
assertTrue(scoringScheme.isParticipatingInMedalRace(c1, /* competitorMedalRaceScore */ null, quarterFinal, ()->competitorsRankedByOpeningSeries));
|
||||
assertTrue(scoringScheme.isParticipatingInMedalRace(c2, /* competitorMedalRaceScore */ null, quarterFinal, ()->competitorsRankedByOpeningSeries));
|
||||
assertTrue(scoringScheme.isParticipatingInMedalRace(c3, /* competitorMedalRaceScore */ null, quarterFinal, ()->competitorsRankedByOpeningSeries));
|
||||
assertFalse(scoringScheme.isParticipatingInMedalRace(c4, /* competitorMedalRaceScore */ null, quarterFinal, ()->competitorsRankedByOpeningSeries));
|
||||
assertFalse(scoringScheme.isParticipatingInMedalRace(c5, /* competitorMedalRaceScore */ null, quarterFinal, ()->competitorsRankedByOpeningSeries));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSemiFinalParticipationForPromotedCompetitors() {
|
||||
assertTrue(scoringScheme.isParticipatingInMedalRace(c1, /* competitorMedalRaceScore */ null, semiFinal, ()->competitorsRankedByOpeningSeries));
|
||||
assertTrue(scoringScheme.isParticipatingInMedalRace(c2, /* competitorMedalRaceScore */ null, semiFinal, ()->competitorsRankedByOpeningSeries));
|
||||
assertTrue(scoringScheme.isParticipatingInMedalRace(c3, /* competitorMedalRaceScore */ null, semiFinal, ()->competitorsRankedByOpeningSeries));
|
||||
assertFalse(scoringScheme.isParticipatingInMedalRace(c4, /* competitorMedalRaceScore */ null, semiFinal, ()->competitorsRankedByOpeningSeries));
|
||||
assertFalse(scoringScheme.isParticipatingInMedalRace(c5, /* competitorMedalRaceScore */ null, semiFinal, ()->competitorsRankedByOpeningSeries));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGrandFinalParticipationForPromotedCompetitors() {
|
||||
assertTrue(scoringScheme.isParticipatingInMedalRace(c1, /* competitorMedalRaceScore */ null, grandFinal, ()->competitorsRankedByOpeningSeries));
|
||||
assertFalse(scoringScheme.isParticipatingInMedalRace(c2, /* competitorMedalRaceScore */ null, grandFinal, ()->competitorsRankedByOpeningSeries));
|
||||
assertFalse(scoringScheme.isParticipatingInMedalRace(c3, /* competitorMedalRaceScore */ null, grandFinal, ()->competitorsRankedByOpeningSeries));
|
||||
assertFalse(scoringScheme.isParticipatingInMedalRace(c4, /* competitorMedalRaceScore */ null, grandFinal, ()->competitorsRankedByOpeningSeries));
|
||||
assertFalse(scoringScheme.isParticipatingInMedalRace(c5, /* competitorMedalRaceScore */ null, grandFinal, ()->competitorsRankedByOpeningSeries));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGrandFinalParticipationForScoringCompetitors() {
|
||||
assertTrue(scoringScheme.isParticipatingInMedalRace(c1, /* competitorMedalRaceScore */ 3.0, grandFinal, ()->competitorsRankedByOpeningSeries));
|
||||
assertFalse(scoringScheme.isParticipatingInMedalRace(c2, /* competitorMedalRaceScore */ null, grandFinal, ()->competitorsRankedByOpeningSeries));
|
||||
assertTrue(scoringScheme.isParticipatingInMedalRace(c3, /* competitorMedalRaceScore */ 1.0, grandFinal, ()->competitorsRankedByOpeningSeries));
|
||||
assertTrue(scoringScheme.isParticipatingInMedalRace(c4, /* competitorMedalRaceScore */ 2.0, grandFinal, ()->competitorsRankedByOpeningSeries));
|
||||
assertFalse(scoringScheme.isParticipatingInMedalRace(c5, /* competitorMedalRaceScore */ null, grandFinal, ()->competitorsRankedByOpeningSeries));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMedalRaceParticipationForScoringCompetitors() {
|
||||
assertTrue(scoringScheme.isParticipatingInMedalRace(c1, /* competitorMedalRaceScore */ 4.0, quarterFinal, ()->competitorsRankedByOpeningSeries));
|
||||
assertTrue(scoringScheme.isParticipatingInMedalRace(c4, /* competitorMedalRaceScore */ 2.0, quarterFinal, ()->competitorsRankedByOpeningSeries));
|
||||
assertTrue(scoringScheme.isParticipatingInMedalRace(c5, /* competitorMedalRaceScore */ 3.0, quarterFinal, ()->competitorsRankedByOpeningSeries));
|
||||
}
|
||||
|
||||
private Series mockMedalSeries(Regatta regatta, RaceColumnInSeries... raceColumns) {
|
||||
final Iterable<RaceColumnInSeries> medalSeriesColumns = Arrays.asList(raceColumns);
|
||||
final Series medalSeries = mock(Series.class);
|
||||
final Iterable<RaceColumnInSeries> medalSeriesColumns = Arrays.asList(quarterFinal, semiFinal, grandFinal);
|
||||
when(medalSeries.getRegatta()).thenReturn(regatta);
|
||||
when(medalSeries.isMedal()).thenReturn(true);
|
||||
when(medalSeries.getRaceColumns()).thenAnswer(new Answer<Iterable<RaceColumnInSeries>>() {
|
||||
@Override
|
||||
public Iterable<RaceColumnInSeries> answer(InvocationOnMock invocation) throws Throwable {
|
||||
return medalSeriesColumns;
|
||||
}
|
||||
});
|
||||
when(quarterFinal.getSeries()).thenReturn(medalSeries);
|
||||
when(semiFinal.getSeries()).thenReturn(medalSeries);
|
||||
when(grandFinal.getSeries()).thenReturn(medalSeries);
|
||||
assertEquals(0, scoringScheme.getNumberOfCompetitorsBetterThanThoseSailingInRace(grandFinal));
|
||||
assertEquals(1, scoringScheme.getNumberOfCompetitorsBetterThanThoseSailingInRace(semiFinal));
|
||||
assertEquals(3, scoringScheme.getNumberOfCompetitorsBetterThanThoseSailingInRace(quarterFinal));
|
||||
return medalSeries;
|
||||
}
|
||||
}
|
||||
|
||||
+17
-1
@@ -13,12 +13,14 @@ import java.util.function.Supplier;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.base.RaceColumnInSeries;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.Series;
|
||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.leaderboard.impl.AbstractSimpleLeaderboardImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.LeaderboardTotalRankComparator;
|
||||
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
|
||||
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
@@ -121,7 +123,8 @@ public interface ScoringScheme extends Serializable {
|
||||
* With a lesser result encoding "better" the direction of default integer comparison between the two parameters is
|
||||
* reversed.
|
||||
*/
|
||||
default int compareByMedalRaceParticipation(int zeroBasedIndexOfLastMedalSeriesInWhichO1Scored, int zeroBasedIndexOfLastMedalSeriesInWhichO2Scored) {
|
||||
default int compareByMedalRaceParticipation(int zeroBasedIndexOfLastMedalSeriesInWhichO1Scored,
|
||||
int zeroBasedIndexOfLastMedalSeriesInWhichO2Scored) {
|
||||
return -Integer.compare(zeroBasedIndexOfLastMedalSeriesInWhichO1Scored, zeroBasedIndexOfLastMedalSeriesInWhichO2Scored);
|
||||
}
|
||||
|
||||
@@ -373,4 +376,17 @@ public interface ScoringScheme extends Serializable {
|
||||
BiFunction<Competitor, RaceColumn, Double> totalPointsSupplier, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache, TimePoint timePoint, int zeroBasedIndexOfLastMedalSeriesInWhichBothScored, int numberOfMedalRacesWonO1, int numberOfMedalRacesWonO2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
LeaderboardTotalRankComparator getOpeningSeriesRankComparator(boolean nullScoresAreBetter, TimePoint timePoint,
|
||||
Leaderboard leaderboard, BiFunction<Competitor, RaceColumn, Double> totalPointsSupplier,
|
||||
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache);
|
||||
|
||||
/**
|
||||
* This default implementation decides competitor participation in a medal race by having scored a non-{@code null}
|
||||
* score.
|
||||
*/
|
||||
default boolean isParticipatingInMedalRace(Competitor competitor, Double competitorMedalRaceScore,
|
||||
RaceColumnInSeries medalRaceColumn, Supplier<Map<Competitor, Integer>> competitorsRankedByOpeningSeries) {
|
||||
return competitorMedalRaceScore != null;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -183,7 +183,8 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
|
||||
return Util.filter(leaderboard.getRaceColumns(), rc->!rc.isMedalRace());
|
||||
}
|
||||
|
||||
protected LeaderboardTotalRankComparator getOpeningSeriesRankComparator(boolean nullScoresAreBetter,
|
||||
@Override
|
||||
public LeaderboardTotalRankComparator getOpeningSeriesRankComparator(boolean nullScoresAreBetter,
|
||||
TimePoint timePoint, Leaderboard leaderboard,
|
||||
BiFunction<Competitor, RaceColumn, Double> totalPointsSupplier,
|
||||
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
|
||||
|
||||
+38
-2
@@ -8,6 +8,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Fleet;
|
||||
@@ -62,6 +63,26 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
private final TimePoint timePoint;
|
||||
private final WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache;
|
||||
|
||||
/**
|
||||
* An on-demand map of competitors with their opening series rank, 1-based.<p>
|
||||
*
|
||||
* Some scoring schemes that support medal series may need to break ties or determine promotions to
|
||||
* later rounds/stages based on the "opening series" results. The opening series then is considered
|
||||
* to contain all races excluding {@link RaceColumn#isMedalRace() medal races}.<p>
|
||||
*
|
||||
* When using this comparator in multiple comparisons, the opening series ranking may then be required
|
||||
* in many of these comparisons. It would be unnecessarily expensive to compute that ranking upon each
|
||||
* such comparison. Instead, once requested by this comparator, it is cached and can be re-used by later
|
||||
* comparisons performed by this comparator's {@link #compare(Competitor, Competitor)} method.<p>
|
||||
*
|
||||
* The implementation of the {@link Supplier} synchronizes on itself when {@link Supplier#get() asked}
|
||||
* for its value. If the opening series ranking has not been computed yet then this happens while
|
||||
* holding the supplier's monitor locked. This way, other threads waiting for the opening series
|
||||
* ranking will wait for the computation to complete. If the ranking has already been computed it
|
||||
* is returned immediately and the monitor is released quickly.
|
||||
*/
|
||||
private final Supplier<Map<Competitor, Integer>> competitorsRankedByOpeningSeries;
|
||||
|
||||
/**
|
||||
* Considers only the race columns specified in <code>raceColumnsToConsider</code> and behaves as if the other columns
|
||||
* were filled with <code>null</code> values. Those columns not considered do not count for determining the discards either.
|
||||
@@ -114,6 +135,21 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
totalPointsCache.put(key, totalPoints);
|
||||
}
|
||||
}
|
||||
competitorsRankedByOpeningSeries = new Supplier<Map<Competitor, Integer>>() {
|
||||
private volatile Map<Competitor, Integer> openingSeriesRanking = null;
|
||||
@Override
|
||||
public synchronized Map<Competitor, Integer> get() {
|
||||
if (openingSeriesRanking == null) {
|
||||
final List<Competitor> result = Util.asList(leaderboard.getCompetitors());
|
||||
Collections.sort(result, scoringScheme.getOpeningSeriesRankComparator(nullScoresAreBetter, timePoint, leaderboard, totalPointsSupplier, cache));
|
||||
openingSeriesRanking = new HashMap<>();
|
||||
for (int i=0; i<result.size(); i++) {
|
||||
openingSeriesRanking.put(result.get(i), i+1);
|
||||
}
|
||||
}
|
||||
return openingSeriesRanking;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected Leaderboard getLeaderboard() {
|
||||
@@ -208,10 +244,10 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
o1CarryForwardScoreInMedals = o1Score;
|
||||
o2CarryForwardScoreInMedals = o2Score;
|
||||
}
|
||||
if (o1Score != null && raceColumn instanceof RaceColumnInSeries) {
|
||||
if (raceColumn instanceof RaceColumnInSeries && scoringScheme.isParticipatingInMedalRace(o1, o1Score, (RaceColumnInSeries) raceColumn, competitorsRankedByOpeningSeries)) {
|
||||
zeroBasedIndexOfLastMedalSeriesInWhichO1Scored = getZeroBasedIndexOfSeries((RaceColumnInSeries) raceColumn);
|
||||
}
|
||||
if (o2Score != null && raceColumn instanceof RaceColumnInSeries) {
|
||||
if (raceColumn instanceof RaceColumnInSeries && scoringScheme.isParticipatingInMedalRace(o2, o2Score, (RaceColumnInSeries) raceColumn, competitorsRankedByOpeningSeries)) {
|
||||
zeroBasedIndexOfLastMedalSeriesInWhichO2Scored = getZeroBasedIndexOfSeries((RaceColumnInSeries) raceColumn);
|
||||
}
|
||||
// similar to compareByFleet, however, tracking is not required; having medal race column points
|
||||
|
||||
+67
-15
@@ -2,11 +2,29 @@ package com.sap.sailing.domain.leaderboard.impl;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.RaceColumnInSeries;
|
||||
import com.sap.sailing.domain.base.Series;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
/**
|
||||
* A special low-point scoring scheme that uses one or more medal series to eliminate some competitors
|
||||
* during each such medal series. Competitors may be promoted to later series based on their opening
|
||||
* series results. For example, the scheme may define that the winner of the opening series automatically
|
||||
* advances to the final medal series, and the second and third ranking competitors advance to the last-but-one
|
||||
* medal series already; all others have to race a medal race/series to qualify for the next series, and
|
||||
* some of those will be eliminated on the way.<p>
|
||||
*
|
||||
* The number of competitors from the opening series that qualify to later medal series is provided as
|
||||
* an array to the {@link #LowPointWithEliminatingMedalSeriesWithPromotions(int[]) constructor}.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public abstract class LowPointWithEliminatingMedalSeriesWithPromotions extends LowPoint {
|
||||
private static final long serialVersionUID = 7759999270911627798L;
|
||||
|
||||
@@ -22,14 +40,17 @@ public abstract class LowPointWithEliminatingMedalSeriesWithPromotions extends L
|
||||
/**
|
||||
*
|
||||
* @param numberOfPromotedCompetitorsIntoLastMedalRaces
|
||||
* The last index for this array refers to the last medal race; e.g., the "Grand Final" race in an iQFOil
|
||||
* regatta with the medal series consisting of a "Quarter Final," a "Semi Final" and a "Grand Final". The
|
||||
* last-but-one index in the example would then refer to the Semi Final, and so on. The array may be
|
||||
* The last index for this array refers to the last medal series; e.g., the "Grand Final" race in an iQFOil
|
||||
* regatta with three medal series ("Quarter Final," "Semi Final" and "Grand Final"). The
|
||||
* last-but-one index in the example would then refer to the Semi Final series, and so on. The array may be
|
||||
* empty, meaning that no competitor is promoted into any medal race. The field always has to refer to a
|
||||
* valid array.
|
||||
*/
|
||||
public LowPointWithEliminatingMedalSeriesWithPromotions(int[] numberOfPromotedCompetitorsIntoLastMedalRaces) {
|
||||
super();
|
||||
if (numberOfPromotedCompetitorsIntoLastMedalRaces == null) {
|
||||
throw new NullPointerException("array specifying number of promoted competitors must not be null");
|
||||
}
|
||||
this.numberOfPromotedCompetitorsIntoLastMedalRaces = numberOfPromotedCompetitorsIntoLastMedalRaces;
|
||||
}
|
||||
|
||||
@@ -38,26 +59,57 @@ public abstract class LowPointWithEliminatingMedalSeriesWithPromotions extends L
|
||||
return super.getType();
|
||||
}
|
||||
|
||||
public int getNumberOfCompetitorsBetterThanThoseSailingInRace(RaceColumnInSeries medalRace) {
|
||||
final List<? extends RaceColumnInSeries> allMedalRacesInSeries = Util.asList(medalRace.getSeries().getRaceColumns());
|
||||
/**
|
||||
* Counts the number of competitors in a medal series that, without sailing in it, will be ranked better
|
||||
* than those that do sail in it. This is because these many competitors have already advanced to later
|
||||
* medal series based on their opening series rank.
|
||||
*/
|
||||
public int getNumberOfCompetitorsBetterThanThoseSailingInSeries(Series medalSeries) {
|
||||
final List<? extends Series> allMedalSeries = Util.asList(Util.filter(medalSeries.getRegatta().getSeries(), series->series.isMedal()));
|
||||
int result = 0;
|
||||
int indexInNumberOfPromotedCompetitorsIntoLastMedalRaces = numberOfPromotedCompetitorsIntoLastMedalRaces.length-1;
|
||||
if (allMedalRacesInSeries.size() > 1) {
|
||||
for (final ListIterator<? extends RaceColumnInSeries> i=allMedalRacesInSeries.listIterator(allMedalRacesInSeries.size()); i.hasPrevious() && i.previous() != medalRace; ) {
|
||||
if (allMedalSeries.size() > 1) {
|
||||
for (final ListIterator<? extends Series> i=allMedalSeries.listIterator(allMedalSeries.size()); i.hasPrevious() && i.previous() != medalSeries; ) {
|
||||
result += numberOfPromotedCompetitorsIntoLastMedalRaces[indexInNumberOfPromotedCompetitorsIntoLastMedalRaces--];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getNumberOfCompetitorsAdvancingFromOpeningSeriesToOrThroughSeries(Series medalSeries) {
|
||||
final List<? extends Series> allMedalSeries = Util.asList(Util.filter(medalSeries.getRegatta().getSeries(), series->series.isMedal()));
|
||||
int result = 0;
|
||||
if (numberOfPromotedCompetitorsIntoLastMedalRaces.length > 0) {
|
||||
int indexInNumberOfPromotedCompetitorsIntoLastMedalRaces = numberOfPromotedCompetitorsIntoLastMedalRaces.length;
|
||||
if (!allMedalSeries.isEmpty()) {
|
||||
result = numberOfPromotedCompetitorsIntoLastMedalRaces[--indexInNumberOfPromotedCompetitorsIntoLastMedalRaces];
|
||||
for (final ListIterator<? extends Series> i=allMedalSeries.listIterator(allMedalSeries.size());
|
||||
indexInNumberOfPromotedCompetitorsIntoLastMedalRaces > 0 && i.hasPrevious() && i.previous() != medalSeries; ) {
|
||||
result += numberOfPromotedCompetitorsIntoLastMedalRaces[--indexInNumberOfPromotedCompetitorsIntoLastMedalRaces];
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* In addition to the default implementation (assumed to check for a non-{@code null} medal race score}, this
|
||||
* specialized implementation is aware of the promotion scheme and defines competitors as "participants" of the
|
||||
* medal race represented by {@code medalRaceColumn} if their opening series rank was
|
||||
* {@link #getNumberOfCompetitorsBetterThanThoseSailingInSeries(RaceColumnInSeries) good enough} to have been promoted
|
||||
* to {@code medalRaceColumn}'s or a later medal series already. In this case it is not necessary for the competitor
|
||||
* to have <em>scored</em> in {@code medalRaceColumn} yet.<p>
|
||||
*
|
||||
* Note: Since the promotion decision is made based on the opening series rank, promotion is considered even if
|
||||
* more opening series races are to come. This, however, will not change the results because the promotion scheme
|
||||
* will keep the rank ordering among those promoted.
|
||||
*/
|
||||
@Override
|
||||
public int compareByMedalRaceParticipation(int zeroBasedIndexOfLastMedalSeriesInWhichO1Scored,
|
||||
int zeroBasedIndexOfLastMedalSeriesInWhichO2Scored) {
|
||||
// TODO We'd like to consider those as "having participated" in a medal race that may not have a score but have been promoted already to a later medal race based on their opening series result;
|
||||
// For that we need to check all medal race columns and need to know the two competitors.
|
||||
// If promoted to a later medal race but not sailed in the one currently considered, consider as "has participated."
|
||||
// If both participated, move on to next medal race column, if any. If last medal race column, return 0.
|
||||
// If one competitor is considered as "has participated" and the other one not, prefer the one that "has participated" over the one that has not.
|
||||
return super.compareByMedalRaceParticipation(zeroBasedIndexOfLastMedalSeriesInWhichO1Scored, zeroBasedIndexOfLastMedalSeriesInWhichO2Scored);
|
||||
public boolean isParticipatingInMedalRace(Competitor competitor, Double competitorMedalRaceScore,
|
||||
RaceColumnInSeries medalRaceColumn, Supplier<Map<Competitor, Integer>> competitorsRankedByOpeningSeries) {
|
||||
final Integer openingSeriesRank;
|
||||
return super.isParticipatingInMedalRace(competitor, competitorMedalRaceScore, medalRaceColumn, competitorsRankedByOpeningSeries) ||
|
||||
((openingSeriesRank = competitorsRankedByOpeningSeries.get().get(competitor)) != null &&
|
||||
openingSeriesRank <= getNumberOfCompetitorsAdvancingFromOpeningSeriesToOrThroughSeries(medalRaceColumn.getSeries()));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user