mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-27 16:06:39 +00:00
introduced raceColumnsToConsider through the leaderboard scoring APIs to prepare for leaderboard rank chart
This commit is contained in:
+17
-1
@@ -98,6 +98,22 @@ public interface Leaderboard extends Named {
|
||||
|
||||
Entry getEntry(Competitor competitor, RaceColumn race, TimePoint timePoint) throws NoWindException;
|
||||
|
||||
/**
|
||||
* Computes the competitor's ranks as they were or would have been after each race column (from left to right)
|
||||
* was completed.<p>
|
||||
*
|
||||
* A leaderboard fills up over time, usually "from left to right" with one race after another finishing.
|
||||
* For split fleets things can vary slightly. There, one fleet may complete a few races before the another fleet
|
||||
* starts with those races. In this case there isn't even any point in time at which all fleets have finished
|
||||
* exactly <i>n</i> races. Still, this method pretends such a time point would have existed, actually ignoring
|
||||
* the <i>times</i> at which a race took place but only looking at the resulting scores and discard.<p>
|
||||
*
|
||||
* When computing the ranks after all columns up to and including the race column that is the key of the resulting
|
||||
* map, the method applies the discarding and tie breaking rules as they would have had to be applied had the races
|
||||
* in the respective column just completed.
|
||||
*/
|
||||
Map<RaceColumn, List<Competitor>> getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(TimePoint timePoint) throws NoWindException;
|
||||
|
||||
/**
|
||||
* Tells the number of points carried over from previous races not tracked by this leaderboard for
|
||||
* the <code>competitor</code>. Returns <code>0</code> if there is no carried points definition for
|
||||
@@ -201,7 +217,7 @@ public interface Leaderboard extends Named {
|
||||
* Fetches all entries for all competitors of all races tracked by this leaderboard in one sweep. This saves some
|
||||
* computational effort compared to fetching all entries separately, particularly because all
|
||||
* {@link #isDiscarded(Competitor, RaceColumn, TimePoint) discarded races} of a competitor are computed in one
|
||||
* sweep using {@link ResultDiscardingRule#getDiscardedRaceColumns(Competitor, Leaderboard, TimePoint)} only once.
|
||||
* sweep using {@link ResultDiscardingRule#getDiscardedRaceColumns(Competitor, Leaderboard, Iterable, TimePoint)} only once.
|
||||
* Note that in order to get the {@link #getTotalPoints(Competitor, TimePoint) total points} for a competitor
|
||||
* for the entire leaderboard, the {@link #getCarriedPoints(Competitor) carried-over points} need to be added.
|
||||
*/
|
||||
|
||||
+6
-1
@@ -16,5 +16,10 @@ import com.sap.sailing.domain.common.TimePoint;
|
||||
*
|
||||
*/
|
||||
public interface ResultDiscardingRule extends Serializable {
|
||||
Set<RaceColumn> getDiscardedRaceColumns(Competitor competitor, Leaderboard leaderboard, TimePoint timePoint);
|
||||
/**
|
||||
* @param raceColumnsToConsider if a column is <code>not</code> contained, its existence will be ignored for determining the
|
||||
* columns to discard. It affects the count of races. Only columns contained can be part of the result.
|
||||
*/
|
||||
Set<RaceColumn> getDiscardedRaceColumns(Competitor competitor, Leaderboard leaderboard,
|
||||
Iterable<RaceColumn> raceColumnsToConsider, TimePoint timePoint);
|
||||
}
|
||||
|
||||
+3
@@ -19,6 +19,9 @@ public interface SettableScoreCorrection extends ScoreCorrection {
|
||||
*/
|
||||
void setMaxPointsReason(Competitor competitor, RaceColumn raceColumn, MaxPointsReason reason);
|
||||
|
||||
/**
|
||||
* @return a non-<code>null</code> result
|
||||
*/
|
||||
MaxPointsReason getMaxPointsReason(Competitor competitor, RaceColumn raceColumn);
|
||||
|
||||
void correctScore(Competitor competitor, RaceColumn raceColumn, double points);
|
||||
|
||||
+29
-5
@@ -214,13 +214,22 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
|
||||
@Override
|
||||
public boolean isDiscarded(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
|
||||
return isDiscarded(competitor, raceColumn, getRaceColumns(), timePoint);
|
||||
}
|
||||
|
||||
private boolean isDiscarded(Competitor competitor, RaceColumn raceColumn, Iterable<RaceColumn> raceColumnsToConsider, TimePoint timePoint) {
|
||||
return !raceColumn.isMedalRace() && getMaxPointsReason(competitor, raceColumn, timePoint).isDiscardable()
|
||||
&& getResultDiscardingRule().getDiscardedRaceColumns(competitor, this, timePoint).contains(
|
||||
&& getResultDiscardingRule().getDiscardedRaceColumns(competitor, this, raceColumnsToConsider, timePoint).contains(
|
||||
raceColumn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getTotalPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) throws NoWindException {
|
||||
return getTotalPoints(competitor, raceColumn, getRaceColumns(), timePoint);
|
||||
}
|
||||
|
||||
private Double getTotalPoints(Competitor competitor, RaceColumn raceColumn,
|
||||
Iterable<RaceColumn> raceColumnsToConsider, TimePoint timePoint) throws NoWindException {
|
||||
Double result;
|
||||
if (isDiscarded(competitor, raceColumn, timePoint)) {
|
||||
result = 0.0;
|
||||
@@ -298,16 +307,20 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
|
||||
@Override
|
||||
public List<Competitor> getCompetitorsFromBestToWorst(TimePoint timePoint) throws NoWindException {
|
||||
return getCompetitorsFromBestToWorst(getRaceColumns(), timePoint);
|
||||
}
|
||||
|
||||
private List<Competitor> getCompetitorsFromBestToWorst(Iterable<RaceColumn> raceColumnsToConsider, TimePoint timePoint) throws NoWindException {
|
||||
List<Competitor> result = new ArrayList<Competitor>();
|
||||
for (Competitor competitor : getCompetitors()) {
|
||||
result.add(competitor);
|
||||
}
|
||||
Collections.sort(result, getTotalRankComparator(timePoint));
|
||||
Collections.sort(result, getTotalRankComparator(raceColumnsToConsider, timePoint));
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Comparator<? super Competitor> getTotalRankComparator(TimePoint timePoint) throws NoWindException {
|
||||
return new LeaderboardTotalRankComparator(this, timePoint, getScoringScheme(), /* nullScoresAreBetter */ false);
|
||||
protected Comparator<? super Competitor> getTotalRankComparator(Iterable<RaceColumn> raceColumnsToConsider, TimePoint timePoint) throws NoWindException {
|
||||
return new LeaderboardTotalRankComparator(this, timePoint, getScoringScheme(), /* nullScoresAreBetter */ false, raceColumnsToConsider);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -368,6 +381,17 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
correctedResults.getMaxPointsReason(), discarded, race.getFleetOfCompetitor(competitor));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<RaceColumn, List<Competitor>> getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(TimePoint timePoint) throws NoWindException {
|
||||
Map<RaceColumn, List<Competitor>> result = new HashMap<>();
|
||||
List<RaceColumn> raceColumnsToConsider = new ArrayList<>();
|
||||
for (RaceColumn raceColumn : getRaceColumns()) {
|
||||
raceColumnsToConsider.add(raceColumn);
|
||||
result.put(raceColumn, getCompetitorsFromBestToWorst(raceColumnsToConsider, timePoint));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Pair<Competitor, RaceColumn>, Entry> getContent(final TimePoint timePoint) throws NoWindException {
|
||||
Map<Pair<Competitor, RaceColumn>, Entry> result = new HashMap<Pair<Competitor, RaceColumn>, Entry>();
|
||||
@@ -384,7 +408,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
timePoint, Util.size(getCompetitors()), getScoringScheme());
|
||||
Set<RaceColumn> discardedRacesForCompetitor = discardedRaces.get(competitor);
|
||||
if (discardedRacesForCompetitor == null) {
|
||||
discardedRacesForCompetitor = getResultDiscardingRule().getDiscardedRaceColumns(competitor, this, timePoint);
|
||||
discardedRacesForCompetitor = getResultDiscardingRule().getDiscardedRaceColumns(competitor, this, getRaceColumns(), timePoint);
|
||||
discardedRaces.put(competitor, discardedRacesForCompetitor);
|
||||
}
|
||||
boolean discarded = discardedRacesForCompetitor.contains(raceColumn);
|
||||
|
||||
+20
-1
@@ -52,8 +52,27 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
private final boolean nullScoresAreBetter;
|
||||
private final TimePoint timePoint;
|
||||
|
||||
/**
|
||||
* Considers all of the leaderboard's columns in their state at <code>timePoint</code> for calculating the score and rank.
|
||||
*/
|
||||
public LeaderboardTotalRankComparator(Leaderboard leaderboard, TimePoint timePoint,
|
||||
ScoringScheme scoringScheme, boolean nullScoresAreBetter) throws NoWindException {
|
||||
this(leaderboard, timePoint, scoringScheme, nullScoresAreBetter, leaderboard.getRaceColumns());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* For example, if the first race may be discarded when five races have been completed, and only four {@link RaceColumn}s are
|
||||
* considered, no race's score will be discarded for this call. This allows clients to tell what the ranking would have been
|
||||
* with only the race columns specified in <code>raceColumnsToConsider</code> having completed for all fleets.<p>
|
||||
*
|
||||
* Note, that <code>timePoint</code> is considered in addition to <code>raceColumnsToConsider</code> such that the scores in
|
||||
* those columns considered is computed for the <code>timePoint</code> specified. In particular, if a time point is chosen that
|
||||
* is before a race in a column that is considered has started, <code>null</code> values may result in that column.
|
||||
*/
|
||||
public LeaderboardTotalRankComparator(Leaderboard leaderboard, TimePoint timePoint,
|
||||
ScoringScheme scoringScheme, boolean nullScoresAreBetter, Iterable<RaceColumn> raceColumnsToConsider) throws NoWindException {
|
||||
super();
|
||||
this.leaderboard = leaderboard;
|
||||
this.timePoint = timePoint;
|
||||
@@ -61,7 +80,7 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
this.nullScoresAreBetter = nullScoresAreBetter;
|
||||
totalPointsCache = new HashMap<Pair<Competitor, RaceColumn>, Double>();
|
||||
for (Competitor competitor : leaderboard.getCompetitors()) {
|
||||
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
|
||||
for (RaceColumn raceColumn : raceColumnsToConsider) {
|
||||
totalPointsCache.put(new Pair<Competitor, RaceColumn>(competitor, raceColumn),
|
||||
leaderboard.getTotalPoints(competitor, raceColumn, timePoint));
|
||||
}
|
||||
|
||||
+7
-5
@@ -50,8 +50,9 @@ public class ResultDiscardingRuleImpl implements ThresholdBasedResultDiscardingR
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RaceColumn> getDiscardedRaceColumns(final Competitor competitor, final Leaderboard leaderboard, final TimePoint timePoint) {
|
||||
int resultsToDiscard = getNumberOfResultsToDiscard(competitor, leaderboard.getRaceColumns(), leaderboard, timePoint);
|
||||
public Set<RaceColumn> getDiscardedRaceColumns(final Competitor competitor, final Leaderboard leaderboard,
|
||||
Iterable<RaceColumn> raceColumnsToConsider, final TimePoint timePoint) {
|
||||
int resultsToDiscard = getNumberOfResultsToDiscard(competitor, raceColumnsToConsider, leaderboard, timePoint);
|
||||
Set<RaceColumn> result;
|
||||
if (resultsToDiscard > 0) {
|
||||
result = new HashSet<RaceColumn>();
|
||||
@@ -72,7 +73,7 @@ public class ResultDiscardingRuleImpl implements ThresholdBasedResultDiscardingR
|
||||
}
|
||||
}
|
||||
};
|
||||
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
|
||||
for (RaceColumn raceColumn : raceColumnsToConsider) {
|
||||
if (!raceColumn.isMedalRace()) {
|
||||
sortedRaces.add(raceColumn);
|
||||
}
|
||||
@@ -94,10 +95,11 @@ public class ResultDiscardingRuleImpl implements ThresholdBasedResultDiscardingR
|
||||
return result;
|
||||
}
|
||||
|
||||
private int getNumberOfResultsToDiscard(Competitor competitor, Iterable<RaceColumn> raceColumns, Leaderboard leaderboard, TimePoint timePoint) {
|
||||
private int getNumberOfResultsToDiscard(Competitor competitor, Iterable<RaceColumn> raceColumnsToConsider,
|
||||
Leaderboard leaderboard, TimePoint timePoint) {
|
||||
int numberOfResultsToDiscard;
|
||||
int numberOfStartedRaces = 0;
|
||||
for (RaceColumn raceInLeaderboard : raceColumns) {
|
||||
for (RaceColumn raceInLeaderboard : raceColumnsToConsider) {
|
||||
if (leaderboard.countRaceForComparisonWithDiscardingThresholds(competitor, raceInLeaderboard, timePoint)) {
|
||||
numberOfStartedRaces++;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user