From a87879eaa9eb775093c8178c7477dd0a06227660 Mon Sep 17 00:00:00 2001 From: Joseph Gardner Date: Wed, 2 Aug 2017 12:41:47 +1200 Subject: [PATCH 01/18] Made new branch for command architecture. --- racevisionGame/src/main/java/mock/model/RaceEvent.java | 4 ++++ racevisionGame/src/main/java/mock/model/RaceLogic.java | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 racevisionGame/src/main/java/mock/model/RaceEvent.java create mode 100644 racevisionGame/src/main/java/mock/model/RaceLogic.java diff --git a/racevisionGame/src/main/java/mock/model/RaceEvent.java b/racevisionGame/src/main/java/mock/model/RaceEvent.java new file mode 100644 index 00000000..cf14a180 --- /dev/null +++ b/racevisionGame/src/main/java/mock/model/RaceEvent.java @@ -0,0 +1,4 @@ +package mock.model; + +public class RaceEvent { +} diff --git a/racevisionGame/src/main/java/mock/model/RaceLogic.java b/racevisionGame/src/main/java/mock/model/RaceLogic.java new file mode 100644 index 00000000..b8e904a6 --- /dev/null +++ b/racevisionGame/src/main/java/mock/model/RaceLogic.java @@ -0,0 +1,4 @@ +package mock.model; + +public class RaceLogic { +} From 775c32ca92c37d3f862a11b87b951ab0d039e182 Mon Sep 17 00:00:00 2001 From: Joseph Gardner Date: Wed, 2 Aug 2017 12:48:56 +1200 Subject: [PATCH 02/18] Made new branch for command architecture. --- racevisionGame/src/main/java/mock/model/RaceEvent.java | 4 ---- racevisionGame/src/main/java/mock/model/RaceStatus.java | 4 ++++ .../main/java/mock/model/commandFactory/CommandFactory.java | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) delete mode 100644 racevisionGame/src/main/java/mock/model/RaceEvent.java create mode 100644 racevisionGame/src/main/java/mock/model/RaceStatus.java create mode 100644 racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java diff --git a/racevisionGame/src/main/java/mock/model/RaceEvent.java b/racevisionGame/src/main/java/mock/model/RaceEvent.java deleted file mode 100644 index cf14a180..00000000 --- a/racevisionGame/src/main/java/mock/model/RaceEvent.java +++ /dev/null @@ -1,4 +0,0 @@ -package mock.model; - -public class RaceEvent { -} diff --git a/racevisionGame/src/main/java/mock/model/RaceStatus.java b/racevisionGame/src/main/java/mock/model/RaceStatus.java new file mode 100644 index 00000000..74e2823c --- /dev/null +++ b/racevisionGame/src/main/java/mock/model/RaceStatus.java @@ -0,0 +1,4 @@ +package mock.model; + +public class RaceStatus { +} diff --git a/racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java b/racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java new file mode 100644 index 00000000..c073fc95 --- /dev/null +++ b/racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java @@ -0,0 +1,4 @@ +package mock.model.commandFactory; + +public interface CommandFactory { +} From 81eeca3533077da9c80e5518026e5dd657784c74 Mon Sep 17 00:00:00 2001 From: Joseph Gardner Date: Wed, 2 Aug 2017 13:45:10 +1200 Subject: [PATCH 03/18] Split wind off from Race and made it its own class. Deleted dnfChance as it is no longer used. --- .../src/main/java/mock/model/MockRace.java | 108 +----------------- .../src/main/java/mock/model/RaceState.java | 4 + .../src/main/java/mock/model/RaceStatus.java | 4 - .../src/main/java/mock/model/Wind.java | 92 +++++++++++++++ .../src/main/java/shared/model/Race.java | 20 +--- .../java/visualiser/model/VisualiserRace.java | 8 +- 6 files changed, 110 insertions(+), 126 deletions(-) create mode 100644 racevisionGame/src/main/java/mock/model/RaceState.java delete mode 100644 racevisionGame/src/main/java/mock/model/RaceStatus.java create mode 100644 racevisionGame/src/main/java/mock/model/Wind.java diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index ad259bdc..b611b49b 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -48,35 +48,6 @@ public class MockRace extends Race { private int scaleFactor; - /** - * The percent chance that a boat fails the race, and enters a DNF state, at each checkpoint. - * 0 = 0%, 100 = 100%. - */ - 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. - */ - private static final Bearing windUpperBound = Bearing.fromDegrees(235); - - /** @@ -98,12 +69,7 @@ public class MockRace extends Race { this.shrinkBoundary = GPSCoordinate.getShrinkBoundary(this.boundary); - - - this.windSpeed = 12; - this.windDirection = Bearing.fromDegrees(180); - - + this.wind = new Wind(); } /** @@ -140,7 +106,6 @@ public class MockRace extends Race { */ public void run() { initialiseBoats(); - initialiseWindDirection(); this.countdownTimer.start(); } @@ -288,8 +253,8 @@ public class MockRace extends Race { //Convert wind direction and speed to ints. //TODO this conversion should be done inside the racestatus class. - int windDirectionInt = AC35UnitConverter.encodeHeading(this.windDirection.degrees()); - int windSpeedInt = (int) (this.windSpeed * Constants.KnotsToMMPerSecond); + int windDirectionInt = AC35UnitConverter.encodeHeading(wind.getWindDirection().degrees()); + int windSpeedInt = (int) (wind.getWindSpeed() * Constants.KnotsToMMPerSecond); //Create race status object, and send it. RaceStatus raceStatus = new RaceStatus( @@ -576,7 +541,7 @@ public class MockRace extends Race { //Find the VMG inside these bounds. - VMG bestVMG = boat.getPolars().calculateVMG(this.windDirection, this.windSpeed, boat.calculateBearingToNextMarker(), lowerAcceptableBound, upperAcceptableBound); + VMG bestVMG = boat.getPolars().calculateVMG(wind.getWindDirection(), wind.getWindSpeed(), boat.calculateBearingToNextMarker(), lowerAcceptableBound, upperAcceptableBound); return bestVMG; @@ -810,44 +775,12 @@ public class MockRace extends Race { boat.setCurrentSpeed(0); boat.setStatus(BoatStatusEnum.FINISHED); - } else if (doNotFinish()) { - //Boat has pulled out of race. - boat.setTimeFinished(timeElapsed); - boat.setCurrentLeg(new Leg("DNF", -1)); - boat.setCurrentSpeed(0); - boat.setStatus(BoatStatusEnum.DNF); - } } } - - - - /** - * Sets the chance each boat has of failing at a gate or marker - * - * @param chance percentage chance a boat has of failing per checkpoint. - */ - protected void setDnfChance(int chance) { - if (chance >= 0 && chance <= 100) { - dnfChance = chance; - } - } - - /** - * Decides if a boat should received a DNF status. - * @return True means it should DNF, false means it shouldn't. - */ - protected boolean doNotFinish() { - Random rand = new Random(); - return rand.nextInt(100) < dnfChance; - } - - - /** * Returns the number of boats that are still active in the race. * They become inactive by either finishing or withdrawing. @@ -878,42 +811,11 @@ public class MockRace extends Race { return boats; } - - /** - * Initialises the wind bearing with the value of the windBaselineBearing. - */ - protected void initialiseWindDirection() { - //Set the starting bearing. - this.windDirection = Bearing.fromDegrees(MockRace.windBaselineBearing.degrees()); - } - - /** * Changes the wind direction randomly, while keeping it within [windLowerBound, windUpperBound]. */ protected void changeWindDirection() { - - //Randomly add or remove 0.5 degrees. - int r = new Random().nextInt(changeWind) + 1; - - if (r == 1) { - //Add 0.5 degrees to the wind bearing. - this.windDirection.setDegrees(this.windDirection.degrees() + 0.5); - - } else if (r == 2) { - //Minus 0.5 degrees from the wind bearing. - this.windDirection.setDegrees(this.windDirection.degrees() - 0.5); - - } - - //Ensure that the wind is in the correct bounds. - if (this.windDirection.degrees() > MockRace.windUpperBound.degrees()) { - this.windDirection.setBearing(MockRace.windUpperBound); - - } else if (this.windDirection.degrees() < MockRace.windLowerBound.degrees()) { - this.windDirection.setBearing(MockRace.windLowerBound); - - } + this.wind.changeWindDirection(); } diff --git a/racevisionGame/src/main/java/mock/model/RaceState.java b/racevisionGame/src/main/java/mock/model/RaceState.java new file mode 100644 index 00000000..4b13cbb4 --- /dev/null +++ b/racevisionGame/src/main/java/mock/model/RaceState.java @@ -0,0 +1,4 @@ +package mock.model; + +public class RaceState { +} diff --git a/racevisionGame/src/main/java/mock/model/RaceStatus.java b/racevisionGame/src/main/java/mock/model/RaceStatus.java deleted file mode 100644 index 74e2823c..00000000 --- a/racevisionGame/src/main/java/mock/model/RaceStatus.java +++ /dev/null @@ -1,4 +0,0 @@ -package mock.model; - -public class RaceStatus { -} diff --git a/racevisionGame/src/main/java/mock/model/Wind.java b/racevisionGame/src/main/java/mock/model/Wind.java new file mode 100644 index 00000000..199cb98b --- /dev/null +++ b/racevisionGame/src/main/java/mock/model/Wind.java @@ -0,0 +1,92 @@ +package mock.model; + +import shared.model.Bearing; + +import java.util.Random; + +public class Wind { + + /** + * Used to generate random numbers when changing the wind direction. + */ + private int changeWind = 4; + + /** + * The bearing the wind direction starts at. + */ + private Bearing windBearing; + + /** + * The lower bearing angle that the wind may have. + */ + private Bearing windLowerBound; + + /** + * The upper bearing angle that the wind may have. + */ + private Bearing windUpperBound; + + double windSpeed; + + public Wind() { + this.windBearing = Bearing.fromDegrees(225); + this.windSpeed = 12; + this.windLowerBound = Bearing.fromDegrees(215); + this.windUpperBound = Bearing.fromDegrees(235); + } + + public Wind(Bearing windBearing, double windSpeed, Bearing windLowerBound, Bearing windUpperBound) { + this.windBearing = windBearing; + this.windSpeed = windSpeed; + this.windLowerBound = windLowerBound; + this.windUpperBound = windUpperBound; + } + + /** + * Changes the wind direction randomly, while keeping it within [windLowerBound, windUpperBound]. + */ + public void changeWindDirection() { + + //Randomly add or remove 0.5 degrees. + int r = new Random().nextInt(changeWind) + 1; + + if (r == 1) { + //Add 0.5 degrees to the wind bearing. + this.windBearing.setDegrees(this.windBearing.degrees() + 0.5); + + } else if (r == 2) { + //Minus 0.5 degrees from the wind bearing. + this.windBearing.setDegrees(this.windBearing.degrees() - 0.5); + + } + + //Ensure that the wind is in the correct bounds. + if (this.windBearing.degrees() > this.windUpperBound.degrees()) { + this.windBearing.setBearing(this.windUpperBound); + + } else if (this.windBearing.degrees() < this.windLowerBound.degrees()) { + this.windBearing.setBearing(this.windLowerBound); + + } + } + + public Bearing getWindDirection() { + return this.windBearing; + } + + public double getWindSpeed() { + return this.windSpeed; + } + + public void setWindDirection(Bearing windBearing) { + this.windBearing = windBearing; + } + + public void setWindSpeed(double windSpeed) { + this.windSpeed = windSpeed; + } + + public void setDegrees(double degrees) { + this.windBearing.setDegrees(degrees); + } +} diff --git a/racevisionGame/src/main/java/shared/model/Race.java b/racevisionGame/src/main/java/shared/model/Race.java index 415e9f77..96bdf82a 100644 --- a/racevisionGame/src/main/java/shared/model/Race.java +++ b/racevisionGame/src/main/java/shared/model/Race.java @@ -2,6 +2,7 @@ package shared.model; import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleIntegerProperty; +import mock.model.Wind; import network.Messages.Enums.RaceStatusEnum; import network.Messages.Enums.RaceTypeEnum; import network.Messages.LatestMessages; @@ -100,13 +101,7 @@ public abstract class Race implements Runnable { /** * The current wind direction bearing. */ - protected Bearing windDirection; - - /** - * Wind speed (knots). - * Convert this to millimeters per second before passing to RaceStatus. - */ - protected double windSpeed; + protected Wind wind; /** @@ -169,12 +164,7 @@ public abstract class Race implements Runnable { //Race type. this.raceType = raceDataSource.getRaceType(); - //Wind speed. - this.windSpeed = 0; - //Wind direction. - this.windDirection = Bearing.fromDegrees(0); - - + this.wind = new Wind(); } @@ -259,7 +249,7 @@ public abstract class Race implements Runnable { * @return The wind bearing. */ public Bearing getWindDirection() { - return windDirection; + return wind.getWindDirection(); } /** @@ -268,7 +258,7 @@ public abstract class Race implements Runnable { * @return The wind speed. */ public double getWindSpeed() { - return windSpeed; + return wind.getWindSpeed(); } /** diff --git a/racevisionGame/src/main/java/visualiser/model/VisualiserRace.java b/racevisionGame/src/main/java/visualiser/model/VisualiserRace.java index 3a76631d..a98a8345 100644 --- a/racevisionGame/src/main/java/visualiser/model/VisualiserRace.java +++ b/racevisionGame/src/main/java/visualiser/model/VisualiserRace.java @@ -311,11 +311,11 @@ public class VisualiserRace extends Race { //Race status enum. this.raceStatusEnum = RaceStatusEnum.fromByte(raceStatus.getRaceStatus()); - //Wind bearing. - this.windDirection.setDegrees(raceStatus.getScaledWindDirection()); + // Wind direction + wind.setDegrees(raceStatus.getScaledWindDirection()); - //Wind speed. - this.windSpeed = raceStatus.getWindSpeedKnots(); + // Wind speed + wind.setWindSpeed(raceStatus.getWindSpeedKnots()); //Current race time. this.raceClock.setUTCTime(raceStatus.getCurrentTime()); From a4592a10e6248d1764877e83e041e670537cecea Mon Sep 17 00:00:00 2001 From: cbt24 Date: Wed, 2 Aug 2017 13:51:07 +1200 Subject: [PATCH 04/18] Removed boundary checking logic to clean up MockRace before split story[1094] --- .../src/main/java/mock/model/MockRace.java | 115 ++---------------- .../src/main/java/mock/model/RaceState.java | 18 +++ .../src/main/java/mock/model/RaceStatus.java | 4 - 3 files changed, 29 insertions(+), 108 deletions(-) create mode 100644 racevisionGame/src/main/java/mock/model/RaceState.java delete mode 100644 racevisionGame/src/main/java/mock/model/RaceStatus.java diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index ad259bdc..e7ee2dec 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -143,6 +143,13 @@ public class MockRace extends Race { initialiseWindDirection(); this.countdownTimer.start(); } + /** + * Sets the current race status. + * @param raceStatusEnum The new status of the race. + */ + protected void setRaceStatusEnum(RaceStatusEnum raceStatusEnum) { + this.raceStatusEnum = raceStatusEnum; + } /** @@ -565,18 +572,13 @@ public class MockRace extends Race { /** * Calculates a boat's VMG. * @param boat The boat to calculate VMG for. - * @param bearingBounds An array containing the lower and upper acceptable bearing bounds to keep the boat in the course. * @return VMG for the specified boat. */ - private VMG calculateVMG(MockBoat boat, Bearing[] bearingBounds) { - - //Get the lower and upper acceptable bounds. - Bearing lowerAcceptableBound = bearingBounds[0]; - Bearing upperAcceptableBound = bearingBounds[1]; + private VMG calculateVMG(MockBoat boat) { //Find the VMG inside these bounds. - VMG bestVMG = boat.getPolars().calculateVMG(this.windDirection, this.windSpeed, boat.calculateBearingToNextMarker(), lowerAcceptableBound, upperAcceptableBound); + VMG bestVMG = boat.getPolars().calculateVMG(this.windDirection, this.windSpeed, boat.calculateBearingToNextMarker(), Bearing.fromDegrees(0d), Bearing.fromDegrees(359.99999d)); return bestVMG; @@ -653,29 +655,16 @@ public class MockRace extends Race { //Move the boat forwards that many meters, and advances its time counters by enough milliseconds. boat.moveForwards(distanceTravelledMeters, updatePeriodMilliseconds * this.scaleFactor); - - //Only get a new VMG if the boat will go outside the course, or X seconds have elapsed. - boolean willStayInsideCourse = this.checkBearingInsideCourse(boat.getBearing(), boat.getCurrentPosition()); long tackPeriod = 15000; - if (!willStayInsideCourse || (boat.getTimeSinceTackChange() > tackPeriod)) { - - //Calculate the boat's bearing bounds, to ensure that it doesn't go out of the course. - Bearing[] bearingBounds = this.calculateBearingBounds(boat); - - + if (boat.getTimeSinceTackChange() > tackPeriod) { //Calculate the new VMG. - VMG newVMG = this.calculateVMG(boat, bearingBounds); + VMG newVMG = this.calculateVMG(boat); //If the new vmg improves velocity, use it. if (improvesVelocity(boat, newVMG)) { boat.setVMG(newVMG); - } else { - //We also need to use the new VMG if our current bearing will take us out of the course. - if (!willStayInsideCourse) { - boat.setVMG(newVMG); - } } } @@ -689,88 +678,6 @@ public class MockRace extends Race { } - /** - * Calculates the upper and lower bounds that the boat may have in order to not go outside of the course. - * @param boat The boat to check. - * @return An array of bearings. The first is the lower bound, the second is the upper bound. - */ - private Bearing[] calculateBearingBounds(MockBoat boat) { - - Bearing[] bearings = new Bearing[2]; - - Bearing lowerBearing = Bearing.fromDegrees(0.001); - Bearing upperBearing = Bearing.fromDegrees(359.999); - - - - double lastAngle = -1; - boolean lastAngleWasGood = false; - - //Check all bearings between [0, 360). - for (double angle = 0; angle < 360; angle += 1) { - - //Create bearing from angle. - Bearing bearing = Bearing.fromDegrees(angle); - - //Check that if it is acceptable. - boolean bearingIsGood = this.checkBearingInsideCourse(bearing, boat.getCurrentPosition()); - - - if (lastAngle != -1) { - - if (lastAngleWasGood && !bearingIsGood) { - //We have flipped over from good bearings to bad bearings. So the last good bearing is the upper bearing. - upperBearing = Bearing.fromDegrees(lastAngle); - } - - if (!lastAngleWasGood && bearingIsGood) { - //We have flipped over from bad bearings to good bearings. So the current bearing is the lower bearing. - lowerBearing = Bearing.fromDegrees(angle); - } - - } - - 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) - bearings[0] = lowerBearing; - bearings[1] = upperBearing; - - return bearings; - } - - - - /** - * 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 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) { - - //Get azimuth from bearing. - Azimuth azimuth = Azimuth.fromBearing(bearing); - - - //Tests to see if a point in front of the boat is out of bounds. - double epsilonMeters = 50d; - GPSCoordinate testCoord = GPSCoordinate.calculateNewPosition(position, epsilonMeters, azimuth); - - //If it isn't inside the boundary, calculate new bearing. - if (GPSCoordinate.isInsideBoundary(testCoord, this.shrinkBoundary)) { - return true; - } else { - return false; - } - - } - /** * Checks if a boat has finished any legs, or has pulled out of race (DNF). diff --git a/racevisionGame/src/main/java/mock/model/RaceState.java b/racevisionGame/src/main/java/mock/model/RaceState.java new file mode 100644 index 00000000..9e01deda --- /dev/null +++ b/racevisionGame/src/main/java/mock/model/RaceState.java @@ -0,0 +1,18 @@ +package mock.model; + +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; +import network.Messages.Enums.RaceStatusEnum; +import network.Messages.Enums.RaceTypeEnum; +import network.Messages.LatestMessages; +import shared.dataInput.BoatDataSource; +import shared.dataInput.RaceDataSource; +import shared.dataInput.RegattaDataSource; +import shared.model.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class RaceState { +} diff --git a/racevisionGame/src/main/java/mock/model/RaceStatus.java b/racevisionGame/src/main/java/mock/model/RaceStatus.java deleted file mode 100644 index 74e2823c..00000000 --- a/racevisionGame/src/main/java/mock/model/RaceStatus.java +++ /dev/null @@ -1,4 +0,0 @@ -package mock.model; - -public class RaceStatus { -} From fba256113a7acccba09bc808d50a49a89729b068 Mon Sep 17 00:00:00 2001 From: Joseph Gardner Date: Wed, 2 Aug 2017 15:09:59 +1200 Subject: [PATCH 05/18] Merged wind generator into this branch. #story[1094] --- .../src/main/java/mock/model/MockRace.java | 5 +- .../src/main/java/mock/model/Wind.java | 92 ------------------- 2 files changed, 4 insertions(+), 93 deletions(-) delete mode 100644 racevisionGame/src/main/java/mock/model/Wind.java diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index 845b00a6..78ce44b2 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -47,7 +47,10 @@ public class MockRace extends Race { */ private int scaleFactor; - + /** + * Object used to generate changes in wind speed/direction. + */ + private WindGenerator windGenerator; /** diff --git a/racevisionGame/src/main/java/mock/model/Wind.java b/racevisionGame/src/main/java/mock/model/Wind.java deleted file mode 100644 index 199cb98b..00000000 --- a/racevisionGame/src/main/java/mock/model/Wind.java +++ /dev/null @@ -1,92 +0,0 @@ -package mock.model; - -import shared.model.Bearing; - -import java.util.Random; - -public class Wind { - - /** - * Used to generate random numbers when changing the wind direction. - */ - private int changeWind = 4; - - /** - * The bearing the wind direction starts at. - */ - private Bearing windBearing; - - /** - * The lower bearing angle that the wind may have. - */ - private Bearing windLowerBound; - - /** - * The upper bearing angle that the wind may have. - */ - private Bearing windUpperBound; - - double windSpeed; - - public Wind() { - this.windBearing = Bearing.fromDegrees(225); - this.windSpeed = 12; - this.windLowerBound = Bearing.fromDegrees(215); - this.windUpperBound = Bearing.fromDegrees(235); - } - - public Wind(Bearing windBearing, double windSpeed, Bearing windLowerBound, Bearing windUpperBound) { - this.windBearing = windBearing; - this.windSpeed = windSpeed; - this.windLowerBound = windLowerBound; - this.windUpperBound = windUpperBound; - } - - /** - * Changes the wind direction randomly, while keeping it within [windLowerBound, windUpperBound]. - */ - public void changeWindDirection() { - - //Randomly add or remove 0.5 degrees. - int r = new Random().nextInt(changeWind) + 1; - - if (r == 1) { - //Add 0.5 degrees to the wind bearing. - this.windBearing.setDegrees(this.windBearing.degrees() + 0.5); - - } else if (r == 2) { - //Minus 0.5 degrees from the wind bearing. - this.windBearing.setDegrees(this.windBearing.degrees() - 0.5); - - } - - //Ensure that the wind is in the correct bounds. - if (this.windBearing.degrees() > this.windUpperBound.degrees()) { - this.windBearing.setBearing(this.windUpperBound); - - } else if (this.windBearing.degrees() < this.windLowerBound.degrees()) { - this.windBearing.setBearing(this.windLowerBound); - - } - } - - public Bearing getWindDirection() { - return this.windBearing; - } - - public double getWindSpeed() { - return this.windSpeed; - } - - public void setWindDirection(Bearing windBearing) { - this.windBearing = windBearing; - } - - public void setWindSpeed(double windSpeed) { - this.windSpeed = windSpeed; - } - - public void setDegrees(double degrees) { - this.windBearing.setDegrees(degrees); - } -} From b258e94a542e2af487474596efab9a8fdbeaae61 Mon Sep 17 00:00:00 2001 From: Joseph Gardner Date: Wed, 2 Aug 2017 15:19:32 +1200 Subject: [PATCH 06/18] Merged wind generator into this branch. #story[1094] --- .../src/main/java/mock/model/MockRace.java | 120 +----------------- 1 file changed, 5 insertions(+), 115 deletions(-) diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index 78ce44b2..fec34ad7 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -52,7 +52,6 @@ public class MockRace extends Race { */ private WindGenerator windGenerator; - /** * Constructs a race object with a given RaceDataSource, BoatDataSource, and RegattaDataSource and sends events to the given mockOutput. * @param boatDataSource Data source for boat related data (yachts and marker boats). @@ -72,7 +71,6 @@ 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), @@ -120,7 +118,6 @@ public class MockRace extends Race { */ public void run() { initialiseBoats(); - initialiseWindDirection(); this.countdownTimer.start(); } @@ -545,23 +542,13 @@ public class MockRace extends Race { /** * Calculates a boat's VMG. * @param boat The boat to calculate VMG for. - * @param bearingBounds An array containing the lower and upper acceptable bearing bounds to keep the boat in the course. * @return VMG for the specified boat. */ - private VMG calculateVMG(MockBoat boat, Bearing[] bearingBounds) { - - //Get the lower and upper acceptable bounds. - Bearing lowerAcceptableBound = bearingBounds[0]; - Bearing upperAcceptableBound = bearingBounds[1]; + private VMG calculateVMG(MockBoat boat) { //Find the VMG inside these bounds. - VMG bestVMG = boat.getPolars().calculateVMG( - this.getWindDirection(), - this.getWindSpeed(), - boat.calculateBearingToNextMarker(), - lowerAcceptableBound, - upperAcceptableBound); + VMG bestVMG = boat.getPolars().calculateVMG(this.getWindDirection(), this.getWindSpeed(), boat.calculateBearingToNextMarker(), Bearing.fromDegrees(0d), Bearing.fromDegrees(359.99999d)); return bestVMG; @@ -638,29 +625,16 @@ public class MockRace extends Race { //Move the boat forwards that many meters, and advances its time counters by enough milliseconds. boat.moveForwards(distanceTravelledMeters, updatePeriodMilliseconds * this.scaleFactor); - - //Only get a new VMG if the boat will go outside the course, or X seconds have elapsed. - boolean willStayInsideCourse = this.checkBearingInsideCourse(boat.getBearing(), boat.getCurrentPosition()); long tackPeriod = 15000; - if (!willStayInsideCourse || (boat.getTimeSinceTackChange() > tackPeriod)) { - - //Calculate the boat's bearing bounds, to ensure that it doesn't go out of the course. - Bearing[] bearingBounds = this.calculateBearingBounds(boat); - - + if (boat.getTimeSinceTackChange() > tackPeriod) { //Calculate the new VMG. - VMG newVMG = this.calculateVMG(boat, bearingBounds); + VMG newVMG = this.calculateVMG(boat); //If the new vmg improves velocity, use it. if (improvesVelocity(boat, newVMG)) { boat.setVMG(newVMG); - } else { - //We also need to use the new VMG if our current bearing will take us out of the course. - if (!willStayInsideCourse) { - boat.setVMG(newVMG); - } } } @@ -674,88 +648,6 @@ public class MockRace extends Race { } - /** - * Calculates the upper and lower bounds that the boat may have in order to not go outside of the course. - * @param boat The boat to check. - * @return An array of bearings. The first is the lower bound, the second is the upper bound. - */ - private Bearing[] calculateBearingBounds(MockBoat boat) { - - Bearing[] bearings = new Bearing[2]; - - Bearing lowerBearing = Bearing.fromDegrees(0.001); - Bearing upperBearing = Bearing.fromDegrees(359.999); - - - - double lastAngle = -1; - boolean lastAngleWasGood = false; - - //Check all bearings between [0, 360). - for (double angle = 0; angle < 360; angle += 1) { - - //Create bearing from angle. - Bearing bearing = Bearing.fromDegrees(angle); - - //Check that if it is acceptable. - boolean bearingIsGood = this.checkBearingInsideCourse(bearing, boat.getCurrentPosition()); - - - if (lastAngle != -1) { - - if (lastAngleWasGood && !bearingIsGood) { - //We have flipped over from good bearings to bad bearings. So the last good bearing is the upper bearing. - upperBearing = Bearing.fromDegrees(lastAngle); - } - - if (!lastAngleWasGood && bearingIsGood) { - //We have flipped over from bad bearings to good bearings. So the current bearing is the lower bearing. - lowerBearing = Bearing.fromDegrees(angle); - } - - } - - 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) - bearings[0] = lowerBearing; - bearings[1] = upperBearing; - - return bearings; - } - - - - /** - * 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 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) { - - //Get azimuth from bearing. - Azimuth azimuth = Azimuth.fromBearing(bearing); - - - //Tests to see if a point in front of the boat is out of bounds. - double epsilonMeters = 50d; - GPSCoordinate testCoord = GPSCoordinate.calculateNewPosition(position, epsilonMeters, azimuth); - - //If it isn't inside the boundary, calculate new bearing. - if (GPSCoordinate.isInsideBoundary(testCoord, this.shrinkBoundary)) { - return true; - } else { - return false; - } - - } - /** * Checks if a boat has finished any legs, or has pulled out of race (DNF). @@ -831,7 +723,6 @@ public class MockRace extends Race { return boats; } - /** * Initialises the wind bearing with the value of the windBaselineBearing. */ @@ -840,7 +731,6 @@ public class MockRace extends Race { this.setWind(windGenerator.generateBaselineWind()); } - /** * Changes the wind direction randomly, while keeping it within [windLowerBound, windUpperBound]. */ @@ -872,4 +762,4 @@ public class MockRace extends Race { } } -} +} \ No newline at end of file From 7fc1347377dee56a665411cf94625cc38dac7d48 Mon Sep 17 00:00:00 2001 From: zwu18 Date: Wed, 2 Aug 2017 22:28:47 +1200 Subject: [PATCH 07/18] Created class TackGybeCommand which implements the CommandFactory interface. #story[1097] --- .../src/main/java/mock/model/MockRace.java | 1 - .../mock/model/commandFactory/CommandFactory.java | 3 +++ .../model/commandFactory/TackGybeCommand.java | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 racevisionGame/src/main/java/mock/model/commandFactory/TackGybeCommand.java diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index fec34ad7..c14e337f 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -630,7 +630,6 @@ public class MockRace extends Race { //Calculate the new VMG. VMG newVMG = this.calculateVMG(boat); - //If the new vmg improves velocity, use it. if (improvesVelocity(boat, newVMG)) { boat.setVMG(newVMG); diff --git a/racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java b/racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java index c073fc95..0e21d68c 100644 --- a/racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java +++ b/racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java @@ -1,4 +1,7 @@ package mock.model.commandFactory; +import mock.model.MockBoat; + public interface CommandFactory { + void runCommand(MockBoat boat); } diff --git a/racevisionGame/src/main/java/mock/model/commandFactory/TackGybeCommand.java b/racevisionGame/src/main/java/mock/model/commandFactory/TackGybeCommand.java new file mode 100644 index 00000000..80b1fb23 --- /dev/null +++ b/racevisionGame/src/main/java/mock/model/commandFactory/TackGybeCommand.java @@ -0,0 +1,15 @@ +package mock.model.commandFactory; + +import mock.model.MockBoat; + +/** + * Created by David on 2/08/2017. + */ +public class TackGybeCommand implements CommandFactory { + + + @Override + public void runCommand(MockBoat boat) { + + } +} From 0466292bd06896d2ed7e550da6aee3ba9f659b76 Mon Sep 17 00:00:00 2001 From: Connor Taylor-Brown Date: Wed, 2 Aug 2017 23:39:13 +1200 Subject: [PATCH 08/18] Partially removed VMG optimisation, separated server-specific functionality from MockRace into RaceServer. #story[1094] --- .../src/main/java/mock/model/MockBoat.java | 15 +- .../src/main/java/mock/model/MockRace.java | 239 ++---------------- .../src/main/java/mock/model/RaceServer.java | 153 +++++++++++ .../src/main/java/shared/model/Race.java | 16 +- 4 files changed, 185 insertions(+), 238 deletions(-) create mode 100644 racevisionGame/src/main/java/mock/model/RaceServer.java diff --git a/racevisionGame/src/main/java/mock/model/MockBoat.java b/racevisionGame/src/main/java/mock/model/MockBoat.java index c8c6825b..597acae7 100644 --- a/racevisionGame/src/main/java/mock/model/MockBoat.java +++ b/racevisionGame/src/main/java/mock/model/MockBoat.java @@ -135,20 +135,9 @@ public class MockBoat extends Boat { /** * Moves the boat meters forward in the direction that it is facing * @param meters The number of meters to move forward. - * @param milliseconds The number of milliseconds to advance the boat's timers by. + * */ - public void moveForwards(double meters, long milliseconds) { - - - //Update the boat's time since last tack. - this.setTimeSinceTackChange(this.getTimeSinceTackChange() + milliseconds); - - //Update the time into the current leg. - this.setTimeElapsedInCurrentLeg(this.getTimeElapsedInCurrentLeg() + milliseconds); - - //Update the distance into the current leg. - this.setDistanceTravelledInLeg(this.getDistanceTravelledInLeg() + meters); - + public void moveForwards(double meters) { //Updates the current position of the boat. GPSCoordinate newPosition = GPSCoordinate.calculateNewPosition(this.getCurrentPosition(), meters, Azimuth.fromBearing(this.getBearing())); this.setCurrentPosition(newPosition); diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index fec34ad7..08c56a2d 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -15,7 +15,6 @@ import shared.model.*; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; -import java.time.temporal.TemporalUnit; import java.util.*; import static java.lang.Math.cos; @@ -27,6 +26,7 @@ import static java.lang.Math.cos; * Is responsible for simulating the race, and sending messages to a MockOutput instance. */ public class MockRace extends Race { + private RaceServer server; /** * An observable list of boats in the race. @@ -82,6 +82,8 @@ public class MockRace extends Race { //Wind. this.setWind(windGenerator.generateBaselineWind()); + + this.server = new RaceServer(this, latestMessages); } /** @@ -122,88 +124,6 @@ public class MockRace extends Race { } - /** - * Parse the compound marker boats through mock output. - */ - private void parseMarks() { - for (CompoundMark compoundMark : this.compoundMarks) { - - //Get the individual marks from the compound mark. - Mark mark1 = compoundMark.getMark1(); - Mark mark2 = compoundMark.getMark2(); - - //If they aren't null, parse them (some compound marks only have one mark). - if (mark1 != null) { - this.parseIndividualMark(mark1); - } - - if (mark2 != null) { - this.parseIndividualMark(mark2); - } - - } - } - - /** - * Parses an individual marker boat, and sends it to mockOutput. - * @param mark The marker boat to parse. - */ - private void parseIndividualMark(Mark mark) { - - //Create message. - BoatLocation boatLocation = new BoatLocation( - mark.getSourceID(), - mark.getPosition().getLatitude(), - mark.getPosition().getLongitude(), - this.boatLocationSequenceNumber, - 0, 0, - this.raceClock.getCurrentTimeMilli()); - - //Iterates the sequence number. - this.boatLocationSequenceNumber++; - - this.latestMessages.setBoatLocation(boatLocation); - - - } - - /** - * Parse the boats in the race, and send it to mockOutput. - */ - private void parseBoatLocations() { - - //Parse each boat. - for (MockBoat boat : this.boats) { - - this.parseIndividualBoatLocation(boat); - - } - - } - - /** - * Parses an individual boat, and sends it to mockOutput. - * @param boat The boat to parse. - */ - private void parseIndividualBoatLocation(MockBoat boat) { - - BoatLocation boatLocation = new BoatLocation( - boat.getSourceID(), - boat.getCurrentPosition().getLatitude(), - boat.getCurrentPosition().getLongitude(), - this.boatLocationSequenceNumber, - boat.getBearing().degrees(), - boat.getCurrentSpeed(), - this.raceClock.getCurrentTimeMilli()); - - //Iterates the sequence number. - this.boatLocationSequenceNumber++; - - this.latestMessages.setBoatLocation(boatLocation); - - } - - /** * Updates the race time to a specified value, in milliseconds since the unix epoch. * @param currentTime Milliseconds since unix epoch. @@ -241,48 +161,6 @@ public class MockRace extends Race { } - } - - /** - * Parses the race status, and sends it to mockOutput. - */ - private void parseRaceStatus() { - - //A race status message contains a list of boat statuses. - List boatStatuses = new ArrayList<>(); - - //Add each boat status to the status list. - for (MockBoat boat : this.boats) { - - BoatStatus boatStatus = new BoatStatus( - boat.getSourceID(), - boat.getStatus(), - boat.getCurrentLeg().getLegNumber(), - boat.getEstimatedTimeAtNextMark().toInstant().toEpochMilli() ); - - boatStatuses.add(boatStatus); - } - - - //Convert wind direction and speed to ints. //TODO this conversion should be done inside the racestatus class. - int windDirectionInt = AC35UnitConverter.encodeHeading(this.getWindDirection().degrees()); - int windSpeedInt = (int) (this.getWindSpeed() * Constants.KnotsToMMPerSecond); - - //Create race status object, and send it. - RaceStatus raceStatus = new RaceStatus( - System.currentTimeMillis(), - this.raceId, - this.getRaceStatusEnum().getValue(), - this.raceClock.getStartingTimeMilli(), - windDirectionInt, - windSpeedInt, - this.getRaceType().getValue(), - boatStatuses); - - - this.latestMessages.setRaceStatus(raceStatus); - - } @@ -330,16 +208,16 @@ public class MockRace extends Race { setBoatsTimeNextMark(raceClock.getCurrentTime()); //Parse the boat locations. - parseBoatLocations(); + server.parseBoatLocations(); //Parse the marks. - parseMarks(); + server.parseMarks(); // Change wind direction changeWindDirection(); //Parse the race status. - parseRaceStatus(); + server.parseRaceStatus(); if (getRaceStatusEnum() == RaceStatusEnum.STARTED) { @@ -414,13 +292,13 @@ public class MockRace extends Race { changeWindDirection(); //Parse the boat locations. - parseBoatLocations(); + server.parseBoatLocations(); //Parse the marks. - parseMarks(); + server.parseMarks(); //Parse the race status. - parseRaceStatus(); + server.parseRaceStatus(); //Update the last frame time. @@ -437,7 +315,7 @@ public class MockRace extends Race { @Override public void handle(long now) { - parseRaceStatus(); + server.parseRaceStatus(); if (iters > 500) { stop(); @@ -452,7 +330,7 @@ public class MockRace extends Race { * This sets their starting positions and current legs. */ @Override - protected void initialiseBoats() { + public void initialiseBoats() { //Gets the starting positions of the boats. List startingPositions = getSpreadStartingPositions(); @@ -497,7 +375,7 @@ public class MockRace extends Race { * * @return A list of starting positions. */ - public List getSpreadStartingPositions() { + private List getSpreadStartingPositions() { //The first compound marker of the race - the starting gate. CompoundMark compoundMark = this.legs.get(0).getStartCompoundMark(); @@ -539,23 +417,6 @@ public class MockRace extends Race { } - /** - * Calculates a boat's VMG. - * @param boat The boat to calculate VMG for. - * @return VMG for the specified boat. - */ - private VMG calculateVMG(MockBoat boat) { - - - //Find the VMG inside these bounds. - VMG bestVMG = boat.getPolars().calculateVMG(this.getWindDirection(), this.getWindSpeed(), boat.calculateBearingToNextMarker(), Bearing.fromDegrees(0d), Bearing.fromDegrees(359.99999d)); - - - return bestVMG; - - } - - /** * Determines whether or not a given VMG improves the velocity of a boat, if it were currently using currentVMG. * @param currentVMG The current VMG of the boat. @@ -607,7 +468,7 @@ public class MockRace extends Race { * @param updatePeriodMilliseconds The time, in milliseconds, since the last update. * @param totalElapsedMilliseconds The total number of milliseconds that have elapsed since the start of the race. */ - protected void updatePosition(MockBoat boat, long updatePeriodMilliseconds, long totalElapsedMilliseconds) { + public void updatePosition(MockBoat boat, long updatePeriodMilliseconds, long totalElapsedMilliseconds) { //Checks if the current boat has finished the race or not. boolean finish = this.isLastLeg(boat.getCurrentLeg()); @@ -623,12 +484,18 @@ public class MockRace extends Race { //Move the boat forwards that many meters, and advances its time counters by enough milliseconds. - boat.moveForwards(distanceTravelledMeters, updatePeriodMilliseconds * this.scaleFactor); + boat.moveForwards(distanceTravelledMeters); long tackPeriod = 15000; + if (boat.getTimeSinceTackChange() > tackPeriod) { //Calculate the new VMG. - VMG newVMG = this.calculateVMG(boat); + VMG newVMG = boat.getPolars().calculateVMG( + this.getWindDirection(), + this.getWindSpeed(), + boat.calculateBearingToNextMarker(), + Bearing.fromDegrees(0d), + Bearing.fromDegrees(359.99999d)); //If the new vmg improves velocity, use it. @@ -639,56 +506,6 @@ public class MockRace extends Race { } this.updateEstimatedTime(boat); - - - //Check the boats position (update leg and stuff). - this.checkPosition(boat, totalElapsedMilliseconds); - - } - - } - - - /** - * Checks if a boat has finished any legs, or has pulled out of race (DNF). - * @param boat The boat to check. - * @param timeElapsed The total time, in milliseconds, that has elapsed since the race started. - */ - protected void checkPosition(MockBoat boat, long timeElapsed) { - - //The distance, in nautical miles, within which the boat needs to get in order to consider that it has reached the marker. - double epsilonNauticalMiles = 100.0 / Constants.NMToMetersConversion; //100 meters. TODO should be more like 5-10. - - if (boat.calculateDistanceToNextMarker() < epsilonNauticalMiles) { - //Boat has reached its target marker, and has moved on to a new leg. - - - - //Calculate how much the boat overshot the marker by. - double overshootMeters = boat.calculateDistanceToNextMarker(); - - - //Move boat on to next leg. - Leg nextLeg = this.legs.get(boat.getCurrentLeg().getLegNumber() + 1); - boat.setCurrentLeg(nextLeg); - - //Add overshoot distance into the distance travelled for the next leg. - boat.setDistanceTravelledInLeg(overshootMeters); - - //Setting a high value for this allows the boat to immediately do a large turn, as it needs to in order to get to the next mark. - boat.setTimeSinceTackChange(999999); - - - //Check if the boat has finished or stopped racing. - - if (this.isLastLeg(boat.getCurrentLeg())) { - //Boat has finished. - boat.setTimeFinished(timeElapsed); - boat.setCurrentSpeed(0); - boat.setStatus(BoatStatusEnum.FINISHED); - - } - } } @@ -723,18 +540,10 @@ public class MockRace extends Race { return boats; } - /** - * Initialises the wind bearing with the value of the windBaselineBearing. - */ - protected void initialiseWindDirection() { - //Set the starting bearing. - this.setWind(windGenerator.generateBaselineWind()); - } - /** * Changes the wind direction randomly, while keeping it within [windLowerBound, windUpperBound]. */ - protected void changeWindDirection() { + public void changeWindDirection() { Wind nextWind = windGenerator.generateNextWind(raceWind.getValue()); @@ -762,4 +571,8 @@ public class MockRace extends Race { } } + + public List getCompoundMarks() { + return compoundMarks; + } } \ No newline at end of file diff --git a/racevisionGame/src/main/java/mock/model/RaceServer.java b/racevisionGame/src/main/java/mock/model/RaceServer.java new file mode 100644 index 00000000..c7e3ab69 --- /dev/null +++ b/racevisionGame/src/main/java/mock/model/RaceServer.java @@ -0,0 +1,153 @@ +package mock.model; + +import network.Messages.BoatLocation; +import network.Messages.BoatStatus; +import network.Messages.LatestMessages; +import network.Messages.RaceStatus; +import network.Utils.AC35UnitConverter; +import shared.model.CompoundMark; +import shared.model.Constants; +import shared.model.Mark; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by connortaylorbrown on 2/08/17. + */ +public class RaceServer { + private MockRace race; + private LatestMessages latestMessages; + + /** + * The sequence number of the latest RaceStatus message sent or received. + */ + private int raceStatusSequenceNumber = 1; + + /** + * The sequence number of the latest BoatLocation message sent or received. + */ + private int boatLocationSequenceNumber = 1; + + + public RaceServer(MockRace race, LatestMessages latestMessages) { + this.race = race; + this.latestMessages = latestMessages; + } + + + /** + * Parses an individual marker boat, and sends it to mockOutput. + * @param mark The marker boat to parse. + */ + private void parseIndividualMark(Mark mark) { + //Create message. + BoatLocation boatLocation = new BoatLocation( + mark.getSourceID(), + mark.getPosition().getLatitude(), + mark.getPosition().getLongitude(), + this.boatLocationSequenceNumber, + 0, 0, + race.getRaceClock().getCurrentTimeMilli()); + + //Iterates the sequence number. + this.boatLocationSequenceNumber++; + + this.latestMessages.setBoatLocation(boatLocation); + } + + /** + * Parse the compound marker boats through mock output. + */ + public void parseMarks() { + for (CompoundMark compoundMark : race.getCompoundMarks()) { + + //Get the individual marks from the compound mark. + Mark mark1 = compoundMark.getMark1(); + Mark mark2 = compoundMark.getMark2(); + + //If they aren't null, parse them (some compound marks only have one mark). + if (mark1 != null) { + this.parseIndividualMark(mark1); + } + + if (mark2 != null) { + this.parseIndividualMark(mark2); + } + } + } + + + /** + * Parse the boats in the race, and send it to mockOutput. + */ + public void parseBoatLocations() { + //Parse each boat. + for (MockBoat boat : race.getBoats()) { + this.parseIndividualBoatLocation(boat); + } + } + + /** + * Parses an individual boat, and sends it to mockOutput. + * @param boat The boat to parse. + */ + private void parseIndividualBoatLocation(MockBoat boat) { + + BoatLocation boatLocation = new BoatLocation( + boat.getSourceID(), + boat.getCurrentPosition().getLatitude(), + boat.getCurrentPosition().getLongitude(), + this.boatLocationSequenceNumber, + boat.getBearing().degrees(), + boat.getCurrentSpeed(), + race.getRaceClock().getCurrentTimeMilli()); + + //Iterates the sequence number. + this.boatLocationSequenceNumber++; + + this.latestMessages.setBoatLocation(boatLocation); + + } + + + + /** + * Parses the race status, and sends it to mockOutput. + */ + public void parseRaceStatus() { + + //A race status message contains a list of boat statuses. + List boatStatuses = new ArrayList<>(); + + //Add each boat status to the status list. + for (MockBoat boat : race.getBoats()) { + + BoatStatus boatStatus = new BoatStatus( + boat.getSourceID(), + boat.getStatus(), + boat.getCurrentLeg().getLegNumber(), + boat.getEstimatedTimeAtNextMark().toInstant().toEpochMilli() ); + + boatStatuses.add(boatStatus); + } + + + //Convert wind direction and speed to ints. //TODO this conversion should be done inside the racestatus class. + int windDirectionInt = AC35UnitConverter.encodeHeading(race.getWindDirection().degrees()); + int windSpeedInt = (int) (race.getWindSpeed() * Constants.KnotsToMMPerSecond); + + //Create race status object, and send it. + RaceStatus raceStatus = new RaceStatus( + System.currentTimeMillis(), + race.getRaceId(), + race.getRaceStatusEnum().getValue(), + race.getRaceClock().getStartingTimeMilli(), + windDirectionInt, + windSpeedInt, + race.getRaceType().getValue(), + boatStatuses); + + this.latestMessages.setRaceStatus(raceStatus); + } +} diff --git a/racevisionGame/src/main/java/shared/model/Race.java b/racevisionGame/src/main/java/shared/model/Race.java index aec57882..040e0d19 100644 --- a/racevisionGame/src/main/java/shared/model/Race.java +++ b/racevisionGame/src/main/java/shared/model/Race.java @@ -43,18 +43,6 @@ public abstract class Race implements Runnable { */ protected LatestMessages latestMessages; - /** - * The sequence number of the latest BoatLocation message sent or received. - */ - protected int boatLocationSequenceNumber = 1; - - /** - * The sequence number of the latest RaceStatus message sent or received. - */ - protected int raceStatusSequenceNumber = 1; - - - /** * A list of compound marks in the race. */ @@ -364,4 +352,8 @@ public abstract class Race implements Runnable { this.lastFpsResetTime = 0; } } + + public int getRaceId() { + return raceId; + } } From ad61dc6bcef719028364422465dc779a200d7bd3 Mon Sep 17 00:00:00 2001 From: zwu18 Date: Thu, 3 Aug 2017 00:22:46 +1200 Subject: [PATCH 09/18] Implemented runCommand method in VMGCommand and TackGybeCommand classes. Both classes currently rely on the MockRace class so changes will be required when MockRace is refactored. #story[1097] --- .../src/main/java/mock/model/MockBoat.java | 1 - .../src/main/java/mock/model/MockRace.java | 12 +++++++----- .../model/commandFactory/CommandFactory.java | 4 +++- .../model/commandFactory/TackGybeCommand.java | 12 +++++++++--- .../mock/model/commandFactory/VMGCommand.java | 19 +++++++++++++++++++ .../src/main/java/shared/model/Constants.java | 2 +- 6 files changed, 39 insertions(+), 11 deletions(-) create mode 100644 racevisionGame/src/main/java/mock/model/commandFactory/VMGCommand.java diff --git a/racevisionGame/src/main/java/mock/model/MockBoat.java b/racevisionGame/src/main/java/mock/model/MockBoat.java index c8c6825b..dde207e3 100644 --- a/racevisionGame/src/main/java/mock/model/MockBoat.java +++ b/racevisionGame/src/main/java/mock/model/MockBoat.java @@ -66,7 +66,6 @@ public class MockBoat extends Boat { //Calculate bearing. Bearing bearing = GPSCoordinate.calculateBearing(currentPosition, nextMarkerPosition); - return bearing; } diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index c14e337f..56fe760a 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -383,7 +383,6 @@ public class MockRace extends Race { //Update race time. updateRaceTime(currentTime); - //As long as there is at least one boat racing, we still simulate the race. if (getNumberOfActiveBoats() != 0) { @@ -392,7 +391,6 @@ public class MockRace extends Race { //For each boat, we update its position, and generate a BoatLocationMessage. for (MockBoat boat : boats) { - //If it is still racing, update its position. if (boat.getStatus() == BoatStatusEnum.RACING) { @@ -544,7 +542,7 @@ public class MockRace extends Race { * @param boat The boat to calculate VMG for. * @return VMG for the specified boat. */ - private VMG calculateVMG(MockBoat boat) { + public VMG calculateVMG(MockBoat boat) { //Find the VMG inside these bounds. @@ -563,14 +561,14 @@ public class MockRace extends Race { * @param bearingToDestination The bearing between the boat and its destination. * @return True if the new VMG is improves velocity, false otherwise. */ - private boolean improvesVelocity(VMG currentVMG, VMG potentialVMG, Bearing bearingToDestination) { + public boolean improvesVelocity(VMG currentVMG, VMG potentialVMG, Bearing bearingToDestination) { //Calculates the angle between the boat and its destination. Angle angleBetweenDestAndHeading = Angle.fromDegrees(currentVMG.getBearing().degrees() - bearingToDestination.degrees()); //Calculates the angle between the new VMG and the boat's destination. Angle angleBetweenDestAndNewVMG = Angle.fromDegrees(potentialVMG.getBearing().degrees() - bearingToDestination.degrees()); - + //System.out.println(angleBetweenDestAndHeading.degrees() + ":" + angleBetweenDestAndNewVMG.degrees()); //Calculate the boat's current velocity. double currentVelocity = Math.cos(angleBetweenDestAndHeading.radians()) * currentVMG.getSpeed(); @@ -581,6 +579,7 @@ public class MockRace extends Race { //Return whether or not the new VMG gives better velocity. return vmgVelocity > currentVelocity; + } /** @@ -625,13 +624,16 @@ public class MockRace extends Race { //Move the boat forwards that many meters, and advances its time counters by enough milliseconds. boat.moveForwards(distanceTravelledMeters, updatePeriodMilliseconds * this.scaleFactor); + long tackPeriod = 15000; if (boat.getTimeSinceTackChange() > tackPeriod) { + System.out.println("tack loop"); //Calculate the new VMG. VMG newVMG = this.calculateVMG(boat); //If the new vmg improves velocity, use it. if (improvesVelocity(boat, newVMG)) { + System.out.println("NEW VMG"); boat.setVMG(newVMG); } diff --git a/racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java b/racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java index 0e21d68c..ace2f3be 100644 --- a/racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java +++ b/racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java @@ -1,7 +1,9 @@ package mock.model.commandFactory; import mock.model.MockBoat; +import mock.model.MockRace; public interface CommandFactory { - void runCommand(MockBoat boat); + + void runCommand(MockBoat boat, MockRace race); } diff --git a/racevisionGame/src/main/java/mock/model/commandFactory/TackGybeCommand.java b/racevisionGame/src/main/java/mock/model/commandFactory/TackGybeCommand.java index 80b1fb23..4f1f848e 100644 --- a/racevisionGame/src/main/java/mock/model/commandFactory/TackGybeCommand.java +++ b/racevisionGame/src/main/java/mock/model/commandFactory/TackGybeCommand.java @@ -1,15 +1,21 @@ package mock.model.commandFactory; import mock.model.MockBoat; +import mock.model.MockRace; +import mock.model.VMG; /** * Created by David on 2/08/2017. */ public class TackGybeCommand implements CommandFactory { - + //The refactoring of MockRace will require changes to be made @Override - public void runCommand(MockBoat boat) { - + public void runCommand(MockBoat boat, MockRace race) { + VMG newVMG = race.calculateVMG(boat); + VMG boatVMG = new VMG(boat.getCurrentSpeed(), boat.getBearing()); + if(race.improvesVelocity(boatVMG, newVMG, boat.calculateBearingToNextMarker())){ + boat.setVMG(newVMG); + } } } diff --git a/racevisionGame/src/main/java/mock/model/commandFactory/VMGCommand.java b/racevisionGame/src/main/java/mock/model/commandFactory/VMGCommand.java new file mode 100644 index 00000000..2f0f527d --- /dev/null +++ b/racevisionGame/src/main/java/mock/model/commandFactory/VMGCommand.java @@ -0,0 +1,19 @@ +package mock.model.commandFactory; + +import mock.model.MockBoat; +import mock.model.MockRace; +import mock.model.VMG; +import shared.model.Bearing; + +/** + * Created by David on 2/08/2017. + */ +public class VMGCommand implements CommandFactory { + + //The refactoring of MockRace will require changes to be made + @Override + public void runCommand(MockBoat boat, MockRace race) { + VMG newVMG = race.calculateVMG(boat); + boat.setVMG(newVMG); + } +} diff --git a/racevisionGame/src/main/java/shared/model/Constants.java b/racevisionGame/src/main/java/shared/model/Constants.java index 51666bf2..a6fe3844 100644 --- a/racevisionGame/src/main/java/shared/model/Constants.java +++ b/racevisionGame/src/main/java/shared/model/Constants.java @@ -38,7 +38,7 @@ public class Constants { /** * The race pre-start time, in milliseconds. 3 minutes. */ - public static final long RacePreStartTime = 3 * 60 * 1000; + public static final long RacePreStartTime = 1 * 10 * 1000; /** From 2672c2b13b2a02ed3a0589bd76edbb7a24153c0e Mon Sep 17 00:00:00 2001 From: Connor Taylor-Brown Date: Thu, 3 Aug 2017 00:32:17 +1200 Subject: [PATCH 10/18] Separated RaceLogic from MockRace - MockRace satisfies refactor requirement for RaceState - VisualiserRace directly implements Runnable to remove method from MockRace #story[1094] --- .../src/main/java/mock/app/Event.java | 3 +- .../src/main/java/mock/model/MockRace.java | 163 +---------------- .../src/main/java/mock/model/RaceLogic.java | 172 +++++++++++++++++- .../src/main/java/shared/model/Race.java | 4 +- .../java/visualiser/model/VisualiserRace.java | 2 +- 5 files changed, 180 insertions(+), 164 deletions(-) diff --git a/racevisionGame/src/main/java/mock/app/Event.java b/racevisionGame/src/main/java/mock/app/Event.java index c94a691f..b4b0586c 100644 --- a/racevisionGame/src/main/java/mock/app/Event.java +++ b/racevisionGame/src/main/java/mock/app/Event.java @@ -3,6 +3,7 @@ package mock.app; import mock.dataInput.PolarParser; import mock.model.MockRace; import mock.model.Polars; +import mock.model.RaceLogic; import network.Messages.LatestMessages; import shared.dataInput.*; import shared.enums.XMLFileType; @@ -91,7 +92,7 @@ public class Event { RegattaDataSource regattaDataSource = new RegattaXMLReader(this.regattaXML, this.xmlFileType); //Create and start race. - MockRace newRace = new MockRace(boatDataSource, raceDataSource, regattaDataSource, this.latestMessages, this.boatPolars, Constants.RaceTimeScale); + RaceLogic newRace = new RaceLogic(new MockRace(boatDataSource, raceDataSource, regattaDataSource, this.latestMessages, this.boatPolars, Constants.RaceTimeScale), this.latestMessages); new Thread(newRace).start(); } diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index 08c56a2d..a32787d2 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -1,12 +1,7 @@ package mock.model; -import javafx.animation.AnimationTimer; -import network.Messages.BoatLocation; -import network.Messages.BoatStatus; import network.Messages.Enums.BoatStatusEnum; import network.Messages.LatestMessages; -import network.Messages.RaceStatus; -import network.Utils.AC35UnitConverter; import shared.dataInput.BoatDataSource; import shared.dataInput.RaceDataSource; import network.Messages.Enums.RaceStatusEnum; @@ -26,7 +21,6 @@ import static java.lang.Math.cos; * Is responsible for simulating the race, and sending messages to a MockOutput instance. */ public class MockRace extends Race { - private RaceServer server; /** * An observable list of boats in the race. @@ -82,8 +76,6 @@ public class MockRace extends Race { //Wind. this.setWind(windGenerator.generateBaselineWind()); - - this.server = new RaceServer(this, latestMessages); } /** @@ -115,20 +107,11 @@ public class MockRace extends Race { } - /** - * Runnable for the thread. - */ - public void run() { - initialiseBoats(); - this.countdownTimer.start(); - } - - /** * Updates the race time to a specified value, in milliseconds since the unix epoch. * @param currentTime Milliseconds since unix epoch. */ - private void updateRaceTime(long currentTime) { + public void updateRaceTime(long currentTime) { this.raceClock.setUTCTime(currentTime); } @@ -136,7 +119,7 @@ public class MockRace extends Race { /** * Updates the race status enumeration based on the current time. */ - private void updateRaceStatusEnum() { + public void updateRaceStatusEnum() { //The millisecond duration of the race. Negative means it hasn't started, so we flip sign. long timeToStart = - this.raceClock.getDurationMilli(); @@ -167,7 +150,7 @@ public class MockRace extends Race { /** * Sets the status of all boats in the race to RACING. */ - private void setBoatsStatusToRacing() { + public void setBoatsStatusToRacing() { for (MockBoat boat : this.boats) { boat.setStatus(BoatStatusEnum.RACING); @@ -179,7 +162,7 @@ public class MockRace extends Race { * Sets the estimated time at next mark for each boat to a specified time. This is used during the countdown timer to provide this value to boat before the race starts. * @param time The time to provide to each boat. */ - private void setBoatsTimeNextMark(ZonedDateTime time) { + public void setBoatsTimeNextMark(ZonedDateTime time) { for (MockBoat boat : this.boats) { boat.setEstimatedTimeAtNextMark(time); @@ -187,144 +170,6 @@ public class MockRace extends Race { } - /** - * Countdown timer until race starts. - */ - protected AnimationTimer countdownTimer = new AnimationTimer() { - - - long currentTime = System.currentTimeMillis(); - - @Override - public void handle(long arg0) { - - //Update race time. - updateRaceTime(currentTime); - - //Update the race status based on the current time. - updateRaceStatusEnum(); - - //Provide boat's with an estimated time at next mark until the race starts. - setBoatsTimeNextMark(raceClock.getCurrentTime()); - - //Parse the boat locations. - server.parseBoatLocations(); - - //Parse the marks. - server.parseMarks(); - - // Change wind direction - changeWindDirection(); - - //Parse the race status. - server.parseRaceStatus(); - - - if (getRaceStatusEnum() == RaceStatusEnum.STARTED) { - setBoatsStatusToRacing(); - raceTimer.start(); - this.stop(); - } - - //Update the animations timer's time. - currentTime = System.currentTimeMillis(); - } - }; - - - /** - * Timer that runs for the duration of the race, until all boats finish. - */ - private AnimationTimer raceTimer = new AnimationTimer() { - - /** - * Start time of loop, in milliseconds. - */ - long timeRaceStarted = System.currentTimeMillis(); - - /** - * Current time during a loop iteration. - */ - long currentTime = System.currentTimeMillis(); - - /** - * The time of the previous frame, in milliseconds. - */ - long lastFrameTime = timeRaceStarted; - - @Override - public void handle(long arg0) { - - //Get the current time. - currentTime = System.currentTimeMillis(); - - //Update race time. - updateRaceTime(currentTime); - - - //As long as there is at least one boat racing, we still simulate the race. - if (getNumberOfActiveBoats() != 0) { - - //Get the time period of this frame. - long framePeriod = currentTime - lastFrameTime; - - //For each boat, we update its position, and generate a BoatLocationMessage. - for (MockBoat boat : boats) { - - //If it is still racing, update its position. - if (boat.getStatus() == BoatStatusEnum.RACING) { - - updatePosition(boat, framePeriod, raceClock.getDurationMilli()); - - } - - } - - } else { - //Otherwise, the race is over! - raceFinished.start(); - setRaceStatusEnum(RaceStatusEnum.FINISHED); - this.stop(); - } - - if (getNumberOfActiveBoats() != 0) { - // Change wind direction - changeWindDirection(); - - //Parse the boat locations. - server.parseBoatLocations(); - - //Parse the marks. - server.parseMarks(); - - //Parse the race status. - server.parseRaceStatus(); - - - //Update the last frame time. - this.lastFrameTime = currentTime; - } - } - }; - - /** - * Broadcast that the race has finished. - */ - protected AnimationTimer raceFinished = new AnimationTimer(){ - int iters = 0; - @Override - public void handle(long now) { - - server.parseRaceStatus(); - - if (iters > 500) { - stop(); - } - iters++; - } - }; - - /** * Initialise the boats in the race. * This sets their starting positions and current legs. diff --git a/racevisionGame/src/main/java/mock/model/RaceLogic.java b/racevisionGame/src/main/java/mock/model/RaceLogic.java index b8e904a6..73b38c0d 100644 --- a/racevisionGame/src/main/java/mock/model/RaceLogic.java +++ b/racevisionGame/src/main/java/mock/model/RaceLogic.java @@ -1,4 +1,174 @@ package mock.model; -public class RaceLogic { +import javafx.animation.AnimationTimer; +import network.Messages.Enums.BoatStatusEnum; +import network.Messages.Enums.RaceStatusEnum; +import network.Messages.LatestMessages; + +public class RaceLogic implements Runnable { + /** + * State of current race modified by this object + */ + private MockRace race; + /** + * High-level interface to AC35 protocol server + */ + private RaceServer server; + + /** + * Initialises race loop with state and server message queue + * @param race state of race to modify + * @param messages to send to server + */ + public RaceLogic(MockRace race, LatestMessages messages) { + this.race = race; + this.server = new RaceServer(race, messages); + } + + /** + * Initialise boats and start countdown timer + */ + @Override + public void run() { + race.initialiseBoats(); + this.countdownTimer.start(); + } + + + /** + * Countdown timer until race starts. + */ + protected AnimationTimer countdownTimer = new AnimationTimer() { + + + long currentTime = System.currentTimeMillis(); + + @Override + public void handle(long arg0) { + + //Update race time. + race.updateRaceTime(currentTime); + + //Update the race status based on the current time. + race.updateRaceStatusEnum(); + + //Provide boat's with an estimated time at next mark until the race starts. + race.setBoatsTimeNextMark(race.getRaceClock().getCurrentTime()); + + //Parse the boat locations. + server.parseBoatLocations(); + + //Parse the marks. + server.parseMarks(); + + // Change wind direction + race.changeWindDirection(); + + //Parse the race status. + server.parseRaceStatus(); + + + if (race.getRaceStatusEnum() == RaceStatusEnum.STARTED) { + race.setBoatsStatusToRacing(); + raceTimer.start(); + this.stop(); + } + + //Update the animations timer's time. + currentTime = System.currentTimeMillis(); + } + }; + + + /** + * Timer that runs for the duration of the race, until all boats finish. + */ + private AnimationTimer raceTimer = new AnimationTimer() { + + /** + * Start time of loop, in milliseconds. + */ + long timeRaceStarted = System.currentTimeMillis(); + + /** + * Current time during a loop iteration. + */ + long currentTime = System.currentTimeMillis(); + + /** + * The time of the previous frame, in milliseconds. + */ + long lastFrameTime = timeRaceStarted; + + @Override + public void handle(long arg0) { + + //Get the current time. + currentTime = System.currentTimeMillis(); + + //Update race time. + race.updateRaceTime(currentTime); + + + //As long as there is at least one boat racing, we still simulate the race. + if (race.getNumberOfActiveBoats() != 0) { + + //Get the time period of this frame. + long framePeriod = currentTime - lastFrameTime; + + //For each boat, we update its position, and generate a BoatLocationMessage. + for (MockBoat boat : race.getBoats()) { + + //If it is still racing, update its position. + if (boat.getStatus() == BoatStatusEnum.RACING) { + + race.updatePosition(boat, framePeriod, race.getRaceClock().getDurationMilli()); + + } + + } + + } else { + //Otherwise, the race is over! + raceFinished.start(); + race.setRaceStatusEnum(RaceStatusEnum.FINISHED); + this.stop(); + } + + if (race.getNumberOfActiveBoats() != 0) { + // Change wind direction + race.changeWindDirection(); + + //Parse the boat locations. + server.parseBoatLocations(); + + //Parse the marks. + server.parseMarks(); + + //Parse the race status. + server.parseRaceStatus(); + + + //Update the last frame time. + this.lastFrameTime = currentTime; + } + } + }; + + /** + * Broadcast that the race has finished. + */ + protected AnimationTimer raceFinished = new AnimationTimer(){ + int iters = 0; + @Override + public void handle(long now) { + + server.parseRaceStatus(); + + if (iters > 500) { + stop(); + } + iters++; + } + }; } diff --git a/racevisionGame/src/main/java/shared/model/Race.java b/racevisionGame/src/main/java/shared/model/Race.java index 040e0d19..f9fc984e 100644 --- a/racevisionGame/src/main/java/shared/model/Race.java +++ b/racevisionGame/src/main/java/shared/model/Race.java @@ -19,7 +19,7 @@ import java.util.List; * This is a base class inherited by {@link mock.model.MockRace} and {@link visualiser.model.VisualiserRace}. * Has a course, state, wind, boundaries, etc.... Boats are added by inheriting classes (see {@link Boat}, {@link mock.model.MockBoat}, {@link visualiser.model.VisualiserBoat}. */ -public abstract class Race implements Runnable { +public abstract class Race { /** @@ -214,7 +214,7 @@ public abstract class Race implements Runnable { * Sets the current race status. * @param raceStatusEnum The new status of the race. */ - protected void setRaceStatusEnum(RaceStatusEnum raceStatusEnum) { + public void setRaceStatusEnum(RaceStatusEnum raceStatusEnum) { this.raceStatusEnum = raceStatusEnum; } diff --git a/racevisionGame/src/main/java/visualiser/model/VisualiserRace.java b/racevisionGame/src/main/java/visualiser/model/VisualiserRace.java index 976a4b6e..2969fbb8 100644 --- a/racevisionGame/src/main/java/visualiser/model/VisualiserRace.java +++ b/racevisionGame/src/main/java/visualiser/model/VisualiserRace.java @@ -27,7 +27,7 @@ import java.util.Map; * Has a course, boats, boundaries, etc... * Observes LatestMessages and updates its state based on new messages. */ -public class VisualiserRace extends Race { +public class VisualiserRace extends Race implements Runnable { /** From d9679c1497676c68b89194bb3edcc15fb2fb298f Mon Sep 17 00:00:00 2001 From: zwu18 Date: Thu, 3 Aug 2017 01:31:05 +1200 Subject: [PATCH 11/18] Build failing due to junit even though junit passes all tests. Commented out VMGCommand and TackGybeCommand content. --- .../mock/model/commandFactory/TackGybeCommand.java | 10 ++++++++-- .../java/mock/model/commandFactory/VMGCommand.java | 9 +++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/racevisionGame/src/main/java/mock/model/commandFactory/TackGybeCommand.java b/racevisionGame/src/main/java/mock/model/commandFactory/TackGybeCommand.java index 4f1f848e..ef776536 100644 --- a/racevisionGame/src/main/java/mock/model/commandFactory/TackGybeCommand.java +++ b/racevisionGame/src/main/java/mock/model/commandFactory/TackGybeCommand.java @@ -3,6 +3,7 @@ package mock.model.commandFactory; import mock.model.MockBoat; import mock.model.MockRace; import mock.model.VMG; +import shared.model.Bearing; /** * Created by David on 2/08/2017. @@ -12,10 +13,15 @@ public class TackGybeCommand implements CommandFactory { //The refactoring of MockRace will require changes to be made @Override public void runCommand(MockBoat boat, MockRace race) { - VMG newVMG = race.calculateVMG(boat); + /*VMG newVMG = boat.getPolars().calculateVMG( + race.getWindDirection(), + race.getWindSpeed(), + boat.calculateBearingToNextMarker(), + Bearing.fromDegrees(0d), + Bearing.fromDegrees(359.99999d)); VMG boatVMG = new VMG(boat.getCurrentSpeed(), boat.getBearing()); if(race.improvesVelocity(boatVMG, newVMG, boat.calculateBearingToNextMarker())){ boat.setVMG(newVMG); - } + }*/ } } diff --git a/racevisionGame/src/main/java/mock/model/commandFactory/VMGCommand.java b/racevisionGame/src/main/java/mock/model/commandFactory/VMGCommand.java index 2f0f527d..5d11a27b 100644 --- a/racevisionGame/src/main/java/mock/model/commandFactory/VMGCommand.java +++ b/racevisionGame/src/main/java/mock/model/commandFactory/VMGCommand.java @@ -13,7 +13,12 @@ public class VMGCommand implements CommandFactory { //The refactoring of MockRace will require changes to be made @Override public void runCommand(MockBoat boat, MockRace race) { - VMG newVMG = race.calculateVMG(boat); - boat.setVMG(newVMG); + /*VMG newVMG = boat.getPolars().calculateVMG( + race.getWindDirection(), + race.getWindSpeed(), + boat.calculateBearingToNextMarker(), + Bearing.fromDegrees(0d), + Bearing.fromDegrees(359.99999d)); + boat.setVMG(newVMG);*/ } } From e4999a3c937d4765362317711e9cc7795f571b9c Mon Sep 17 00:00:00 2001 From: fjc40 Date: Thu, 3 Aug 2017 12:56:15 +1200 Subject: [PATCH 12/18] Added a light variant of the arrow image. nightMode.css loads this instead of the regular dark arrow. ResizableRaceCanvas doesn't cache the race boundary background, as it was unneccessary and stopped the canvas from being transparent. #story[1093] --- .../visualiser/model/ResizableRaceCanvas.java | 29 ++---------------- .../src/main/resources/css/dayMode.css | 4 ++- .../src/main/resources/css/nightMode.css | 7 ++++- .../visualiser/images/arrowLight.png | Bin 0 -> 23441 bytes 4 files changed, 11 insertions(+), 29 deletions(-) create mode 100644 racevisionGame/src/main/resources/visualiser/images/arrowLight.png diff --git a/racevisionGame/src/main/java/visualiser/model/ResizableRaceCanvas.java b/racevisionGame/src/main/java/visualiser/model/ResizableRaceCanvas.java index adbd4840..e9b4c31c 100644 --- a/racevisionGame/src/main/java/visualiser/model/ResizableRaceCanvas.java +++ b/racevisionGame/src/main/java/visualiser/model/ResizableRaceCanvas.java @@ -12,7 +12,6 @@ import shared.model.GPSCoordinate; import shared.model.Mark; import shared.model.RaceClock; -import java.time.Duration; import java.util.List; /** @@ -39,12 +38,6 @@ public class ResizableRaceCanvas extends ResizableCanvas { */ private VisualiserRace visualiserRace; - /** - * The background of the race. - * We render the background whenever the race boundary changes, or the screen size changes. - */ - private Image background; - private boolean annoName = true; private boolean annoAbbrev = true; @@ -407,13 +400,9 @@ public class ResizableRaceCanvas extends ResizableCanvas { this.map.setWidth((int) getWidth()); this.map.setHeight((int) getHeight()); - //Redraw the boundary. - redrawBoundaryImage(); - //Draw the race. drawRace(); - } @@ -426,15 +415,13 @@ public class ResizableRaceCanvas extends ResizableCanvas { /** - * Draws the race boundary, and saves the image to {@link #background}. - * You should call {@link #clear()} before calling this. + * Draws the race boundary. */ - private void redrawBoundaryImage() { + private void drawBoundary() { //Prepare to draw. gc.setLineWidth(1); gc.setFill(Color.AQUA); - gc.drawImage(new Image(getClass().getClassLoader().getResourceAsStream("images/WaterBackground.png")), 0, 0); //Calculate the screen coordinates of the boundary. @@ -454,9 +441,6 @@ public class ResizableRaceCanvas extends ResizableCanvas { //Draw the boundary. gc.fillPolygon(xpoints, ypoints, xpoints.length); - //Render boundary to image. - this.background = snapshot(null, null); - } /** @@ -481,15 +465,6 @@ public class ResizableRaceCanvas extends ResizableCanvas { } - /** - * Draws the race boundary image onto the canvas. - * See {@link #background}. - */ - private void drawBoundary() { - gc.drawImage(this.background, 0, 0); - } - - diff --git a/racevisionGame/src/main/resources/css/dayMode.css b/racevisionGame/src/main/resources/css/dayMode.css index b15f242b..d0f62fb7 100644 --- a/racevisionGame/src/main/resources/css/dayMode.css +++ b/racevisionGame/src/main/resources/css/dayMode.css @@ -50,4 +50,6 @@ .scroll-bar > .increment-button:pressed > .increment-arrow, .scroll-bar > .decrement-button:pressed > .decrement-arrow { -fx-background-color: -fx-mark-highlight-color, rgb(255, 255, 255); -} \ No newline at end of file +} + + diff --git a/racevisionGame/src/main/resources/css/nightMode.css b/racevisionGame/src/main/resources/css/nightMode.css index 406cc60b..7fe6a67b 100644 --- a/racevisionGame/src/main/resources/css/nightMode.css +++ b/racevisionGame/src/main/resources/css/nightMode.css @@ -51,4 +51,9 @@ .scroll-bar > .increment-button:pressed > .increment-arrow, .scroll-bar > .decrement-button:pressed > .decrement-arrow { -fx-background-color: -fx-mark-highlight-color, rgb(255, 255, 255); -} \ No newline at end of file +} + + +#arrowImage { + -fx-image: url("/visualiser/images/arrowLight.png"); +} diff --git a/racevisionGame/src/main/resources/visualiser/images/arrowLight.png b/racevisionGame/src/main/resources/visualiser/images/arrowLight.png new file mode 100644 index 0000000000000000000000000000000000000000..7a80459d956a2dcb4a2ab5aa4d5704f7f8a23f8f GIT binary patch literal 23441 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 NIImcA!ATbLe*l%T0dD{R literal 0 HcmV?d00001 From 2e325d5177ec4bb4147086dc6fc6cfc876bb55d5 Mon Sep 17 00:00:00 2001 From: fjc40 Date: Thu, 3 Aug 2017 13:21:53 +1200 Subject: [PATCH 13/18] Added WindGeneratorTest. --- .../java/mock/model/WindGeneratorTest.java | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 racevisionGame/src/test/java/mock/model/WindGeneratorTest.java diff --git a/racevisionGame/src/test/java/mock/model/WindGeneratorTest.java b/racevisionGame/src/test/java/mock/model/WindGeneratorTest.java new file mode 100644 index 00000000..74ad7421 --- /dev/null +++ b/racevisionGame/src/test/java/mock/model/WindGeneratorTest.java @@ -0,0 +1,111 @@ +package mock.model; + +import org.junit.Before; +import org.junit.Test; +import shared.model.Bearing; +import shared.model.Wind; + +import static org.junit.Assert.*; + + +public class WindGeneratorTest { + + + private WindGenerator windGenerator; + + private Bearing windBaselineBearing; + private Bearing windBearingLowerBound; + private Bearing windBearingUpperBound; + private double windBaselineSpeed; + private double windSpeedLowerBound; + private double windSpeedUpperBound; + + private double speedKnotsEpsilon; + private double bearingDegreeEpsilon; + + + @Before + public void setUp() throws Exception { + + + //Bounds. + this.windBaselineBearing = Bearing.fromDegrees(88.3); + this.windBearingLowerBound = Bearing.fromDegrees(66.5); + this.windBearingUpperBound = Bearing.fromDegrees(248.6); + this.windBaselineSpeed = 13; + this.windSpeedLowerBound = 7; + this.windSpeedUpperBound = 20; + + this.windGenerator = new WindGenerator( + windBaselineBearing, + windBearingLowerBound, + windBearingUpperBound, + windBaselineSpeed, + windSpeedLowerBound, + windSpeedUpperBound ); + + this.bearingDegreeEpsilon = 0.001; + this.speedKnotsEpsilon = 0.001; + } + + + /** + * Tests if the baseline wind generated it accurate. + */ + @Test + public void generateBaselineWindTest() { + + Wind wind = windGenerator.generateBaselineWind(); + + assertEquals(windBaselineSpeed, wind.getWindSpeed(), speedKnotsEpsilon); + assertEquals(windBaselineBearing.degrees(), wind.getWindDirection().degrees(), bearingDegreeEpsilon); + + } + + /** + * Tests if the random wind generated is inside the bounds. + */ + @Test + public void generateRandomWindTest() { + + int randomWindCount = 1000; + + for (int i = 0; i < randomWindCount; i++) { + + Wind wind = windGenerator.generateRandomWind(); + + assertTrue(wind.getWindSpeed() >= windSpeedLowerBound); + assertTrue(wind.getWindSpeed() <= windSpeedUpperBound); + + assertTrue(wind.getWindDirection().degrees() >= windBearingLowerBound.degrees()); + assertTrue(wind.getWindDirection().degrees() <= windBearingUpperBound.degrees()); + + } + + } + + + /** + * Tests if the next wind generated is inside the bounds. + */ + @Test + public void generateNextWindTest() { + + Wind wind = windGenerator.generateBaselineWind(); + + int randomWindCount = 1000; + + for (int i = 0; i < randomWindCount; i++) { + + wind = windGenerator.generateNextWind(wind); + + assertTrue(wind.getWindSpeed() >= windSpeedLowerBound); + assertTrue(wind.getWindSpeed() <= windSpeedUpperBound); + + assertTrue(wind.getWindDirection().degrees() >= windBearingLowerBound.degrees()); + assertTrue(wind.getWindDirection().degrees() <= windBearingUpperBound.degrees()); + + } + + } +} From 40a3ed1bb00af265d87fd529e76bbad1bf93952b Mon Sep 17 00:00:00 2001 From: Joseph Gardner Date: Thu, 3 Aug 2017 16:56:52 +1200 Subject: [PATCH 14/18] Boats now store whether autoVMG is on or off, boat speed scales off their bearing relative to the TWA. #story[1094] --- .../src/main/java/mock/model/MockBoat.java | 12 + .../src/main/java/mock/model/MockRace.java | 60 +++-- .../src/main/java/mock/model/RaceLogic.java | 6 +- .../src/main/java/mock/model/RaceState.java | 226 +++++++++++++++++- 4 files changed, 283 insertions(+), 21 deletions(-) diff --git a/racevisionGame/src/main/java/mock/model/MockBoat.java b/racevisionGame/src/main/java/mock/model/MockBoat.java index 3c932126..104fa264 100644 --- a/racevisionGame/src/main/java/mock/model/MockBoat.java +++ b/racevisionGame/src/main/java/mock/model/MockBoat.java @@ -22,6 +22,11 @@ public class MockBoat extends Boat { */ private long timeSinceTackChange = 0; + /** + * Stores whether the boat is on autoVMG or not + */ + private boolean autoVMG = true; + /** @@ -191,4 +196,11 @@ public class MockBoat extends Boat { return distanceTravelledMeters; } + public boolean isAutoVMG() { + return autoVMG; + } + + public void setAutoVMG(boolean autoVMG) { + this.autoVMG = autoVMG; + } } diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index 5670e989..0ae5cfcc 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -2,11 +2,13 @@ package mock.model; import network.Messages.Enums.BoatStatusEnum; import network.Messages.LatestMessages; +import org.opengis.geometry.primitive.*; import shared.dataInput.BoatDataSource; import shared.dataInput.RaceDataSource; import network.Messages.Enums.RaceStatusEnum; import shared.dataInput.RegattaDataSource; import shared.model.*; +import shared.model.Bearing; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; @@ -209,8 +211,7 @@ public class MockRace extends Race { boat.setStatus(BoatStatusEnum.PRESTART); //We set a large time since tack change so that it calculates a new VMG when the simulation starts. - boat.setTimeSinceTackChange(999999); - + boat.setTimeSinceTackChange(Long.MAX_VALUE); } } @@ -319,8 +320,14 @@ public class MockRace extends Race { //Checks if the current boat has finished the race or not. boolean finish = this.isLastLeg(boat.getCurrentLeg()); - if (!finish) { + if (!finish && totalElapsedMilliseconds >= updatePeriodMilliseconds) { + + if (boat.getCurrentSpeed() == 0) { + newOptimalVMG(boat); + boat.setBearing(boat.calculateBearingToNextMarker()); + } + setBoatSpeed(boat); //Calculates the distance travelled, in meters, in the current timeslice. double distanceTravelledMeters = boat.calculateMetersTravelled(updatePeriodMilliseconds); @@ -331,30 +338,47 @@ public class MockRace extends Race { //Move the boat forwards that many meters, and advances its time counters by enough milliseconds. boat.moveForwards(distanceTravelledMeters); + boat.setTimeSinceTackChange(boat.getTimeSinceTackChange() + updatePeriodMilliseconds); + if (boat.isAutoVMG()) { + newOptimalVMG(boat); + } + + this.updateEstimatedTime(boat); + } - long tackPeriod = 15000; + } - if (boat.getTimeSinceTackChange() > tackPeriod) { - //Calculate the new VMG. - VMG newVMG = boat.getPolars().calculateVMG( - this.getWindDirection(), - this.getWindSpeed(), - boat.calculateBearingToNextMarker(), - Bearing.fromDegrees(0d), - Bearing.fromDegrees(359.99999d)); + private void newOptimalVMG(MockBoat boat) { + long tackPeriod = 15000; + if (boat.getTimeSinceTackChange() > tackPeriod) { + //Calculate the new VMG. + VMG newVMG = boat.getPolars().calculateVMG( + this.getWindDirection(), + this.getWindSpeed(), + boat.calculateBearingToNextMarker(), + Bearing.fromDegrees(0d), + Bearing.fromDegrees(359.99999d)); - //If the new vmg improves velocity, use it. - if (improvesVelocity(boat, newVMG)) { - boat.setVMG(newVMG); - } + //If the new vmg improves velocity, use it. + if (improvesVelocity(boat, newVMG)) { + boat.setVMG(newVMG); } - - this.updateEstimatedTime(boat); } + } + private void setBoatSpeed(MockBoat boat) { + VMG vmg = boat.getPolars().calculateVMG( + this.getWindDirection(), + this.getWindSpeed(), + boat.getBearing(), + boat.getBearing(), + boat.getBearing()); + if (vmg.getSpeed() > 0) { + boat.setCurrentSpeed(vmg.getSpeed()); + } } /** diff --git a/racevisionGame/src/main/java/mock/model/RaceLogic.java b/racevisionGame/src/main/java/mock/model/RaceLogic.java index 73b38c0d..9e810761 100644 --- a/racevisionGame/src/main/java/mock/model/RaceLogic.java +++ b/racevisionGame/src/main/java/mock/model/RaceLogic.java @@ -4,6 +4,7 @@ import javafx.animation.AnimationTimer; import network.Messages.Enums.BoatStatusEnum; import network.Messages.Enums.RaceStatusEnum; import network.Messages.LatestMessages; +import shared.model.Race; public class RaceLogic implements Runnable { /** @@ -100,6 +101,8 @@ public class RaceLogic implements Runnable { */ long lastFrameTime = timeRaceStarted; + long framePeriod = currentTime - lastFrameTime; + @Override public void handle(long arg0) { @@ -109,12 +112,11 @@ public class RaceLogic implements Runnable { //Update race time. race.updateRaceTime(currentTime); - //As long as there is at least one boat racing, we still simulate the race. if (race.getNumberOfActiveBoats() != 0) { //Get the time period of this frame. - long framePeriod = currentTime - lastFrameTime; + framePeriod = currentTime - lastFrameTime; //For each boat, we update its position, and generate a BoatLocationMessage. for (MockBoat boat : race.getBoats()) { diff --git a/racevisionGame/src/main/java/mock/model/RaceState.java b/racevisionGame/src/main/java/mock/model/RaceState.java index 4b13cbb4..dd72cc97 100644 --- a/racevisionGame/src/main/java/mock/model/RaceState.java +++ b/racevisionGame/src/main/java/mock/model/RaceState.java @@ -1,4 +1,228 @@ package mock.model; -public class RaceState { +import network.Messages.Enums.BoatStatusEnum; +import network.Messages.LatestMessages; +import shared.dataInput.BoatDataSource; +import shared.dataInput.RaceDataSource; +import shared.dataInput.RegattaDataSource; +import shared.model.*; + +import java.util.*; + +public class RaceState extends Race { + /** + * An observable list of boats in the race. + */ + private List boats; + + private Map boatMap; + + private Wind wind; + + public RaceState(BoatDataSource boatDataSource, RaceDataSource raceDataSource, RegattaDataSource regattaDataSource, LatestMessages latestMessages, Polars polars) { + super(boatDataSource, raceDataSource, regattaDataSource, latestMessages); + this.boats = this.generateMockBoats(boatDataSource.getBoats(), raceDataSource.getParticipants(), polars); + this.boatMap = this.generateMockBoatMap(this.boats); + } + + /** + * Generates a list of MockBoats given a list of Boats, and a list of participating boats. + * @param boats The map of Boats describing boats that are potentially in the race. Maps boat sourceID to boat. + * @param sourceIDs The list of boat sourceIDs describing which specific boats are actually participating. + * @param polars The polars table to be used for boat simulation. + * @return A list of MockBoats that are participating in the race. + */ + private List generateMockBoats(Map boats, List sourceIDs, Polars polars) { + + List mockBoats = new ArrayList<>(sourceIDs.size()); + + //For each sourceID participating... + for (int sourceID : sourceIDs) { + + //Get the boat associated with the sourceID. + Boat boat = boats.get(sourceID); + + //Construct a MockBoat using the Boat and Polars. + MockBoat mockBoat = new MockBoat(boat, polars); + + mockBoats.add(mockBoat); + + } + + return mockBoats; + + } + + /** + * Generates a map of the sourceID to its MockBoat. + * @param boats The list of MockBoats describing boats that are potentially in the race. + * @return A map of sourceID to MockBoats. + */ + private Map generateMockBoatMap(List boats) { + Map boatMap = new HashMap<>(); + + for (MockBoat boat : boats) { + boatMap.put(boat.getSourceID(), boat); + } + + return boatMap; + } + + /** + * Checks if a boat has finished any legs, or has pulled out of race (DNF). + * @param boat The boat to check. + * @param timeElapsed The total time, in milliseconds, that has elapsed since the race started. + */ + public void checkPosition(MockBoat boat, long timeElapsed) { + + //The distance, in nautical miles, within which the boat needs to get in order to consider that it has reached the marker. + double epsilonNauticalMiles = 100.0 / Constants.NMToMetersConversion; //100 meters. TODO should be more like 5-10. + + if (boat.calculateDistanceToNextMarker() < epsilonNauticalMiles) { + //Boat has reached its target marker, and has moved on to a new leg. + + + + //Calculate how much the boat overshot the marker by. + double overshootMeters = boat.calculateDistanceToNextMarker(); + + + //Move boat on to next leg. + Leg nextLeg = this.legs.get(boat.getCurrentLeg().getLegNumber() + 1); + boat.setCurrentLeg(nextLeg); + + //Add overshoot distance into the distance travelled for the next leg. + boat.setDistanceTravelledInLeg(overshootMeters); + + //Setting a high value for this allows the boat to immediately do a large turn, as it needs to in order to get to the next mark. + boat.setTimeSinceTackChange(999999); + + + //Check if the boat has finished or stopped racing. + + if (this.isLastLeg(boat.getCurrentLeg())) { + //Boat has finished. + boat.setTimeFinished(timeElapsed); + boat.setCurrentSpeed(0); + boat.setStatus(BoatStatusEnum.FINISHED); + + } + + } + + } + + /** + * Initialise the boats in the race. + * This sets their starting positions and current legs. + */ + @Override + protected void initialiseBoats() { + + //Gets the starting positions of the boats. + List startingPositions = getSpreadStartingPositions(); + + //Get iterators for our boat and position lists. + Iterator boatIt = this.boats.iterator(); + Iterator startPositionIt = startingPositions.iterator(); + + //Iterate over the pair of lists. + while (boatIt.hasNext() && startPositionIt.hasNext()) { + + //Get the next boat and position. + MockBoat boat = boatIt.next(); + GPSCoordinate startPosition = startPositionIt.next(); + + + //The boat starts on the first leg of the race. + boat.setCurrentLeg(this.legs.get(0)); + + //Boats start with 0 knots speed. + boat.setCurrentSpeed(0d); + + //Place the boat at its starting position. + boat.setCurrentPosition(startPosition); + + //Boats start facing their next marker. + boat.setBearing(boat.calculateBearingToNextMarker()); + + //Sets the boats status to prestart - it changes to racing when the race starts. + boat.setStatus(BoatStatusEnum.PRESTART); + + //We set a large time since tack change so that it calculates a new VMG when the simulation starts. + boat.setTimeSinceTackChange(999999); + + } + + } + + /** + * Creates a list of starting positions for the different boats, so they do not appear cramped at the start line. + * + * @return A list of starting positions. + */ + public List getSpreadStartingPositions() { + + //The first compound marker of the race - the starting gate. + CompoundMark compoundMark = this.legs.get(0).getStartCompoundMark(); + + //The position of the two markers from the compound marker. + GPSCoordinate mark1Position = compoundMark.getMark1Position(); + GPSCoordinate mark2Position = compoundMark.getMark2Position(); + + + //Calculates the azimuth between the two points. + Azimuth azimuth = GPSCoordinate.calculateAzimuth(mark1Position, mark2Position); + + //Calculates the distance between the two points. + double distanceMeters = GPSCoordinate.calculateDistanceMeters(mark1Position, mark2Position); + + //The number of boats in the race. + int numberOfBoats = this.boats.size(); + + //Calculates the distance between each boat. We divide by numberOfBoats + 1 to ensure that no boat is placed on one of the starting gate's marks. + double distanceBetweenBoatsMeters = distanceMeters / (numberOfBoats + 1); + + + //List to store coordinates in. + List positions = new ArrayList<>(); + + //We start spacing boats out from mark 1. + GPSCoordinate position = mark1Position; + + //For each boat, displace position, and store it. + for (int i = 0; i < numberOfBoats; i++) { + + position = GPSCoordinate.calculateNewPosition(position, distanceBetweenBoatsMeters, azimuth); + + positions.add(position); + + } + + return positions; + } + + /** + * Updates the race time to a specified value, in milliseconds since the unix epoch. + * @param currentTime Milliseconds since unix epoch. + */ + public void updateRaceTime(long currentTime) { + this.raceClock.setUTCTime(currentTime); + } + + public void run() { + initialiseBoats(); + } + + public Wind getWind() { + return wind; + } + + public List getBoats() { + return boats; + } + + public MockBoat getBoatByid(int id) { + return boatMap.get(id); + } } From c83442761ca6502cedc28feb671705fdf4c0a768 Mon Sep 17 00:00:00 2001 From: Connor Taylor-Brown Date: Thu, 3 Aug 2017 17:54:03 +1200 Subject: [PATCH 15/18] Implemented Factory for Command objects and fixed Command Pattern implementation. - Commands now have standard 0-parameter execute method - Commands are created by CommandFactory #story[1094] --- .../mock/model/commandFactory/Command.java | 14 +++++++++++++ .../model/commandFactory/CommandFactory.java | 21 ++++++++++++++++--- .../model/commandFactory/TackGybeCommand.java | 13 ++++++++---- .../mock/model/commandFactory/VMGCommand.java | 13 ++++++++---- 4 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 racevisionGame/src/main/java/mock/model/commandFactory/Command.java diff --git a/racevisionGame/src/main/java/mock/model/commandFactory/Command.java b/racevisionGame/src/main/java/mock/model/commandFactory/Command.java new file mode 100644 index 00000000..e0486114 --- /dev/null +++ b/racevisionGame/src/main/java/mock/model/commandFactory/Command.java @@ -0,0 +1,14 @@ +package mock.model.commandFactory; + +import mock.model.MockBoat; +import mock.model.MockRace; + +/** + * Allows RaceLogic to control MockRace state according to the Command pattern + */ +public interface Command { + /** + * Execute command - standard method name in pattern + */ + void execute(); +} diff --git a/racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java b/racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java index ace2f3be..17834963 100644 --- a/racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java +++ b/racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java @@ -3,7 +3,22 @@ package mock.model.commandFactory; import mock.model.MockBoat; import mock.model.MockRace; -public interface CommandFactory { - - void runCommand(MockBoat boat, MockRace race); +/** + * Factory class for Command objects + */ +public class CommandFactory { + /** + * Generates a command on a race and boat corresponding to the protocol action number. + * @param race to receive command + * @param boat to receive command in race + * @param action number to select command + * @return + */ + public static Command createCommand(MockRace race, MockBoat boat, int action) { + switch(action) { + case 1: return new VMGCommand(race, boat); + case 4: return new TackGybeCommand(race, boat); + default: return null; // TODO - please please have discussion over what to default to + } + } } diff --git a/racevisionGame/src/main/java/mock/model/commandFactory/TackGybeCommand.java b/racevisionGame/src/main/java/mock/model/commandFactory/TackGybeCommand.java index ef776536..150a1da8 100644 --- a/racevisionGame/src/main/java/mock/model/commandFactory/TackGybeCommand.java +++ b/racevisionGame/src/main/java/mock/model/commandFactory/TackGybeCommand.java @@ -2,17 +2,22 @@ package mock.model.commandFactory; import mock.model.MockBoat; import mock.model.MockRace; -import mock.model.VMG; -import shared.model.Bearing; /** * Created by David on 2/08/2017. */ -public class TackGybeCommand implements CommandFactory { +public class TackGybeCommand implements Command { + private MockRace race; + private MockBoat boat; + + public TackGybeCommand(MockRace race, MockBoat boat) { + this.race = race; + this.boat = boat; + } //The refactoring of MockRace will require changes to be made @Override - public void runCommand(MockBoat boat, MockRace race) { + public void execute() { /*VMG newVMG = boat.getPolars().calculateVMG( race.getWindDirection(), race.getWindSpeed(), diff --git a/racevisionGame/src/main/java/mock/model/commandFactory/VMGCommand.java b/racevisionGame/src/main/java/mock/model/commandFactory/VMGCommand.java index 5d11a27b..64cc6a9f 100644 --- a/racevisionGame/src/main/java/mock/model/commandFactory/VMGCommand.java +++ b/racevisionGame/src/main/java/mock/model/commandFactory/VMGCommand.java @@ -2,17 +2,22 @@ package mock.model.commandFactory; import mock.model.MockBoat; import mock.model.MockRace; -import mock.model.VMG; -import shared.model.Bearing; /** * Created by David on 2/08/2017. */ -public class VMGCommand implements CommandFactory { +public class VMGCommand implements Command { + private MockRace race; + private MockBoat boat; + + public VMGCommand(MockRace race, MockBoat boat) { + this.race = race; + this.boat = boat; + } //The refactoring of MockRace will require changes to be made @Override - public void runCommand(MockBoat boat, MockRace race) { + public void execute() { /*VMG newVMG = boat.getPolars().calculateVMG( race.getWindDirection(), race.getWindSpeed(), From 148108a6581b16d346c891f4a1165e20ed832a96 Mon Sep 17 00:00:00 2001 From: fjc40 Date: Thu, 3 Aug 2017 21:11:25 +1200 Subject: [PATCH 16/18] CommandFactory uses BoatActionEnum instead of raw ints. #story[1094] --- .../java/mock/model/commandFactory/CommandFactory.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java b/racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java index 17834963..6e87f11b 100644 --- a/racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java +++ b/racevisionGame/src/main/java/mock/model/commandFactory/CommandFactory.java @@ -2,6 +2,7 @@ package mock.model.commandFactory; import mock.model.MockBoat; import mock.model.MockRace; +import network.Messages.Enums.BoatActionEnum; /** * Factory class for Command objects @@ -14,10 +15,10 @@ public class CommandFactory { * @param action number to select command * @return */ - public static Command createCommand(MockRace race, MockBoat boat, int action) { + public static Command createCommand(MockRace race, MockBoat boat, BoatActionEnum action) { switch(action) { - case 1: return new VMGCommand(race, boat); - case 4: return new TackGybeCommand(race, boat); + case AUTO_PILOT: return new VMGCommand(race, boat); + case TACK_GYBE: return new TackGybeCommand(race, boat); default: return null; // TODO - please please have discussion over what to default to } } From 851bbb4fde539ed3b3db3bab8bec24c5986657bc Mon Sep 17 00:00:00 2001 From: Connor Taylor-Brown Date: Thu, 3 Aug 2017 23:03:15 +1200 Subject: [PATCH 17/18] Merged branch "RaceFactory" with master --- .../src/main/java/mock/model/RaceState.java | 228 ------------------ 1 file changed, 228 deletions(-) delete mode 100644 racevisionGame/src/main/java/mock/model/RaceState.java diff --git a/racevisionGame/src/main/java/mock/model/RaceState.java b/racevisionGame/src/main/java/mock/model/RaceState.java deleted file mode 100644 index dd72cc97..00000000 --- a/racevisionGame/src/main/java/mock/model/RaceState.java +++ /dev/null @@ -1,228 +0,0 @@ -package mock.model; - -import network.Messages.Enums.BoatStatusEnum; -import network.Messages.LatestMessages; -import shared.dataInput.BoatDataSource; -import shared.dataInput.RaceDataSource; -import shared.dataInput.RegattaDataSource; -import shared.model.*; - -import java.util.*; - -public class RaceState extends Race { - /** - * An observable list of boats in the race. - */ - private List boats; - - private Map boatMap; - - private Wind wind; - - public RaceState(BoatDataSource boatDataSource, RaceDataSource raceDataSource, RegattaDataSource regattaDataSource, LatestMessages latestMessages, Polars polars) { - super(boatDataSource, raceDataSource, regattaDataSource, latestMessages); - this.boats = this.generateMockBoats(boatDataSource.getBoats(), raceDataSource.getParticipants(), polars); - this.boatMap = this.generateMockBoatMap(this.boats); - } - - /** - * Generates a list of MockBoats given a list of Boats, and a list of participating boats. - * @param boats The map of Boats describing boats that are potentially in the race. Maps boat sourceID to boat. - * @param sourceIDs The list of boat sourceIDs describing which specific boats are actually participating. - * @param polars The polars table to be used for boat simulation. - * @return A list of MockBoats that are participating in the race. - */ - private List generateMockBoats(Map boats, List sourceIDs, Polars polars) { - - List mockBoats = new ArrayList<>(sourceIDs.size()); - - //For each sourceID participating... - for (int sourceID : sourceIDs) { - - //Get the boat associated with the sourceID. - Boat boat = boats.get(sourceID); - - //Construct a MockBoat using the Boat and Polars. - MockBoat mockBoat = new MockBoat(boat, polars); - - mockBoats.add(mockBoat); - - } - - return mockBoats; - - } - - /** - * Generates a map of the sourceID to its MockBoat. - * @param boats The list of MockBoats describing boats that are potentially in the race. - * @return A map of sourceID to MockBoats. - */ - private Map generateMockBoatMap(List boats) { - Map boatMap = new HashMap<>(); - - for (MockBoat boat : boats) { - boatMap.put(boat.getSourceID(), boat); - } - - return boatMap; - } - - /** - * Checks if a boat has finished any legs, or has pulled out of race (DNF). - * @param boat The boat to check. - * @param timeElapsed The total time, in milliseconds, that has elapsed since the race started. - */ - public void checkPosition(MockBoat boat, long timeElapsed) { - - //The distance, in nautical miles, within which the boat needs to get in order to consider that it has reached the marker. - double epsilonNauticalMiles = 100.0 / Constants.NMToMetersConversion; //100 meters. TODO should be more like 5-10. - - if (boat.calculateDistanceToNextMarker() < epsilonNauticalMiles) { - //Boat has reached its target marker, and has moved on to a new leg. - - - - //Calculate how much the boat overshot the marker by. - double overshootMeters = boat.calculateDistanceToNextMarker(); - - - //Move boat on to next leg. - Leg nextLeg = this.legs.get(boat.getCurrentLeg().getLegNumber() + 1); - boat.setCurrentLeg(nextLeg); - - //Add overshoot distance into the distance travelled for the next leg. - boat.setDistanceTravelledInLeg(overshootMeters); - - //Setting a high value for this allows the boat to immediately do a large turn, as it needs to in order to get to the next mark. - boat.setTimeSinceTackChange(999999); - - - //Check if the boat has finished or stopped racing. - - if (this.isLastLeg(boat.getCurrentLeg())) { - //Boat has finished. - boat.setTimeFinished(timeElapsed); - boat.setCurrentSpeed(0); - boat.setStatus(BoatStatusEnum.FINISHED); - - } - - } - - } - - /** - * Initialise the boats in the race. - * This sets their starting positions and current legs. - */ - @Override - protected void initialiseBoats() { - - //Gets the starting positions of the boats. - List startingPositions = getSpreadStartingPositions(); - - //Get iterators for our boat and position lists. - Iterator boatIt = this.boats.iterator(); - Iterator startPositionIt = startingPositions.iterator(); - - //Iterate over the pair of lists. - while (boatIt.hasNext() && startPositionIt.hasNext()) { - - //Get the next boat and position. - MockBoat boat = boatIt.next(); - GPSCoordinate startPosition = startPositionIt.next(); - - - //The boat starts on the first leg of the race. - boat.setCurrentLeg(this.legs.get(0)); - - //Boats start with 0 knots speed. - boat.setCurrentSpeed(0d); - - //Place the boat at its starting position. - boat.setCurrentPosition(startPosition); - - //Boats start facing their next marker. - boat.setBearing(boat.calculateBearingToNextMarker()); - - //Sets the boats status to prestart - it changes to racing when the race starts. - boat.setStatus(BoatStatusEnum.PRESTART); - - //We set a large time since tack change so that it calculates a new VMG when the simulation starts. - boat.setTimeSinceTackChange(999999); - - } - - } - - /** - * Creates a list of starting positions for the different boats, so they do not appear cramped at the start line. - * - * @return A list of starting positions. - */ - public List getSpreadStartingPositions() { - - //The first compound marker of the race - the starting gate. - CompoundMark compoundMark = this.legs.get(0).getStartCompoundMark(); - - //The position of the two markers from the compound marker. - GPSCoordinate mark1Position = compoundMark.getMark1Position(); - GPSCoordinate mark2Position = compoundMark.getMark2Position(); - - - //Calculates the azimuth between the two points. - Azimuth azimuth = GPSCoordinate.calculateAzimuth(mark1Position, mark2Position); - - //Calculates the distance between the two points. - double distanceMeters = GPSCoordinate.calculateDistanceMeters(mark1Position, mark2Position); - - //The number of boats in the race. - int numberOfBoats = this.boats.size(); - - //Calculates the distance between each boat. We divide by numberOfBoats + 1 to ensure that no boat is placed on one of the starting gate's marks. - double distanceBetweenBoatsMeters = distanceMeters / (numberOfBoats + 1); - - - //List to store coordinates in. - List positions = new ArrayList<>(); - - //We start spacing boats out from mark 1. - GPSCoordinate position = mark1Position; - - //For each boat, displace position, and store it. - for (int i = 0; i < numberOfBoats; i++) { - - position = GPSCoordinate.calculateNewPosition(position, distanceBetweenBoatsMeters, azimuth); - - positions.add(position); - - } - - return positions; - } - - /** - * Updates the race time to a specified value, in milliseconds since the unix epoch. - * @param currentTime Milliseconds since unix epoch. - */ - public void updateRaceTime(long currentTime) { - this.raceClock.setUTCTime(currentTime); - } - - public void run() { - initialiseBoats(); - } - - public Wind getWind() { - return wind; - } - - public List getBoats() { - return boats; - } - - public MockBoat getBoatByid(int id) { - return boatMap.get(id); - } -} From 425cc7f91faf648536ea8a2a0ae318bb9781ba94 Mon Sep 17 00:00:00 2001 From: Connor Taylor-Brown Date: Fri, 4 Aug 2017 21:27:53 +1200 Subject: [PATCH 18/18] Changed ControlKey to use BoatActionEnum instead of magic numbers. --- .../visualiser/gameController/ControllerClient.java | 6 +++--- .../visualiser/gameController/Keys/ControlKey.java | 11 ++++++----- .../visualiser/gameController/Keys/DownWindKey.java | 4 +++- .../gameController/Keys/SailsToggleKey.java | 10 ++++++++-- .../visualiser/gameController/Keys/TackGybeKey.java | 4 +++- .../visualiser/gameController/Keys/UpWindKey.java | 4 +++- .../java/visualiser/gameController/Keys/VMGKey.java | 3 ++- 7 files changed, 28 insertions(+), 14 deletions(-) diff --git a/racevisionGame/src/main/java/visualiser/gameController/ControllerClient.java b/racevisionGame/src/main/java/visualiser/gameController/ControllerClient.java index eb46d361..a0f1af75 100644 --- a/racevisionGame/src/main/java/visualiser/gameController/ControllerClient.java +++ b/racevisionGame/src/main/java/visualiser/gameController/ControllerClient.java @@ -47,11 +47,11 @@ public class ControllerClient { * @throws IOException if socket write fails */ public void sendKey(ControlKey key) throws IOException { - int protocolCode = key.getProtocolCode(); - if(protocolCode > -1) { + BoatActionEnum protocolCode = key.getProtocolCode(); + if(protocolCode != BoatActionEnum.NOT_A_STATUS) { byte[] bytes = new byte[4]; - ByteBuffer.wrap(bytes).putInt(key.getProtocolCode()); + ByteBuffer.wrap(bytes).putInt(protocolCode.getValue()); BoatActionEnum boatActionEnum = BoatActionEnum.fromByte(bytes[3]); BoatAction boatAction = new BoatAction(boatActionEnum); diff --git a/racevisionGame/src/main/java/visualiser/gameController/Keys/ControlKey.java b/racevisionGame/src/main/java/visualiser/gameController/Keys/ControlKey.java index ad8a559a..dd489f73 100644 --- a/racevisionGame/src/main/java/visualiser/gameController/Keys/ControlKey.java +++ b/racevisionGame/src/main/java/visualiser/gameController/Keys/ControlKey.java @@ -1,6 +1,7 @@ package visualiser.gameController.Keys; import javafx.scene.input.KeyCode; +import network.Messages.Enums.BoatActionEnum; /** * Key for the controller, part of the abstract factory KeyFactory @@ -8,14 +9,14 @@ import javafx.scene.input.KeyCode; public abstract class ControlKey { private String name; - protected int protocolCode; + protected BoatActionEnum protocolCode; /** * Constructor for key state with specified protocol code * @param name of action - * @param protocolCode -1 if not sent + * @param protocolCode NOT_A_STATUS if not sent */ - public ControlKey(String name, int protocolCode) { + public ControlKey(String name, BoatActionEnum protocolCode) { this.name = name; this.protocolCode = protocolCode; } @@ -26,10 +27,10 @@ public abstract class ControlKey { */ public ControlKey(String name){ this.name = name; - this.protocolCode = -1; + this.protocolCode = BoatActionEnum.NOT_A_STATUS; } - public int getProtocolCode() { + public BoatActionEnum getProtocolCode() { return protocolCode; } diff --git a/racevisionGame/src/main/java/visualiser/gameController/Keys/DownWindKey.java b/racevisionGame/src/main/java/visualiser/gameController/Keys/DownWindKey.java index 6d929ca1..e4b5455a 100644 --- a/racevisionGame/src/main/java/visualiser/gameController/Keys/DownWindKey.java +++ b/racevisionGame/src/main/java/visualiser/gameController/Keys/DownWindKey.java @@ -1,5 +1,7 @@ package visualiser.gameController.Keys; +import network.Messages.Enums.BoatActionEnum; + /** * Key to send downwind packet to server */ @@ -11,7 +13,7 @@ public class DownWindKey extends ControlKey { * */ public DownWindKey(String name) { - super(name, 6); + super(name, BoatActionEnum.DOWNWIND); } @Override diff --git a/racevisionGame/src/main/java/visualiser/gameController/Keys/SailsToggleKey.java b/racevisionGame/src/main/java/visualiser/gameController/Keys/SailsToggleKey.java index bc9b81a6..15b88512 100644 --- a/racevisionGame/src/main/java/visualiser/gameController/Keys/SailsToggleKey.java +++ b/racevisionGame/src/main/java/visualiser/gameController/Keys/SailsToggleKey.java @@ -1,5 +1,7 @@ package visualiser.gameController.Keys; +import network.Messages.Enums.BoatActionEnum; + /** * Key to toggle the sails */ @@ -11,7 +13,7 @@ public class SailsToggleKey extends ControlKey { * */ public SailsToggleKey(String name) { - super(name, 2); + super(name, BoatActionEnum.SAILS_IN); } /** @@ -19,7 +21,11 @@ public class SailsToggleKey extends ControlKey { */ @Override public void onAction() { - protocolCode = protocolCode == 2? 3 : 2; + if(protocolCode == BoatActionEnum.SAILS_IN) { + protocolCode = BoatActionEnum.SAILS_OUT; + } else { + protocolCode = BoatActionEnum.SAILS_IN; + } } @Override diff --git a/racevisionGame/src/main/java/visualiser/gameController/Keys/TackGybeKey.java b/racevisionGame/src/main/java/visualiser/gameController/Keys/TackGybeKey.java index cf9a0699..80252e73 100644 --- a/racevisionGame/src/main/java/visualiser/gameController/Keys/TackGybeKey.java +++ b/racevisionGame/src/main/java/visualiser/gameController/Keys/TackGybeKey.java @@ -1,5 +1,7 @@ package visualiser.gameController.Keys; +import network.Messages.Enums.BoatActionEnum; + /** * key to toggle between tacking and gybing */ @@ -11,7 +13,7 @@ public class TackGybeKey extends ControlKey { * */ public TackGybeKey(String name) { - super(name, 4); + super(name, BoatActionEnum.TACK_GYBE); } @Override diff --git a/racevisionGame/src/main/java/visualiser/gameController/Keys/UpWindKey.java b/racevisionGame/src/main/java/visualiser/gameController/Keys/UpWindKey.java index 85f7fc4b..333e5f1f 100644 --- a/racevisionGame/src/main/java/visualiser/gameController/Keys/UpWindKey.java +++ b/racevisionGame/src/main/java/visualiser/gameController/Keys/UpWindKey.java @@ -1,5 +1,7 @@ package visualiser.gameController.Keys; +import network.Messages.Enums.BoatActionEnum; + /** * Key to go upwind */ @@ -11,7 +13,7 @@ public class UpWindKey extends ControlKey { * */ public UpWindKey(String name) { - super(name, 5); + super(name, BoatActionEnum.UPWIND); } @Override diff --git a/racevisionGame/src/main/java/visualiser/gameController/Keys/VMGKey.java b/racevisionGame/src/main/java/visualiser/gameController/Keys/VMGKey.java index e6d82ba7..c01658bb 100644 --- a/racevisionGame/src/main/java/visualiser/gameController/Keys/VMGKey.java +++ b/racevisionGame/src/main/java/visualiser/gameController/Keys/VMGKey.java @@ -1,6 +1,7 @@ package visualiser.gameController.Keys; import javafx.scene.input.KeyCode; +import network.Messages.Enums.BoatActionEnum; /** * Key to trigger auto VMG @@ -13,7 +14,7 @@ public class VMGKey extends ControlKey{ * @param name name of the key */ public VMGKey(String name) { - super(name, 1); + super(name, BoatActionEnum.AUTO_PILOT); } @Override