Fix in Chooser and different penalties for missing waypoints in the middle or et the end of races => Races missing starttimes work ok.

This commit is contained in:
Nick Klose committed 2014-02-04 16:00:02 +01:00
1 parent be3a144e43
commit 851f002b9a
7 files changed
+109 -47

No files matched your search

@@ -19,6 +19,7 @@ import junit.framework.Assert;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util.Pair; import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.markpassingcalculation.Candidate; import com.sap.sailing.domain.markpassingcalculation.Candidate;
import com.sap.sailing.domain.markpassingcalculation.CandidateChooser; import com.sap.sailing.domain.markpassingcalculation.CandidateChooser;
@@ -73,8 +74,8 @@ public class AbstractMarkPassingTest extends OnlineTracTracBasedTest {
int incorrectPasses = 0; int incorrectPasses = 0;
boolean printRight = false; boolean printRight = false;
boolean printWrong = true; boolean printWrong = false;
boolean printResult = true; boolean printResult = false;
for (Competitor c : getRace().getCompetitors()) { for (Competitor c : getRace().getCompetitors()) {
numberOfCompetitors++; numberOfCompetitors++;
@@ -100,8 +101,7 @@ public class AbstractMarkPassingTest extends OnlineTracTracBasedTest {
System.out.println("Both null" + "\n"); System.out.println("Both null" + "\n");
} }
} else { } else {
long timedelta = givenPasses.get(c).get(w).getTimePoint().asMillis() long timedelta = givenPasses.get(c).get(w).getTimePoint().asMillis() - computedPasses.get(c).get(w).getTimePoint().asMillis();
- computedPasses.get(c).get(w).getTimePoint().asMillis();
if ((Math.abs(timedelta) < tolerance)) { if ((Math.abs(timedelta) < tolerance)) {
correctPasses++; correctPasses++;
if (printRight) { if (printRight) {
@@ -124,8 +124,7 @@ public class AbstractMarkPassingTest extends OnlineTracTracBasedTest {
} }
int totalMarkPasses = numberOfCompetitors * waypoints.size(); int totalMarkPasses = numberOfCompetitors * waypoints.size();
assertEquals(totalMarkPasses, incorrectPasses + correctPasses + wronglyNotComputed + correctlyNotComputed assertEquals(totalMarkPasses, incorrectPasses + correctPasses + wronglyNotComputed + correctlyNotComputed + wronglyComputed);
+ wronglyComputed);
double accuracy = (double) (correctPasses + correctlyNotComputed) / totalMarkPasses; double accuracy = (double) (correctPasses + correctlyNotComputed) / totalMarkPasses;
if (printResult) { if (printResult) {
System.out.println("Total theoretical Passes: " + totalMarkPasses); System.out.println("Total theoretical Passes: " + totalMarkPasses);
@@ -144,13 +143,13 @@ public class AbstractMarkPassingTest extends OnlineTracTracBasedTest {
CandidateFinder finder = new CandidateFinder(getTrackedRace()); CandidateFinder finder = new CandidateFinder(getTrackedRace());
CandidateChooser chooser = new CandidateChooser(getTrackedRace()); CandidateChooser chooser = new CandidateChooser(getTrackedRace());
int mistakes = 0; int mistakes = 0;
TimePoint t = getTrackedRace().getStartOfRace();
for (Competitor c : getRace().getCompetitors()) { for (Competitor c : getRace().getCompetitors()) {
List<GPSFix> fixes = new ArrayList<GPSFix>(); List<GPSFix> fixes = new ArrayList<GPSFix>();
try { try {
getTrackedRace().getTrack(c).lockForRead(); getTrackedRace().getTrack(c).lockForRead();
for (GPSFixMoving fix : getTrackedRace().getTrack(c).getFixes()) { for (GPSFixMoving fix : getTrackedRace().getTrack(c).getFixes()) {
if (fix.getTimePoint().minus(120000).before(getTrackedRace().getStartOfRace())) { if (fix.getTimePoint().minus(120000).before(t)) {
fixes.add(fix); fixes.add(fix);
} }
} }
@@ -163,7 +162,7 @@ public class AbstractMarkPassingTest extends OnlineTracTracBasedTest {
boolean gotFirst = false; boolean gotFirst = false;
boolean gotOther = false; boolean gotOther = false;
for (Waypoint w : getRace().getCourse().getWaypoints()) { for (Waypoint w : getRace().getCourse().getWaypoints()) {
System.out.println(getTrackedRace().getMarkPassing(c, w)); // System.out.println(getTrackedRace().getMarkPassing(c, w));
if (w == w1) { if (w == w1) {
gotFirst = (getTrackedRace().getMarkPassing(c, w) != null) ? true : false; gotFirst = (getTrackedRace().getMarkPassing(c, w) != null) ? true : false;
} else { } else {
@@ -179,6 +178,48 @@ public class AbstractMarkPassingTest extends OnlineTracTracBasedTest {
Assert.assertTrue(mistakes == 0); Assert.assertTrue(mistakes == 0);
} }
protected void testFirstTwoWaypoints() {
CandidateFinder finder = new CandidateFinder(getTrackedRace());
CandidateChooser chooser = new CandidateChooser(getTrackedRace());
int mistakes = 0;
TimePoint t = getTrackedRace().getStartOfRace();
for (Competitor c : getRace().getCompetitors()) {
List<GPSFix> fixes = new ArrayList<GPSFix>();
try {
getTrackedRace().getTrack(c).lockForRead();
for (GPSFixMoving fix : getTrackedRace().getTrack(c).getFixes()) {
if (fix.getTimePoint().minus(360000).before(t)) {
fixes.add(fix);
}
}
} finally {
getTrackedRace().getTrack(c).unlockAfterRead();
}
Pair<List<Candidate>, List<Candidate>> f = finder.getCandidateDeltas(c, fixes);
chooser.calculateMarkPassDeltas(c, f);
Waypoint w1 = getRace().getCourse().getFirstWaypoint();
Waypoint w2 = getRace().getCourse().getFirstLeg().getTo();
boolean gotFirst = false;
boolean gotSecond = false;
boolean gotOther = false;
for (Waypoint w : getRace().getCourse().getWaypoints()) {
// System.out.println(getTrackedRace().getMarkPassing(c, w));
if (w == w1) {
gotFirst = (getTrackedRace().getMarkPassing(c, w) != null) ? true : false;
} else if (w == w2) {
gotSecond = (getTrackedRace().getMarkPassing(c, w) != null) ? true : false;
} else if (getTrackedRace().getMarkPassing(c, w) != null) {
gotOther = true;
}
}
if (!gotFirst|| !gotSecond || gotOther) {
mistakes++;
}
}
Assert.assertTrue(mistakes == 0);
}
/** /**
* Loads stored data for the given raceID or returns false if no data is present. * Loads stored data for the given raceID or returns false if no data is present.
* *
@@ -43,6 +43,7 @@ public class FlorianopolisMarkPassingTest extends AbstractMarkPassingTest {
setUp("529a4150-2dd0-0131-2830-60a44ce903c3"); setUp("529a4150-2dd0-0131-2830-60a44ce903c3");
testRace(); testRace();
testStartOfRace(); testStartOfRace();
testFirstTwoWaypoints();
} }
protected void setUp(String raceID) throws IOException, InterruptedException, URISyntaxException { protected void setUp(String raceID) throws IOException, InterruptedException, URISyntaxException {
@@ -25,6 +25,7 @@ public class OBMRMarkPassingTest extends AbstractMarkPassingTest {
setUp("65099a64-245d-11e2-9635-10bf48d758ce"); setUp("65099a64-245d-11e2-9635-10bf48d758ce");
testRace(); testRace();
testStartOfRace(); testStartOfRace();
testFirstTwoWaypoints();
} }
protected void setUp(String raceID) throws IOException, InterruptedException, URISyntaxException { protected void setUp(String raceID) throws IOException, InterruptedException, URISyntaxException {
@@ -9,22 +9,21 @@ import com.sap.sailing.domain.common.TimePoint;
* {@link Waypoint}) and the one-based(!) ID of this Waypoint. The ID is one based because the standard implemantation * {@link Waypoint}) and the one-based(!) ID of this Waypoint. The ID is one based because the standard implemantation
* of {@link AbstractCandidateChooser} (see {@link CandidateChooser}) uses a proxy Candidates at the end and the * of {@link AbstractCandidateChooser} (see {@link CandidateChooser}) uses a proxy Candidates at the end and the
* beginning of the race, the one at the beginning recieves the ID 0. Candidates are created in an * beginning of the race, the one at the beginning recieves the ID 0. Candidates are created in an
* {@link AbstractCandidateFinder}, which determines where a passing could be and assigns them * {@link AbstractCandidateFinder}, which determines where a passing could be and assigns them
* *
* @author Nicolas Klose * @author Nicolas Klose
* *
*/ */
public class Candidate { public class Candidate implements Comparable<Candidate> {
private Waypoint w; private Waypoint w;
private TimePoint p; private TimePoint p;
private double distanceProbability; private double distanceProbability;
private int id; private Integer id;
private boolean rightSide; private boolean rightSide;
private String type; private String type;
// TODO Boolean for right side of waypoint and right rounding way for waypoint
public Candidate(int id, TimePoint p, double distanceProbability, Waypoint w, boolean rightSide, String type){ public Candidate(int id, TimePoint p, double distanceProbability, Waypoint w, boolean rightSide, String type) {
this.w = w; this.w = w;
this.p = p; this.p = p;
this.distanceProbability = distanceProbability; this.distanceProbability = distanceProbability;
@@ -32,7 +31,7 @@ public class Candidate {
this.rightSide = rightSide; this.rightSide = rightSide;
this.type = type; this.type = type;
} }
public int getID() { public int getID() {
return id; return id;
} }
@@ -41,15 +40,21 @@ public class Candidate {
return p; return p;
} }
public double getProbability() { public Double getProbability() {
double cost = rightSide?distanceProbability:distanceProbability*0.3; double cost = rightSide ? distanceProbability : distanceProbability * 0.3;
return cost; return cost;
} }
public Waypoint getWaypoint() { public Waypoint getWaypoint() {
return w; return w;
} }
public String toString(){
return type + "-Candidate for " + id + " with cost "+ getProbability() + "and Timepoint " + p; public String toString() {
return type + "-Candidate for " + id + " with cost " + getProbability() + "and Timepoint " + p;
}
@Override
public int compareTo(Candidate arg0) {
return id != arg0.getID() ? id.compareTo(arg0.getID()) : p != arg0.getTimePoint() ? p.compareTo(arg0.getTimePoint()) : getProbability().compareTo(arg0.getProbability());
} }
} }
@@ -4,6 +4,8 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -35,8 +37,8 @@ public class CandidateChooser implements AbstractCandidateChooser {
private static final Logger logger = Logger.getLogger(CandidateChooser.class.getName()); private static final Logger logger = Logger.getLogger(CandidateChooser.class.getName());
private LinkedHashMap<Competitor, LinkedHashMap<Waypoint, MarkPassing>> currentMarkPasses = new LinkedHashMap<>(); private LinkedHashMap<Competitor, LinkedHashMap<Waypoint, MarkPassing>> currentMarkPasses = new LinkedHashMap<>();
private LinkedHashMap<Competitor, List<Edge>> allEdges = new LinkedHashMap<>(); private LinkedHashMap<Competitor, Set<Edge>> allEdges = new LinkedHashMap<>();
private LinkedHashMap<Competitor, List<Candidate>> candidates = new LinkedHashMap<>(); private LinkedHashMap<Competitor, Set<Candidate>> candidates = new LinkedHashMap<>();
private TimePoint raceStartTime; private TimePoint raceStartTime;
private Candidate start; private Candidate start;
private Candidate end; private Candidate end;
@@ -53,9 +55,9 @@ public class CandidateChooser implements AbstractCandidateChooser {
end = new Candidate(race.getRace().getCourse().getIndexOfWaypoint(race.getRace().getCourse().getLastWaypoint()) + 2, null, 1, null, true, "Proxy"); end = new Candidate(race.getRace().getCourse().getIndexOfWaypoint(race.getRace().getCourse().getLastWaypoint()) + 2, null, 1, null, true, "Proxy");
candidates = new LinkedHashMap<>(); candidates = new LinkedHashMap<>();
for (Competitor c : race.getRace().getCompetitors()) { for (Competitor c : race.getRace().getCompetitors()) {
candidates.put(c, new ArrayList<Candidate>()); candidates.put(c, new TreeSet<Candidate>());
currentMarkPasses.put(c, new LinkedHashMap<Waypoint, MarkPassing>()); currentMarkPasses.put(c, new LinkedHashMap<Waypoint, MarkPassing>());
allEdges.put(c, new ArrayList<Edge>()); allEdges.put(c, new TreeSet<Edge>());
addCandidates(Arrays.asList(start, end), c); addCandidates(Arrays.asList(start, end), c);
} }
} }
@@ -67,6 +69,7 @@ public class CandidateChooser implements AbstractCandidateChooser {
@Override @Override
public void calculateMarkPassDeltas(Competitor c, Pair<List<Candidate>, List<Candidate>> candidateDeltas) { public void calculateMarkPassDeltas(Competitor c, Pair<List<Candidate>, List<Candidate>> candidateDeltas) {
if (race.getStartOfRace() != raceStartTime) { if (race.getStartOfRace() != raceStartTime) {
raceStartTime = race.getStartOfRace(); raceStartTime = race.getStartOfRace();
for (Competitor com : allEdges.keySet()) { for (Competitor com : allEdges.keySet()) {
@@ -77,6 +80,7 @@ public class CandidateChooser implements AbstractCandidateChooser {
addCandidates(Arrays.asList(start), com); addCandidates(Arrays.asList(start), com);
} }
} }
removeCandidates(candidateDeltas.getB(), c); removeCandidates(candidateDeltas.getB(), c);
addCandidates(candidateDeltas.getA(), c); addCandidates(candidateDeltas.getA(), c);
findShortestPath(c); findShortestPath(c);
@@ -92,16 +96,19 @@ public class CandidateChooser implements AbstractCandidateChooser {
late = newCan; late = newCan;
} }
if (raceStartTime != null) { if (raceStartTime != null) {
if (late == end) { if (late == end && early != end) {
allEdges.get(co).add(new Edge(early, late, 1)); allEdges.get(co).add(new Edge(early, late, 1));
} else if (!(early.getID() == late.getID()) && !late.getTimePoint().before(early.getTimePoint()) && estimatedDistance(co, early, late) > penaltyForSkipping) { } else if (!(early.getID() == late.getID()) && !late.getTimePoint().before(early.getTimePoint()) && estimatedDistance(co, early, late) > penaltyForSkipping) {
Edge e = new Edge(early, late, estimatedDistance(co, early, late)); Edge e = new Edge(early, late, estimatedDistance(co, early, late));
allEdges.get(co).add(e); allEdges.get(co).add(e);
} }
} else { } else {
/*if (early == start && late.getID() == 1 && numberOfCloseStarts(late.getTimePoint()) > penaltyForSkipping) {
allEdges.get(co).add(new Edge(early, late, numberOfCloseStarts(late.getTimePoint()))); /*
} else*/ if (late == end || early == start) { * if (early == start && late.getID() == 1 && numberOfCloseStarts(late.getTimePoint()) >
* penaltyForSkipping) { allEdges.get(co).add(new Edge(early, late,
* numberOfCloseStarts(late.getTimePoint()))); } else
*/if ((late == end || early == start) && early != late) {
allEdges.get(co).add(new Edge(early, late, 1)); allEdges.get(co).add(new Edge(early, late, 1));
} else if (!(early.getID() == late.getID()) && late.getTimePoint().after(early.getTimePoint()) && estimatedDistance(co, early, late) > penaltyForSkipping) { } else if (!(early.getID() == late.getID()) && late.getTimePoint().after(early.getTimePoint()) && estimatedDistance(co, early, late) > penaltyForSkipping) {
allEdges.get(co).add(new Edge(early, late, estimatedDistance(co, early, late))); allEdges.get(co).add(new Edge(early, late, estimatedDistance(co, early, late)));
@@ -117,32 +124,34 @@ public class CandidateChooser implements AbstractCandidateChooser {
for (Edge e : allEdges.get(co)) { for (Edge e : allEdges.get(co)) {
all.add(e); all.add(e);
} }
LinkedHashMap<Candidate, Candidate> candidateWithParent = new LinkedHashMap<>(); LinkedHashMap<Candidate, Pair<Candidate, Double>> candidateWithParent = new LinkedHashMap<>();
candidateWithParent.put(start, null); candidateWithParent.put(start, new Pair<Candidate, Double>(null, 0.0));
Edge newMostLikelyEdge = null; Pair<Edge, Double> currentMostLikelyEdge = null;
while (!candidateWithParent.containsKey(end)) { while (!candidateWithParent.containsKey(end)) {
newMostLikelyEdge = null; currentMostLikelyEdge = null;
for (Edge e : all) { for (Edge e : all) {
if (candidateWithParent.containsKey(e.getStart())) { if (candidateWithParent.containsKey(e.getStart())) {
if (newMostLikelyEdge == null) { Double cost = candidateWithParent.get(e.getStart()).getB() + e.getProbability();
newMostLikelyEdge = e; if (currentMostLikelyEdge == null) {
} else if (e.getProbability() < newMostLikelyEdge.getProbability()) { currentMostLikelyEdge = new Pair<Edge, Double>(e, cost);
newMostLikelyEdge = e; } else if (cost < currentMostLikelyEdge.getB()) {
currentMostLikelyEdge = new Pair<Edge, Double>(e, cost);
} }
} }
} }
if (!candidateWithParent.containsKey(newMostLikelyEdge.getEnd())) { if (!candidateWithParent.containsKey(currentMostLikelyEdge.getA().getEnd())
candidateWithParent.put(newMostLikelyEdge.getEnd(), newMostLikelyEdge.getStart()); || candidateWithParent.get(currentMostLikelyEdge.getA().getEnd()).getB() > currentMostLikelyEdge.getB()) {
candidateWithParent.put(currentMostLikelyEdge.getA().getEnd(), new Pair<Candidate, Double>(currentMostLikelyEdge.getA().getStart(), currentMostLikelyEdge.getB()));
} }
all.remove(newMostLikelyEdge); all.remove(currentMostLikelyEdge.getA());
} }
Candidate marker = candidateWithParent.get(end); Candidate marker = candidateWithParent.get(end).getA();
while (!(marker == start)) { while (!(marker == start)) {
if (currentMarkPasses.get(co).get(marker.getWaypoint()) == null || currentMarkPasses.get(co).get(marker.getWaypoint()).getTimePoint() != marker.getTimePoint()) { if (currentMarkPasses.get(co).get(marker.getWaypoint()) == null || currentMarkPasses.get(co).get(marker.getWaypoint()).getTimePoint() != marker.getTimePoint()) {
currentMarkPasses.get(co).put(marker.getWaypoint(), new MarkPassingImpl(marker.getTimePoint(), marker.getWaypoint(), co)); currentMarkPasses.get(co).put(marker.getWaypoint(), new MarkPassingImpl(marker.getTimePoint(), marker.getWaypoint(), co));
changed = true; changed = true;
} }
marker = candidateWithParent.get(marker); marker = candidateWithParent.get(marker).getA();
} }
if (changed) { if (changed) {
logger.info("New MarkPasses for " + co); logger.info("New MarkPasses for " + co);
@@ -242,7 +251,6 @@ public class CandidateChooser implements AbstractCandidateChooser {
/* /*
* if (c.getID() == 1 && race.getStartOfRace() == null) { reEvaluateStartingEdges(); } * if (c.getID() == 1 && race.getStartOfRace() == null) { reEvaluateStartingEdges(); }
*/ */
// TODO Work without starting time
} }
createNewEdges(co, newCandidates); createNewEdges(co, newCandidates);
} }
@@ -383,7 +383,7 @@ public class CandidateFinder implements AbstractCandidateFinder {
private double getDistanceLikelyhood(Waypoint w, Position p, TimePoint t) { private double getDistanceLikelyhood(Waypoint w, Position p, TimePoint t) {
double distance = calculateDistance(p, w, t); double distance = calculateDistance(p, w, t);
double legLength = getLegLength(t, w); double legLength = getLegLength(t, w);
double result = 1 / (15 * Math.abs( distance/legLength ) + 1); double result = 1 / (10 * Math.abs( distance/legLength ) + 1);
// Auch NormalVerteilung??! // Auch NormalVerteilung??!
return result; return result;
} }
@@ -13,11 +13,12 @@ import com.sap.sailing.domain.base.Waypoint;
* *
*/ */
public class Edge { public class Edge implements Comparable<Edge> {
private Candidate start; private Candidate start;
private Candidate end; private Candidate end;
private static int numberOfWaypoints; private static int numberOfWaypoints;
private static double penaltyForSkipped = 0.6; private static double penaltyForSkipped = 0.8;
private static double penaltyForSkippedToEnd = 0.7;
private double timeEstimationOrCloseStartsProbability; private double timeEstimationOrCloseStartsProbability;
public Edge(Candidate start, Candidate end, double timeEstimationOrStartAnalysis) { public Edge(Candidate start, Candidate end, double timeEstimationOrStartAnalysis) {
@@ -34,9 +35,9 @@ public class Edge {
numberOfWaypoints = number; numberOfWaypoints = number;
} }
public double getProbability() { public Double getProbability() {
double penalty = end.getID() == numberOfWaypoints + 1 ? penaltyForSkipped : 0.9; double penalty = end.getID() == numberOfWaypoints + 1 ? penaltyForSkippedToEnd : penaltyForSkipped;
return 1 - (start.getProbability() * end.getProbability() * timeEstimationOrCloseStartsProbability) + 2 * penalty * (end.getID() - start.getID() - 1); return 1-(start.getProbability() * end.getProbability() * timeEstimationOrCloseStartsProbability) + 2 * penalty * (end.getID() - start.getID() - 1);
} }
public Candidate getStart() { public Candidate getStart() {
@@ -50,4 +51,9 @@ public class Edge {
public String toString() { public String toString() {
return "From ID " + start.getID() + " to " + end.getID() + ": " + getProbability(); return "From ID " + start.getID() + " to " + end.getID() + ": " + getProbability();
} }
@Override
public int compareTo(Edge o) {
return start != o.getStart() ? start.compareTo(o.getStart()) : end != o.getEnd() ? end.compareTo(o.getEnd()) : getProbability().compareTo(o.getProbability());
}
} }