diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index 64d0f46e..33884e3d 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -55,26 +55,10 @@ public class MockRace extends Race { private int dnfChance = 0; - - /** - * Used to generate random numbers when changing the wind direction. - */ - private int changeWind = 4; - - /** - * The bearing the wind direction starts at. - */ - private static final Bearing windBaselineBearing = Bearing.fromDegrees(225); - - /** - * The lower bearing angle that the wind may have. - */ - private static final Bearing windLowerBound = Bearing.fromDegrees(215); - /** - * The upper bearing angle that the wind may have. + * Object used to generate changes in wind speed/direction. */ - private static final Bearing windUpperBound = Bearing.fromDegrees(235); + private WindGenerator windGenerator; @@ -99,8 +83,17 @@ public class MockRace extends Race { this.shrinkBoundary = GPSCoordinate.getShrinkBoundary(this.boundary); + //Set up wind generator. It may be tidier to create this outside the race (with the values sourced from a data file maybe?) and pass it in. + this.windGenerator = new WindGenerator( + Bearing.fromDegrees(225), + Bearing.fromDegrees(215), + Bearing.fromDegrees(235), + 12d, + 8d, + 16d ); + //Wind. - this.setWind(Bearing.fromDegrees(180), 12); + this.setWind(windGenerator.generateBaselineWind()); } @@ -887,9 +880,7 @@ public class MockRace extends Race { */ protected void initialiseWindDirection() { //Set the starting bearing. - this.setWind( - Bearing.fromDegrees(MockRace.windBaselineBearing.degrees()), - this.getWindSpeed() ); + this.setWind(windGenerator.generateBaselineWind()); } @@ -897,34 +888,10 @@ public class MockRace extends Race { * Changes the wind direction randomly, while keeping it within [windLowerBound, windUpperBound]. */ protected void changeWindDirection() { - //TODO this wind generation could probably be moved to its own object? - //Randomly add or remove 0.5 degrees. - int r = new Random().nextInt(changeWind) + 1; - - double newWindBearingDegrees = this.getWindDirection().degrees(); - - if (r == 1) { - //Add 0.5 degrees to the wind bearing. - newWindBearingDegrees += 0.5; - } else if (r == 2) { - //Minus 0.5 degrees from the wind bearing. - newWindBearingDegrees -= 0.5; - - } - - //Ensure that the wind is in the correct bounds. - if (newWindBearingDegrees > MockRace.windUpperBound.degrees()) { - newWindBearingDegrees = MockRace.windUpperBound.degrees(); - - } else if (newWindBearingDegrees < MockRace.windLowerBound.degrees()) { - newWindBearingDegrees = MockRace.windLowerBound.degrees(); - - } + Wind nextWind = windGenerator.generateNextWind(raceWind.getValue()); - this.setWind( - Bearing.fromDegrees(newWindBearingDegrees), - this.getWindSpeed() ); + setWind(nextWind); } diff --git a/racevisionGame/src/main/java/mock/model/WindGenerator.java b/racevisionGame/src/main/java/mock/model/WindGenerator.java new file mode 100644 index 00000000..30fd24b4 --- /dev/null +++ b/racevisionGame/src/main/java/mock/model/WindGenerator.java @@ -0,0 +1,249 @@ +package mock.model; + + +import shared.model.Bearing; +import shared.model.Wind; + +import java.util.Random; + +/** + * This class generates Wind objects for use in a MockRace. + * Bounds on bearing and speed can be specified. + * Wind can be completely random, or random incremental change. + */ +public class WindGenerator { + + /** + * The bearing the wind direction starts at. + */ + private Bearing windBaselineBearing; + + /** + * The lower bearing angle that the wind may have. + */ + private Bearing windBearingLowerBound; + + /** + * The upper bearing angle that the wind may have. + */ + private Bearing windBearingUpperBound; + + + + /** + * The speed the wind starts at, in knots. + */ + private double windBaselineSpeed; + + /** + * The lower speed that the wind may have, in knots. + */ + private double windSpeedLowerBound; + + /** + * The upper speed that the wind may have, in knots. + */ + private double windSpeedUpperBound; + + + /** + * Creates a wind generator, with a baseline, lower bound, and upper bound, for the wind speed and direction. + * @param windBaselineBearing Baseline wind direction. + * @param windBearingLowerBound Lower bound for wind direction. + * @param windBearingUpperBound Upper bound for wind direction. + * @param windBaselineSpeed Baseline wind speed, in knots. + * @param windSpeedLowerBound Lower bound for wind speed, in knots. + * @param windSpeedUpperBound Upper bound for wind speed, in knots. + */ + public WindGenerator(Bearing windBaselineBearing, Bearing windBearingLowerBound, Bearing windBearingUpperBound, double windBaselineSpeed, double windSpeedLowerBound, double windSpeedUpperBound) { + + this.windBaselineBearing = windBaselineBearing; + this.windBearingLowerBound = windBearingLowerBound; + this.windBearingUpperBound = windBearingUpperBound; + this.windBaselineSpeed = windBaselineSpeed; + this.windSpeedLowerBound = windSpeedLowerBound; + this.windSpeedUpperBound = windSpeedUpperBound; + + } + + + /** + * Generates a wind object using the baseline wind speed and bearing. + * @return Baseline wind object. + */ + public Wind generateBaselineWind() { + return new Wind(windBaselineBearing, windBaselineSpeed); + } + + /** + * Generates a random Wind object, that is within the provided bounds. + * @return Generated wind object. + */ + public Wind generateRandomWind() { + + double windSpeed = generateRandomWindSpeed(); + Bearing windBearing = generateRandomWindBearing(); + + return new Wind(windBearing, windSpeed); + + } + + /** + * Generates a random wind speed within the specified bounds. In knots. + * @return Wind speed, in knots. + */ + private double generateRandomWindSpeed() { + + double randomSpeedKnots = generateRandomValueInBounds(windSpeedLowerBound, windSpeedUpperBound); + + return randomSpeedKnots; + } + + + /** + * Generates a random wind bearing within the specified bounds. + * @return Wind bearing. + */ + private Bearing generateRandomWindBearing() { + + double randomBearingDegrees = generateRandomValueInBounds(windBearingLowerBound.degrees(), windBearingUpperBound.degrees()); + + return Bearing.fromDegrees(randomBearingDegrees); + } + + + /** + * Generates a random value within a specified interval. + * @param lowerBound The lower bound of the interval. + * @param upperBound The upper bound of the interval. + * @return A random value within the interval. + */ + private static double generateRandomValueInBounds(double lowerBound, double upperBound) { + + float proportion = new Random().nextFloat(); + + double delta = upperBound - lowerBound; + + double amount = delta * proportion; + + double finalAmount = amount + lowerBound; + + return finalAmount; + + } + + + /** + * Generates a new value within an interval, given a start value, chance to change, and change amount. + * @param lowerBound Lower bound of interval. + * @param upperBound Upper bound of interval. + * @param currentValue The current value to change. + * @param changeAmount The amount to change by. + * @param chanceToChange The change to actually change the value. + * @return The new value. + */ + private static double generateNextValueInBounds(double lowerBound, double upperBound, double currentValue, double changeAmount, double chanceToChange) { + + float chance = new Random().nextFloat(); + + + if (chance <= chanceToChange) { + currentValue += changeAmount; + + } else if (chance <= (2 * chanceToChange)) { + currentValue -= changeAmount; + + } + + currentValue = clamp(lowerBound, upperBound, currentValue); + + return currentValue; + + } + + + /** + * Generates the next Wind object, that is within the provided bounds. This randomly increases or decreases the wind's speed and bearing. + * @param currentWind The current wind to change. This is not modified. + * @return Generated wind object. + */ + public Wind generateNextWind(Wind currentWind) { + + double windSpeed = generateNextWindSpeed(currentWind.getWindSpeed()); + Bearing windBearing = generateNextWindBearing(currentWind.getWindDirection()); + + return new Wind(windBearing, windSpeed); + + } + + + /** + * Generates the next wind speed to use. + * @param windSpeed Current wind speed, in knots. + * @return Next wind speed, in knots. + */ + private double generateNextWindSpeed(double windSpeed) { + + double chanceToChange = 0.2; + double changeAmount = 0.1; + + double nextWindSpeed = generateNextValueInBounds( + windSpeedLowerBound, + windSpeedUpperBound, + windSpeed, + changeAmount, + chanceToChange); + + return nextWindSpeed; + } + + + /** + * Generates the next wind speed to use. + * @param windBearing Current wind bearing. + * @return Next wind speed. + */ + private Bearing generateNextWindBearing(Bearing windBearing) { + + double chanceToChange = 0.2; + double changeAmount = 0.5; + + double nextWindBearingDegrees = generateNextValueInBounds( + windBearingLowerBound.degrees(), + windBearingUpperBound.degrees(), + windBearing.degrees(), + changeAmount, + chanceToChange); + + return Bearing.fromDegrees(nextWindBearingDegrees); + } + + + + + + /** + * Clamps a value to be within an interval. + * @param lower Lower bound of the interval. + * @param upper Upper bound of the interval. + * @param value Value to clamp. + * @return The clamped value. + */ + private static double clamp(double lower, double upper, double value) { + + if (value > upper) { + value = upper; + + } else if (value < lower) { + value = lower; + + } + + return value; + } + + + + + +} diff --git a/racevisionGame/src/main/java/shared/model/Race.java b/racevisionGame/src/main/java/shared/model/Race.java index 6240539f..aec57882 100644 --- a/racevisionGame/src/main/java/shared/model/Race.java +++ b/racevisionGame/src/main/java/shared/model/Race.java @@ -255,6 +255,14 @@ public abstract class Race implements Runnable { */ protected void setWind(Bearing windBearing, double windSpeedKnots) { Wind wind = new Wind(windBearing, windSpeedKnots); + setWind(wind); + } + + /** + * Updates the race to have a specified wind (bearing and speed). + * @param wind New wind. + */ + protected void setWind(Wind wind) { this.raceWind.setValue(wind); }