@ -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 ( ) - b oat. calculateBearingToNextMarker ( ) . degrees ( ) ) ;
Angle angleBetweenDestAndHeading = Angle . fromDegrees ( currentVMG . getBearing ( ) . degrees ( ) - b earingToDestination . 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 ( ) - b oat. calculateBearingToNextMarker ( ) . degrees ( ) ) ;
Angle angleBetweenDestAndNewVMG = Angle . fromDegrees ( potentialVMG . getBearing ( ) . degrees ( ) - b earingToDestination . degrees ( ) ) ;
//Calculate the boat's current velocity.
//Calculate the boat's current velocity.
double currentVelocity = Math . cos ( angleBetweenDestAndHeading . radians ( ) ) * boat. getCurren tSpeed( ) ;
double currentVelocity = Math . cos ( angleBetweenDestAndHeading . radians ( ) ) * currentVMG. ge tSpeed( ) ;
//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 ) {