Mock.Polars:

Moved some code into isBearingInsideInterval and isFlippedInterval utility functions.

Mock.Race:
Appear to have fixed the boats going out bounds issue at last, by fixing a few bugs, simplifying the code, and removing the boat turning speeds.
main
fjc40 9 years ago
parent 824a5ed3a2
commit 7f3500202f

@ -118,10 +118,7 @@ public class Polars {
//If the lower bound is greater than the upper bound, we have a "flipped" interval. That is for, e.g., [70, 55] the lower bound is greater than the upper bound, and so it checks that (VMGAngle >= 70 OR VMGAngle =< 55), instead of (VMGAngle >= 70 AND VMGAngle =< 55). //If the lower bound is greater than the upper bound, we have a "flipped" interval. That is for, e.g., [70, 55] the lower bound is greater than the upper bound, and so it checks that (VMGAngle >= 70 OR VMGAngle =< 55), instead of (VMGAngle >= 70 AND VMGAngle =< 55).
boolean flippedInterval = false; boolean flippedInterval = Polars.isFlippedInterval(bearingLowerBound, bearingUpperBound);
if (bearingLowerBound.degrees() > bearingUpperBound.degrees()) {
flippedInterval = true;
}
@ -179,7 +176,7 @@ public class Polars {
Bearing bestVMGAngle = Bearing.fromDegrees(0d); Bearing bestVMGAngle = Bearing.fromDegrees(0d);
//Calculate the VMG for all possible angles at this wind speed. //Calculate the VMG for all possible angles at this wind speed.
for (double angleDegree = 0; angleDegree < 360; angleDegree += 0.1) { for (double angleDegree = 0; angleDegree < 360; angleDegree += 1) {
Bearing angle = Bearing.fromDegrees(angleDegree); Bearing angle = Bearing.fromDegrees(angleDegree);
//This is the true bearing of the boat, if it went at the angle against the wind. //This is the true bearing of the boat, if it went at the angle against the wind.
@ -191,22 +188,11 @@ public class Polars {
//Check that the boat's bearing would actually be acceptable. //Check that the boat's bearing would actually be acceptable.
//We continue (skip to next iteration) if it is outside of the interval. //We continue (skip to next iteration) if it is outside of the interval.
if (flippedInterval) { if (!Polars.isBearingInsideInterval(trueBoatBearing, bearingLowerBound, bearingUpperBound)) {
//Bearing must be inside [lower, upper], where lower > upper. So, bearing must be >= lower, or bearing < upper. We use inverted logic since we are skipping if it is true. continue;
if ((trueBoatBearing.degrees() < bearingLowerBound.degrees()) & (trueBoatBearing.degrees() > bearingUpperBound.degrees())) {
continue;
}
} else {
//Bearing must be inside [lower, upper].
if ((trueBoatBearing.degrees() < bearingLowerBound.degrees()) || (trueBoatBearing.degrees() > bearingUpperBound.degrees())) {
continue;
}
} }
//Basic linear interpolation. Find the nearest two angles from the table, and interpolate between them. //Basic linear interpolation. Find the nearest two angles from the table, and interpolate between them.
//Check which pair of adjacent angles the angle is between. //Check which pair of adjacent angles the angle is between.
@ -313,6 +299,58 @@ public class Polars {
} }
/**
* Determines whether an interval is "flipped". This means that the lower bound is greater than the upper bound (e.g., [290, 43] degrees).
* @param lowerBound The lower bound.
* @param upperBound The upper bound.
* @return True if the interval is flipped, false otherwise.
*/
public static boolean isFlippedInterval(Bearing lowerBound, Bearing upperBound) {
//If the lower bound is greater than the upper bound, we have a "flipped" interval.
boolean flippedInterval = false;
if (lowerBound.degrees() > upperBound.degrees()) {
flippedInterval = true;
}
return flippedInterval;
}
/**
* Determines if a bearing is inside an interval.
* @param bearing The bearing to check.
* @param lowerBound The lower bound of the interval.
* @param upperBound The upper bound of the interval.
* @return True if the bearing is inside the interval, false otherwise.
*/
public static boolean isBearingInsideInterval(Bearing bearing, Bearing lowerBound, Bearing upperBound) {
//Check if it's a flipped interval.
boolean flippedInterval = Polars.isFlippedInterval(lowerBound, upperBound);
if (flippedInterval) {
//Bearing must be inside [lower, upper], where lower > upper. So, bearing must be >= lower, or bearing < upper. We use inverted logic since we are skipping if it is true.
if ((bearing.degrees() >= lowerBound.degrees()) || (bearing.degrees() <= upperBound.degrees())) {
return true;
} else {
return false;
}
} else {
//Bearing must be inside [lower, upper].
if ((bearing.degrees() >= lowerBound.degrees()) && (bearing.degrees() <= upperBound.degrees())) {
return true;
} else {
return false;
}
}
}
/** /**
* Calculate the linear interpolation scalar for a value between two bounds. E.g., lower = 7, upper = 10, value = 8, therefore the scalar (or the proportion between the bounds) is 0.333. * Calculate the linear interpolation scalar for a value between two bounds. E.g., lower = 7, upper = 10, value = 8, therefore the scalar (or the proportion between the bounds) is 0.333.
* Also assumes that the bounds are periodic - e.g., for angles a lower bound of 350deg and upper bound of 5deg is in interval of 15 degrees. * Also assumes that the bounds are periodic - e.g., for angles a lower bound of 350deg and upper bound of 5deg is in interval of 15 degrees.

@ -502,67 +502,64 @@ public class Race implements Runnable {
*/ */
private VMG calculateVMG(Boat boat, Bearing[] bearingBounds) { private VMG calculateVMG(Boat boat, Bearing[] bearingBounds) {
//How fast a boat can turn, in degrees per millisecond.
double turnRate = 0.03;
//How much the boat is allowed to turn, considering how long since it last turned.
double turnAngle = turnRate * boat.getTimeSinceTackChange();
//This ensures that the boat bounds don't flip around.
turnAngle = Math.min(turnAngle, 179.999);
//Find the bounds on what angle the boat is allowed to travel at. The bounds cap out at [0, 360).
double bound1Degrees = boat.getBearing().degrees() - turnAngle;
double bound2Degrees = boat.getBearing().degrees() + turnAngle;
Bearing bound1 = Bearing.fromDegrees(bound1Degrees);
Bearing bound2 = Bearing.fromDegrees(bound2Degrees);
//Get the lower and upper acceptable bounds. //Get the lower and upper acceptable bounds.
Bearing lowerAcceptableBound = bearingBounds[0]; Bearing lowerAcceptableBound = bearingBounds[0];
Bearing upperAcceptableBound = bearingBounds[1]; Bearing upperAcceptableBound = bearingBounds[1];
//Find the bounds on what angle the boat can travel on, taking into account both turning rate and the acceptable bounds. //Find the VMG inside these bounds.
bound1Degrees = Math.max(bound1.degrees(), lowerAcceptableBound.degrees()); VMG bestVMG = boat.getPolars().calculateVMG(this.windDirection, this.windSpeed, boat.calculateBearingToNextMarker(), lowerAcceptableBound, upperAcceptableBound);
bound2Degrees = Math.min(bound2.degrees(), upperAcceptableBound.degrees());
bound1 = Bearing.fromDegrees(bound1Degrees);
bound2 = Bearing.fromDegrees(bound2Degrees);
//Calculate VMG using these bounds. return bestVMG;
return boat.getPolars().calculateVMG(this.windDirection, this.windSpeed, boat.calculateBearingToNextMarker(), bound1, bound2);
} }
/** /**
* Determines whether or not a given VMG improves the velocity of a boat. * Determines whether or not a given VMG improves the velocity of a boat, if it were currently using currentVMG.
* @param boat The boat to test. * @param currentVMG The current VMG of the boat.
* @param vmg The new VMG to test. * @param potentialVMG The new VMG to test.
* @param bearingToDestination The bearing between the boat and its destination.
* @return True if the new VMG is improves velocity, false otherwise. * @return True if the new VMG is improves velocity, false otherwise.
*/ */
private boolean improvesVelocity(Boat boat, VMG vmg) { private boolean improvesVelocity(VMG currentVMG, VMG potentialVMG, Bearing bearingToDestination) {
//Calculates the angle between the boat and its destination. //Calculates the angle between the boat and its destination.
Angle angleBetweenDestAndHeading = Angle.fromDegrees(boat.getBearing().degrees() - boat.calculateBearingToNextMarker().degrees()); Angle angleBetweenDestAndHeading = Angle.fromDegrees(currentVMG.getBearing().degrees() - bearingToDestination.degrees());
//Calculates the angle between the new VMG and the boat's destination. //Calculates the angle between the new VMG and the boat's destination.
Angle angleBetweenDestAndNewVMG = Angle.fromDegrees(vmg.getBearing().degrees() - boat.calculateBearingToNextMarker().degrees()); Angle angleBetweenDestAndNewVMG = Angle.fromDegrees(potentialVMG.getBearing().degrees() - bearingToDestination.degrees());
//Calculate the boat's current velocity. //Calculate the boat's current velocity.
double currentVelocity = Math.cos(angleBetweenDestAndHeading.radians()) * boat.getCurrentSpeed(); double currentVelocity = Math.cos(angleBetweenDestAndHeading.radians()) * currentVMG.getSpeed();
//Calculate the potential velocity with the new VMG. //Calculate the potential velocity with the new VMG.
double vmgVelocity = Math.cos(angleBetweenDestAndNewVMG.radians()) * vmg.getSpeed(); double vmgVelocity = Math.cos(angleBetweenDestAndNewVMG.radians()) * potentialVMG.getSpeed();
//Return whether or not the new VMG gives better velocity. //Return whether or not the new VMG gives better velocity.
return vmgVelocity > currentVelocity; return vmgVelocity > currentVelocity;
} }
/**
* Determines whether or not a given VMG improves the velocity of a boat.
* @param boat The boat to test.
* @param vmg The new VMG to test.
* @return True if the new VMG is improves velocity, false otherwise.
*/
private boolean improvesVelocity(Boat boat, VMG vmg) {
//Get the boats "current" VMG.
VMG boatVMG = new VMG(boat.getCurrentSpeed(), boat.getBearing());
//Check if the new VMG is better than the boat's current VMG.
return this.improvesVelocity(boatVMG, vmg, boat.calculateBearingToNextMarker());
}
/** /**
* Calculates the distance a boat has travelled and updates its current position according to this value. * Calculates the distance a boat has travelled and updates its current position according to this value.
@ -593,6 +590,7 @@ public class Race implements Runnable {
//Calculate the boat's bearing bounds, to ensure that it doesn't go out of the course. //Calculate the boat's bearing bounds, to ensure that it doesn't go out of the course.
Bearing[] bearingBounds = this.calculateBearingBounds(boat); Bearing[] bearingBounds = this.calculateBearingBounds(boat);
//Calculate the new VMG. //Calculate the new VMG.
VMG newVMG = this.calculateVMG(boat, bearingBounds); VMG newVMG = this.calculateVMG(boat, bearingBounds);
@ -626,14 +624,13 @@ public class Race implements Runnable {
Bearing[] bearings = new Bearing[2]; Bearing[] bearings = new Bearing[2];
Bearing lowerBearing = Bearing.fromDegrees(0); Bearing lowerBearing = Bearing.fromDegrees(0.001);
Bearing upperBearing = Bearing.fromDegrees(359.999); Bearing upperBearing = Bearing.fromDegrees(359.999);
boolean foundAnyBadBearing = false;
boolean foundLowerBearing = false; double lastAngle = -1;
boolean foundAnyGoodBearing = false; boolean lastAngleWasGood = false;
boolean stopFindingUpperBearing = false;
//Check all bearings between [0, 360). //Check all bearings between [0, 360).
for (double angle = 0; angle < 360; angle += 1) { for (double angle = 0; angle < 360; angle += 1) {
@ -644,36 +641,28 @@ public class Race implements Runnable {
//Check that if it is acceptable. //Check that if it is acceptable.
boolean bearingIsGood = this.checkBearingInsideCourse(bearing, boat.getCurrentPosition()); boolean bearingIsGood = this.checkBearingInsideCourse(bearing, boat.getCurrentPosition());
if (bearingIsGood) {
//The lower bearing will be the first acceptable bearing after finding any unacceptable bearing. if (lastAngle != -1) {
if (foundAnyBadBearing && !foundLowerBearing) {
lowerBearing = bearing;
foundLowerBearing = true;
foundAnyGoodBearing = true;
}
//The upper bearing will be the last acceptable bearing before finding any unacceptable bearing. if (lastAngleWasGood && !bearingIsGood) {
if (!foundAnyBadBearing) { //We have flipped over from good bearings to bad bearings. So the last good bearing is the upper bearing.
upperBearing = bearing; upperBearing = Bearing.fromDegrees(lastAngle);
} else if (foundAnyBadBearing && foundAnyGoodBearing && !stopFindingUpperBearing) {
upperBearing = bearing;
} }
if (!lastAngleWasGood && bearingIsGood) {
} else { //We have flipped over from bad bearings to good bearings. So the current bearing is the lower bearing.
lowerBearing = Bearing.fromDegrees(angle);
foundAnyBadBearing = true;
if (foundAnyBadBearing && foundAnyGoodBearing) {
stopFindingUpperBearing = true;
} }
} }
lastAngle = angle;
lastAngleWasGood = bearingIsGood;
} }
//TODO BUG if it can't find either upper or lower, it returns (0, 359.999). Should return (boatbearing, boatbearing+0.0001) //TODO BUG if it can't find either upper or lower, it returns (0, 359.999). Should return (boatbearing, boatbearing+0.0001)
bearings[0] = lowerBearing; bearings[0] = lowerBearing;
bearings[1] = upperBearing; bearings[1] = upperBearing;
@ -687,6 +676,7 @@ public class Race implements Runnable {
* Checks if a given bearing, starting at a given position, would put a boat out of the course boundaries. * Checks if a given bearing, starting at a given position, would put a boat out of the course boundaries.
* @param bearing The bearing to check. * @param bearing The bearing to check.
* @param position The position to start from. * @param position The position to start from.
* @return True if the bearing would keep the boat in the course, false if it would take it out of the course.
*/ */
private boolean checkBearingInsideCourse(Bearing bearing, GPSCoordinate position) { private boolean checkBearingInsideCourse(Bearing bearing, GPSCoordinate position) {

Loading…
Cancel
Save