diff --git a/racevisionGame/src/main/java/mock/model/MockBoat.java b/racevisionGame/src/main/java/mock/model/MockBoat.java index 6ab43cf3..86570c75 100644 --- a/racevisionGame/src/main/java/mock/model/MockBoat.java +++ b/racevisionGame/src/main/java/mock/model/MockBoat.java @@ -24,10 +24,18 @@ public class MockBoat extends Boat implements Collider { */ private long timeSinceTackChange = 0; + /** + * This stores the boats current status of rounding a mark + * 0: not started rounding + * 1: passed only first check + * 2: passed first and second check + */ + private Integer roundingStatus = 0; + /** * Stores whether the boat is on autoVMG or not */ - private boolean autoVMG = true; + private boolean autoVMG = false; @@ -69,7 +77,15 @@ public class MockBoat extends Boat implements Collider { //Get the start and end points. GPSCoordinate currentPosition = this.getCurrentPosition(); - GPSCoordinate nextMarkerPosition = this.getCurrentLeg().getEndCompoundMark().getAverageGPSCoordinate(); + GPSCoordinate nextMarkerPosition; + + // if boat is at the finish + if (this.getCurrentLeg().getEndCompoundMark() == null) { + nextMarkerPosition = currentPosition; + } + else { + nextMarkerPosition = this.getCurrentLeg().getEndCompoundMark().getAverageGPSCoordinate(); + } //Calculate bearing. Bearing bearing = GPSCoordinate.calculateBearing(currentPosition, nextMarkerPosition); @@ -96,9 +112,7 @@ public class MockBoat extends Boat implements Collider { //Calculate distance. - double distanceNauticalMiles = GPSCoordinate.calculateDistanceNauticalMiles(startPosition, endMarker); - - return distanceNauticalMiles; + return GPSCoordinate.calculateDistanceNauticalMiles(startPosition, endMarker); } @@ -198,7 +212,91 @@ public class MockBoat extends Boat implements Collider { return distanceTravelledMeters; } - public boolean isAutoVMG() { + /** + * Check if a mark is on the port side of the boat + * @param mark mark to be passed + * @return true if mark is on port side + */ + public boolean isPortSide(Mark mark){ + Bearing towardsMark = GPSCoordinate.calculateBearing(this.getCurrentPosition(), mark.getPosition()); + if (towardsMark.degrees() > 315 || towardsMark.degrees() <= 45){ + //south quadrant + return this.getBearing().degrees() <= 180; + } else if(towardsMark.degrees() > 45 && towardsMark.degrees() <= 135){ + //west quadrant + return (this.getBearing().degrees() <= 270 && this.getBearing().degrees() >= 90); + }else if(towardsMark.degrees() > 135 && towardsMark.degrees() <= 225){ + //north quadrant + return this.getBearing().degrees() >= 180; + }else if(towardsMark.degrees() > 225 && towardsMark.degrees() <= 315){ + //east quadrant + return (this.getBearing().degrees() <= 90 || this.getBearing().degrees() >= 270); + }else{ + //should not reach here + return false; + } + } + + /** + * Check if a mark is on the starboard side of the boat + * @param mark mark to be passed + * @return true if mark is on starboard side + */ + public boolean isStarboardSide(Mark mark){ + //if this boat is lower than the mark check which way it is facing + Bearing towardsMark = GPSCoordinate.calculateBearing(this.getCurrentPosition(), mark.getPosition()); + if (towardsMark.degrees() > 315 || towardsMark.degrees() <= 45){ + //south quadrant + return !(this.getBearing().degrees() <= 180); + } else if(towardsMark.degrees() > 45 && towardsMark.degrees() <= 135){ + //west quadrant + return !(this.getBearing().degrees() <= 270 && this.getBearing().degrees() >= 90); + }else if(towardsMark.degrees() > 135 && towardsMark.degrees() <= 225){ + //north quadrant + return !(this.getBearing().degrees() >= 180); + }else if(towardsMark.degrees() > 225 && towardsMark.degrees() <= 315){ + //east quadrant + return !(this.getBearing().degrees() <= 90 || this.getBearing().degrees() >= 270); + }else{ + //should not reach here + return false; + } + } + + /** + * Used to check if this boat is between a gate + * @param gate the gate to be checked + * @return true if the boat is between two marks that make up a gate + */ + public boolean isBetweenGate(CompoundMark gate){ + return (this.isPortSide(gate.getMark1()) && this.isStarboardSide(gate.getMark2())) || + (this.isStarboardSide(gate.getMark1()) && this.isPortSide(gate.getMark2())); + } + + /** + * Used to check if this boat is between a two marks + * @param mark1 the first mark + * @param mark2 the second mark + * @return true if the boat is between two marks + */ + public boolean isBetweenGate(Mark mark1, Mark mark2){ + return (this.isPortSide(mark1) && this.isStarboardSide(mark2)) || + (this.isStarboardSide(mark1) && this.isPortSide(mark2)); + } + + public Integer getRoundingStatus() { + return Integer.valueOf(roundingStatus); + } + + public void increaseRoundingStatus() { + this.roundingStatus++; + } + + public void resetRoundingStatus() { + this.roundingStatus = 0; + } + + public boolean getAutoVMG(){ return autoVMG; } diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index 9d03ff14..63ebf278 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -1,19 +1,15 @@ package mock.model; -import mock.model.collider.ColliderRegistry; import network.Messages.Enums.BoatStatusEnum; +import network.Messages.Enums.RaceStatusEnum; 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; -import java.time.temporal.TemporalUnit; import java.util.*; import static java.lang.Math.cos; @@ -53,13 +49,13 @@ public class MockRace extends Race { * @param boatDataSource Data source for boat related data (yachts and marker boats). * @param raceDataSource Data source for race related data (participating boats, legs, etc...). * @param regattaDataSource Data source for race related data (course name, location, timezone, etc...). - * @param latestMessages The LatestMessages to send events to. * @param polars The polars table to be used for boat simulation. * @param timeScale The timeScale for the race. See {@link Constants#RaceTimeScale}. + * @param windGenerator The wind generator used for the race. */ - public MockRace(BoatDataSource boatDataSource, RaceDataSource raceDataSource, RegattaDataSource regattaDataSource, LatestMessages latestMessages, Polars polars, int timeScale) { + public MockRace(BoatDataSource boatDataSource, RaceDataSource raceDataSource, RegattaDataSource regattaDataSource, Polars polars, int timeScale, WindGenerator windGenerator) { - super(boatDataSource, raceDataSource, regattaDataSource, latestMessages); + super(boatDataSource, raceDataSource, regattaDataSource); this.scaleFactor = timeScale; @@ -67,14 +63,8 @@ public class MockRace extends Race { this.shrinkBoundary = GPSCoordinate.getShrinkBoundary(this.boundary); - //Set up wind generator. It may be tidier to create this outside the race (with the values sourced from a data file maybe?) and pass it in. - this.windGenerator = new WindGenerator( - Bearing.fromDegrees(225), - Bearing.fromDegrees(215), - Bearing.fromDegrees(235), - 12d, - 8d, - 16d ); + + this.windGenerator = windGenerator; //Wind. this.setWind(windGenerator.generateBaselineWind()); @@ -341,7 +331,7 @@ public class MockRace extends Race { boat.moveForwards(distanceTravelledMeters); boat.setTimeSinceTackChange(boat.getTimeSinceTackChange() + updatePeriodMilliseconds); - if (boat.isAutoVMG()) { + if (boat.getAutoVMG()) { newOptimalVMG(boat); } @@ -351,7 +341,7 @@ public class MockRace extends Race { } private void newOptimalVMG(MockBoat boat) { - long tackPeriod = 15000; + long tackPeriod = 1000; if (boat.getTimeSinceTackChange() > tackPeriod) { //Calculate the new VMG. @@ -375,8 +365,8 @@ public class MockRace extends Race { this.getWindDirection(), this.getWindSpeed(), boat.getBearing(), - boat.getBearing(), - boat.getBearing()); + Bearing.fromDegrees(boat.getBearing().degrees() - 1), + Bearing.fromDegrees(boat.getBearing().degrees() + 1)); if (vmg.getSpeed() > 0) { boat.setCurrentSpeed(vmg.getSpeed()); } diff --git a/racevisionGame/src/main/java/shared/model/Race.java b/racevisionGame/src/main/java/shared/model/Race.java index 00b6eaf0..6df5ebbc 100644 --- a/racevisionGame/src/main/java/shared/model/Race.java +++ b/racevisionGame/src/main/java/shared/model/Race.java @@ -7,7 +7,6 @@ import javafx.beans.property.SimpleObjectProperty; import mock.model.collider.ColliderRegistry; 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; @@ -33,11 +32,6 @@ public abstract class Race { * The source of regatta related data. */ protected RegattaDataSource regattaDataSource; - /** - * The collection of latest race messages. - * Can be either read from or written to. - */ - protected LatestMessages latestMessages; /** * A list of compound marks in the race. */ @@ -80,7 +74,7 @@ public abstract class Race { protected ColliderRegistry colliderRegistry; /** * The number of frames per second. - * We essentially track the number of frames generated per second, over a one second period. When {@link #lastFpsResetTime} reaches 1 second, {@link #currentFps} is reset. + * We essentially track the number of frames generated per second, over a one second period. When {@link #lastFpsResetTime} reaches 1 second, currentFps is reset. */ private int currentFps = 0; /** @@ -92,53 +86,38 @@ public abstract class Race { */ private long lastFpsResetTime; - - /** * Constructs a race object with a given BoatDataSource, RaceDataSource, and RegattaDataSource. * @param boatDataSource Data source for boat related data (yachts and marker boats). * @param raceDataSource Data source for race related data (participating boats, legs, etc...). * @param regattaDataSource Data source for race related data (course name, location, timezone, etc...). - * @param latestMessages The collection of latest messages, which can be written to, or read from. */ - public Race(BoatDataSource boatDataSource, RaceDataSource raceDataSource, RegattaDataSource regattaDataSource, LatestMessages latestMessages) { + public Race(BoatDataSource boatDataSource, RaceDataSource raceDataSource, RegattaDataSource regattaDataSource) { //Keep a reference to data sources. this.raceDataSource = raceDataSource; this.boatDataSource = boatDataSource; this.regattaDataSource = regattaDataSource; - this.latestMessages = latestMessages; - - //Marks. this.compoundMarks = raceDataSource.getCompoundMarks(); - //Boundaries. this.boundary = raceDataSource.getBoundary(); - - //Legs. this.useLegsList(raceDataSource.getLegs()); - - //Race ID. this.raceId = raceDataSource.getRaceId(); - //Regatta name. this.regattaName = regattaDataSource.getRegattaName(); - //Race clock. this.raceClock = new RaceClock(this.raceDataSource.getStartDateTime()); - //Race status. this.setRaceStatusEnum(RaceStatusEnum.NOT_ACTIVE); //Race type. this.raceType = raceDataSource.getRaceType(); - //Wind. this.setWind(Bearing.fromDegrees(0), 0); - + // Set up colliders this.colliderRegistry = new ColliderRegistry(); this.colliderRegistry.addAllColliders(compoundMarks); } @@ -153,7 +132,6 @@ public abstract class Race { */ protected abstract void initialiseBoats(); - /** * Updates the race to use a new list of legs, and adds a dummy "Finish" leg at the end. * @param legs The new list of legs to use. @@ -185,10 +163,6 @@ public abstract class Race { return legID == lastLegID; } - - - - /** * Returns the current race status. * @return The current race status. @@ -205,7 +179,6 @@ public abstract class Race { this.raceStatusEnum = raceStatusEnum; } - /** * Returns the type of race this is. * @return The type of race this is. @@ -222,7 +195,6 @@ public abstract class Race { return regattaName; } - /** * Updates the race to have a specified wind bearing and speed. * @param windBearing New wind bearing. @@ -241,7 +213,6 @@ public abstract class Race { this.raceWind.setValue(wind); } - /** * Returns the wind bearing. * @return The wind bearing. @@ -268,7 +239,6 @@ public abstract class Race { return raceClock; } - /** * Returns the RaceDataSource used for the race. * @return The RaceDataSource used for the race. @@ -286,7 +256,6 @@ public abstract class Race { return legs.size() - 1; } - /** * Returns the race boundary. * @return The race boundary. @@ -295,12 +264,21 @@ public abstract class Race { return boundary; } + + /** + * Returns the marks of the race. + * @return Marks of the race. + */ + public List getCompoundMarks() { + return compoundMarks; + } + /** - * Returns the number of frames generated per second. - * @return Frames per second. + * Returns the legs of the race. + * @return Legs of the race. */ - public int getFps() { - return lastFps.getValue(); + public List getLegs() { + return legs; } /** @@ -311,7 +289,6 @@ public abstract class Race { return lastFps; } - /** * Increments the FPS counter, and adds timePeriod milliseconds to our FPS reset timer. * @param timePeriod Time, in milliseconds, to add to {@link #lastFpsResetTime}.