Merge branch 'master' into story_61
Also got client-server handshake working. Multiple clients can connect and control their own boat. The client's boat has a larger black triangle drawn around it. Probably needs tidying up. Added isClientBoat boolean to VisualiserBoat. WindGenerator is now an interface, implemented by ConstantWindGenerator (useful for testing) and RandomWindGenerator. Added ConstantWindGeneratorTest. RandomWindGenerator was formerly WindGenerator. The WindGenerator is passed in to MockRace. CommandFactory throws a CommandConstructionException if it cannot create a command. MessageSerialiser and MessageDeserialiser correctly terminate on error. Readded VisualiserInput's switch statement in the main loop, pending a refactor. Removed the sleep statement from LobbyController - it was blocking javaFX thread. Lobby.fxml: moved buttons and text input into grid panes, so they don't break when you resize screen. Added some test object creation functions in several test classes (MockRaceTest.createMockRace(), BoatXMLReaderTest.createBoatDataSource(), etc...). #story[1095]
This commit is contained in:
@@ -36,6 +36,11 @@ public class ClientConnection implements Runnable {
|
||||
*/
|
||||
private HeartBeatService heartBeatService;
|
||||
|
||||
/**
|
||||
* The thread the {@link HeartBeatService} runs on.
|
||||
*/
|
||||
private Thread heartBeatThread;
|
||||
|
||||
|
||||
/**
|
||||
* Used to allocate source ID to client, if they request to participate.
|
||||
@@ -53,16 +58,31 @@ public class ClientConnection implements Runnable {
|
||||
*/
|
||||
private CompositeCommand compositeCommand;
|
||||
|
||||
/**
|
||||
* The race the client is connected to.
|
||||
*/
|
||||
private RaceLogic raceLogic;
|
||||
|
||||
/**
|
||||
* Used to send the race snapshot to client.
|
||||
*/
|
||||
private MockOutput mockOutput;
|
||||
|
||||
/**
|
||||
* The thread the {@link MockOutput} runs on.
|
||||
*/
|
||||
private Thread mockOutputThread;
|
||||
|
||||
/**
|
||||
* Used to receive client input, and turn it into commands.
|
||||
*/
|
||||
private ControllerServer controllerServer;
|
||||
|
||||
/**
|
||||
* The thread the {@link ControllerServer} runs on.
|
||||
*/
|
||||
private Thread controllerServerThread;
|
||||
|
||||
|
||||
/**
|
||||
* Used to write messages to socket.
|
||||
@@ -100,13 +120,15 @@ public class ClientConnection implements Runnable {
|
||||
* @param sourceIdAllocator Used to allocate a source ID for the client.
|
||||
* @param latestMessages Latest race snapshot to send to client.
|
||||
* @param compositeCommand Collection of commands for race to execute.
|
||||
* @param raceLogic The race the client is connected to.
|
||||
* @throws IOException Thrown if there is a problem with the client socket.
|
||||
*/
|
||||
public ClientConnection(Socket socket, SourceIdAllocator sourceIdAllocator, LatestMessages latestMessages, CompositeCommand compositeCommand) throws IOException {
|
||||
public ClientConnection(Socket socket, SourceIdAllocator sourceIdAllocator, LatestMessages latestMessages, CompositeCommand compositeCommand, RaceLogic raceLogic) throws IOException {
|
||||
this.socket = socket;
|
||||
this.sourceIdAllocator = sourceIdAllocator;
|
||||
this.latestMessages = latestMessages;
|
||||
this.compositeCommand = compositeCommand;
|
||||
this.raceLogic = raceLogic;
|
||||
|
||||
this.outputQueue = new LinkedBlockingQueue<>();
|
||||
this.inputQueue = new LinkedBlockingQueue<>();
|
||||
@@ -120,7 +142,8 @@ public class ClientConnection implements Runnable {
|
||||
|
||||
|
||||
this.heartBeatService = new HeartBeatService(outputQueue);
|
||||
new Thread(heartBeatService, "ClientConnection()->HeartBeatService thread " + heartBeatService).start();
|
||||
this.heartBeatThread = new Thread(heartBeatService, "ClientConnection()->HeartBeatService thread " + heartBeatService);
|
||||
this.heartBeatThread.start();
|
||||
|
||||
}
|
||||
|
||||
@@ -145,7 +168,7 @@ public class ClientConnection implements Runnable {
|
||||
* @throws HandshakeException Thrown if something goes wrong with the handshake.
|
||||
* @throws SourceIDAllocationException Thrown if we cannot allocate a sourceID.
|
||||
*/
|
||||
private void handshake() throws SourceIDAllocationException, HandshakeException {
|
||||
private void handshake() throws SourceIDAllocationException, HandshakeException {
|
||||
|
||||
//This function is a bit messy, and could probably be refactored a bit.
|
||||
|
||||
@@ -162,16 +185,20 @@ public class ClientConnection implements Runnable {
|
||||
|
||||
allocatedSourceID = sourceIdAllocator.allocateSourceID();
|
||||
|
||||
this.controllerServer = new ControllerServer(compositeCommand, inputQueue, allocatedSourceID);
|
||||
new Thread(controllerServer, "ClientConnection.run()->ControllerServer thread" + controllerServer).start();
|
||||
this.controllerServer = new ControllerServer(compositeCommand, inputQueue, allocatedSourceID, raceLogic.getRace());
|
||||
this.controllerServerThread = new Thread(controllerServer, "ClientConnection.run()->ControllerServer thread" + controllerServer);
|
||||
this.controllerServerThread.start();
|
||||
|
||||
}
|
||||
|
||||
this.mockOutput = new MockOutput(latestMessages, outputQueue);
|
||||
new Thread(mockOutput, "ClientConnection.run()->MockOutput thread" + mockOutput).start();
|
||||
|
||||
sendJoinAcceptanceMessage(allocatedSourceID);
|
||||
|
||||
this.mockOutput = new MockOutput(latestMessages, outputQueue);
|
||||
this.mockOutputThread = new Thread(mockOutput, "ClientConnection.run()->MockOutput thread" + mockOutput);
|
||||
this.mockOutputThread.start();
|
||||
|
||||
|
||||
connectionState = ConnectionStateEnum.CONNECTED;
|
||||
|
||||
}
|
||||
@@ -240,4 +267,22 @@ public class ClientConnection implements Runnable {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Terminates this connection.
|
||||
*/
|
||||
public void terminate() {
|
||||
|
||||
if (this.heartBeatThread != null) {
|
||||
this.heartBeatThread.interrupt();
|
||||
}
|
||||
|
||||
if (this.mockOutputThread != null) {
|
||||
this.mockOutputThread.interrupt();
|
||||
}
|
||||
|
||||
if (this.controllerServerThread != null) {
|
||||
this.controllerServerThread.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package mock.model;
|
||||
|
||||
|
||||
import shared.model.Bearing;
|
||||
import shared.model.Wind;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* This class generates Wind objects for use in a MockRace.
|
||||
* Initialised with a baseline wind speed and direction, and keeps it constant.
|
||||
*/
|
||||
public class ConstantWindGenerator implements WindGenerator {
|
||||
|
||||
/**
|
||||
* The bearing the wind direction starts at.
|
||||
*/
|
||||
private Bearing windBaselineBearing;
|
||||
|
||||
|
||||
/**
|
||||
* The speed the wind starts at, in knots.
|
||||
*/
|
||||
private double windBaselineSpeed;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a constant wind generator, with a baseline wind speed and direction.
|
||||
* @param windBaselineBearing Baseline wind direction.
|
||||
* @param windBaselineSpeed Baseline wind speed, in knots.
|
||||
*/
|
||||
public ConstantWindGenerator(Bearing windBaselineBearing, double windBaselineSpeed) {
|
||||
|
||||
this.windBaselineBearing = windBaselineBearing;
|
||||
this.windBaselineSpeed = windBaselineSpeed;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Wind generateBaselineWind() {
|
||||
return new Wind(windBaselineBearing, windBaselineSpeed);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Wind generateNextWind(Wind currentWind) {
|
||||
|
||||
return generateBaselineWind();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,7 +25,7 @@ public class MockBoat extends Boat {
|
||||
/**
|
||||
* Stores whether the boat is on autoVMG or not
|
||||
*/
|
||||
private boolean autoVMG = true;
|
||||
private boolean autoVMG = false;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ 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;
|
||||
@@ -13,7 +12,6 @@ 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;
|
||||
@@ -59,7 +57,7 @@ public class MockRace extends Race {
|
||||
* @param polars The polars table to be used for boat simulation.
|
||||
* @param timeScale The timeScale for the race. See {@link Constants#RaceTimeScale}.
|
||||
*/
|
||||
public MockRace(BoatDataSource boatDataSource, RaceDataSource raceDataSource, RegattaDataSource regattaDataSource, LatestMessages latestMessages, Polars polars, int timeScale) {
|
||||
public MockRace(BoatDataSource boatDataSource, RaceDataSource raceDataSource, RegattaDataSource regattaDataSource, LatestMessages latestMessages, Polars polars, int timeScale, WindGenerator windGenerator) {
|
||||
|
||||
super(boatDataSource, raceDataSource, regattaDataSource, latestMessages);
|
||||
|
||||
@@ -69,14 +67,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());
|
||||
|
||||
@@ -2,14 +2,15 @@ package mock.model;
|
||||
|
||||
import javafx.animation.AnimationTimer;
|
||||
import mock.model.commandFactory.CompositeCommand;
|
||||
import mock.model.commandFactory.CommandFactory;
|
||||
import network.Messages.Enums.BoatActionEnum;
|
||||
import network.Messages.Enums.BoatStatusEnum;
|
||||
import network.Messages.Enums.RaceStatusEnum;
|
||||
import network.Messages.LatestMessages;
|
||||
import shared.model.RunnableWithFramePeriod;
|
||||
|
||||
|
||||
|
||||
public class RaceLogic implements Runnable {
|
||||
|
||||
public class RaceLogic implements RunnableWithFramePeriod {
|
||||
/**
|
||||
* State of current race modified by this object
|
||||
*/
|
||||
@@ -66,6 +67,7 @@ public class RaceLogic implements Runnable {
|
||||
//Parse the race snapshot.
|
||||
server.parseSnapshot();
|
||||
|
||||
|
||||
// Change wind direction
|
||||
race.changeWindDirection();
|
||||
|
||||
@@ -113,7 +115,7 @@ public class RaceLogic implements Runnable {
|
||||
currentTime = System.currentTimeMillis();
|
||||
|
||||
//Execute commands from clients.
|
||||
commands.execute(race);
|
||||
commands.execute();
|
||||
|
||||
//Update race time.
|
||||
race.updateRaceTime(currentTime);
|
||||
@@ -172,4 +174,12 @@ public class RaceLogic implements Runnable {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the race state that this RaceLogic is simulating.
|
||||
* @return Race state this RaceLogic is simulating.
|
||||
*/
|
||||
public MockRace getRace() {
|
||||
return race;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
package mock.model;
|
||||
|
||||
|
||||
import shared.model.Bearing;
|
||||
import shared.model.Wind;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* This class generates Wind objects for use in a MockRace.
|
||||
* Bounds on bearing and speed can be specified.
|
||||
* Wind can be completely random, or random incremental change.
|
||||
*/
|
||||
public class RandomWindGenerator implements WindGenerator {
|
||||
|
||||
/**
|
||||
* The bearing the wind direction starts at.
|
||||
*/
|
||||
private Bearing windBaselineBearing;
|
||||
|
||||
/**
|
||||
* The lower bearing angle that the wind may have.
|
||||
*/
|
||||
private Bearing windBearingLowerBound;
|
||||
|
||||
/**
|
||||
* The upper bearing angle that the wind may have.
|
||||
*/
|
||||
private Bearing windBearingUpperBound;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The speed the wind starts at, in knots.
|
||||
*/
|
||||
private double windBaselineSpeed;
|
||||
|
||||
/**
|
||||
* The lower speed that the wind may have, in knots.
|
||||
*/
|
||||
private double windSpeedLowerBound;
|
||||
|
||||
/**
|
||||
* The upper speed that the wind may have, in knots.
|
||||
*/
|
||||
private double windSpeedUpperBound;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a wind generator, with a baseline, lower bound, and upper bound, for the wind speed and direction.
|
||||
* @param windBaselineBearing Baseline wind direction.
|
||||
* @param windBearingLowerBound Lower bound for wind direction.
|
||||
* @param windBearingUpperBound Upper bound for wind direction.
|
||||
* @param windBaselineSpeed Baseline wind speed, in knots.
|
||||
* @param windSpeedLowerBound Lower bound for wind speed, in knots.
|
||||
* @param windSpeedUpperBound Upper bound for wind speed, in knots.
|
||||
*/
|
||||
public RandomWindGenerator(Bearing windBaselineBearing, Bearing windBearingLowerBound, Bearing windBearingUpperBound, double windBaselineSpeed, double windSpeedLowerBound, double windSpeedUpperBound) {
|
||||
|
||||
this.windBaselineBearing = windBaselineBearing;
|
||||
this.windBearingLowerBound = windBearingLowerBound;
|
||||
this.windBearingUpperBound = windBearingUpperBound;
|
||||
this.windBaselineSpeed = windBaselineSpeed;
|
||||
this.windSpeedLowerBound = windSpeedLowerBound;
|
||||
this.windSpeedUpperBound = windSpeedUpperBound;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Wind generateBaselineWind() {
|
||||
return new Wind(windBaselineBearing, windBaselineSpeed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random Wind object, that is within the provided bounds.
|
||||
* @return Generated wind object.
|
||||
*/
|
||||
public Wind generateRandomWind() {
|
||||
|
||||
double windSpeed = generateRandomWindSpeed();
|
||||
Bearing windBearing = generateRandomWindBearing();
|
||||
|
||||
return new Wind(windBearing, windSpeed);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random wind speed within the specified bounds. In knots.
|
||||
* @return Wind speed, in knots.
|
||||
*/
|
||||
private double generateRandomWindSpeed() {
|
||||
|
||||
double randomSpeedKnots = generateRandomValueInBounds(windSpeedLowerBound, windSpeedUpperBound);
|
||||
|
||||
return randomSpeedKnots;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a random wind bearing within the specified bounds.
|
||||
* @return Wind bearing.
|
||||
*/
|
||||
private Bearing generateRandomWindBearing() {
|
||||
|
||||
double randomBearingDegrees = generateRandomValueInBounds(windBearingLowerBound.degrees(), windBearingUpperBound.degrees());
|
||||
|
||||
return Bearing.fromDegrees(randomBearingDegrees);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a random value within a specified interval.
|
||||
* @param lowerBound The lower bound of the interval.
|
||||
* @param upperBound The upper bound of the interval.
|
||||
* @return A random value within the interval.
|
||||
*/
|
||||
private static double generateRandomValueInBounds(double lowerBound, double upperBound) {
|
||||
|
||||
float proportion = new Random().nextFloat();
|
||||
|
||||
double delta = upperBound - lowerBound;
|
||||
|
||||
double amount = delta * proportion;
|
||||
|
||||
double finalAmount = amount + lowerBound;
|
||||
|
||||
return finalAmount;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a new value within an interval, given a start value, chance to change, and change amount.
|
||||
* @param lowerBound Lower bound of interval.
|
||||
* @param upperBound Upper bound of interval.
|
||||
* @param currentValue The current value to change.
|
||||
* @param changeAmount The amount to change by.
|
||||
* @param chanceToChange The change to actually change the value.
|
||||
* @return The new value.
|
||||
*/
|
||||
private static double generateNextValueInBounds(double lowerBound, double upperBound, double currentValue, double changeAmount, double chanceToChange) {
|
||||
|
||||
float chance = new Random().nextFloat();
|
||||
|
||||
|
||||
if (chance <= chanceToChange) {
|
||||
currentValue += changeAmount;
|
||||
|
||||
} else if (chance <= (2 * chanceToChange)) {
|
||||
currentValue -= changeAmount;
|
||||
|
||||
}
|
||||
|
||||
currentValue = clamp(lowerBound, upperBound, currentValue);
|
||||
|
||||
return currentValue;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Wind generateNextWind(Wind currentWind) {
|
||||
|
||||
double windSpeed = generateNextWindSpeed(currentWind.getWindSpeed());
|
||||
Bearing windBearing = generateNextWindBearing(currentWind.getWindDirection());
|
||||
|
||||
return new Wind(windBearing, windSpeed);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates the next wind speed to use.
|
||||
* @param windSpeed Current wind speed, in knots.
|
||||
* @return Next wind speed, in knots.
|
||||
*/
|
||||
private double generateNextWindSpeed(double windSpeed) {
|
||||
|
||||
double chanceToChange = 0.2;
|
||||
double changeAmount = 0.1;
|
||||
|
||||
double nextWindSpeed = generateNextValueInBounds(
|
||||
windSpeedLowerBound,
|
||||
windSpeedUpperBound,
|
||||
windSpeed,
|
||||
changeAmount,
|
||||
chanceToChange);
|
||||
|
||||
return nextWindSpeed;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates the next wind speed to use.
|
||||
* @param windBearing Current wind bearing.
|
||||
* @return Next wind speed.
|
||||
*/
|
||||
private Bearing generateNextWindBearing(Bearing windBearing) {
|
||||
|
||||
double chanceToChange = 0.2;
|
||||
double changeAmount = 0.5;
|
||||
|
||||
double nextWindBearingDegrees = generateNextValueInBounds(
|
||||
windBearingLowerBound.degrees(),
|
||||
windBearingUpperBound.degrees(),
|
||||
windBearing.degrees(),
|
||||
changeAmount,
|
||||
chanceToChange);
|
||||
|
||||
return Bearing.fromDegrees(nextWindBearingDegrees);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Clamps a value to be within an interval.
|
||||
* @param lower Lower bound of the interval.
|
||||
* @param upper Upper bound of the interval.
|
||||
* @param value Value to clamp.
|
||||
* @return The clamped value.
|
||||
*/
|
||||
private static double clamp(double lower, double upper, double value) {
|
||||
|
||||
if (value > upper) {
|
||||
value = upper;
|
||||
|
||||
} else if (value < lower) {
|
||||
value = lower;
|
||||
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,249 +1,29 @@
|
||||
package mock.model;
|
||||
|
||||
|
||||
import shared.model.Bearing;
|
||||
import shared.model.Wind;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* This class generates Wind objects for use in a MockRace.
|
||||
* Bounds on bearing and speed can be specified.
|
||||
* Wind can be completely random, or random incremental change.
|
||||
* Interface for wind generators. It allows for generating a baseline wind, and subsequent winds.
|
||||
*/
|
||||
public class WindGenerator {
|
||||
public interface WindGenerator {
|
||||
|
||||
/**
|
||||
* The bearing the wind direction starts at.
|
||||
*/
|
||||
private Bearing windBaselineBearing;
|
||||
|
||||
/**
|
||||
* The lower bearing angle that the wind may have.
|
||||
*/
|
||||
private Bearing windBearingLowerBound;
|
||||
|
||||
/**
|
||||
* The upper bearing angle that the wind may have.
|
||||
*/
|
||||
private Bearing windBearingUpperBound;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The speed the wind starts at, in knots.
|
||||
*/
|
||||
private double windBaselineSpeed;
|
||||
|
||||
/**
|
||||
* The lower speed that the wind may have, in knots.
|
||||
*/
|
||||
private double windSpeedLowerBound;
|
||||
|
||||
/**
|
||||
* The upper speed that the wind may have, in knots.
|
||||
*/
|
||||
private double windSpeedUpperBound;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a wind generator, with a baseline, lower bound, and upper bound, for the wind speed and direction.
|
||||
* @param windBaselineBearing Baseline wind direction.
|
||||
* @param windBearingLowerBound Lower bound for wind direction.
|
||||
* @param windBearingUpperBound Upper bound for wind direction.
|
||||
* @param windBaselineSpeed Baseline wind speed, in knots.
|
||||
* @param windSpeedLowerBound Lower bound for wind speed, in knots.
|
||||
* @param windSpeedUpperBound Upper bound for wind speed, in knots.
|
||||
*/
|
||||
public WindGenerator(Bearing windBaselineBearing, Bearing windBearingLowerBound, Bearing windBearingUpperBound, double windBaselineSpeed, double windSpeedLowerBound, double windSpeedUpperBound) {
|
||||
|
||||
this.windBaselineBearing = windBaselineBearing;
|
||||
this.windBearingLowerBound = windBearingLowerBound;
|
||||
this.windBearingUpperBound = windBearingUpperBound;
|
||||
this.windBaselineSpeed = windBaselineSpeed;
|
||||
this.windSpeedLowerBound = windSpeedLowerBound;
|
||||
this.windSpeedUpperBound = windSpeedUpperBound;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a wind object using the baseline wind speed and bearing.
|
||||
* @return Baseline wind object.
|
||||
*/
|
||||
public Wind generateBaselineWind() {
|
||||
return new Wind(windBaselineBearing, windBaselineSpeed);
|
||||
}
|
||||
Wind generateBaselineWind();
|
||||
|
||||
/**
|
||||
* Generates a random Wind object, that is within the provided bounds.
|
||||
* @return Generated wind object.
|
||||
*/
|
||||
public Wind generateRandomWind() {
|
||||
|
||||
double windSpeed = generateRandomWindSpeed();
|
||||
Bearing windBearing = generateRandomWindBearing();
|
||||
|
||||
return new Wind(windBearing, windSpeed);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random wind speed within the specified bounds. In knots.
|
||||
* @return Wind speed, in knots.
|
||||
*/
|
||||
private double generateRandomWindSpeed() {
|
||||
|
||||
double randomSpeedKnots = generateRandomValueInBounds(windSpeedLowerBound, windSpeedUpperBound);
|
||||
|
||||
return randomSpeedKnots;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a random wind bearing within the specified bounds.
|
||||
* @return Wind bearing.
|
||||
*/
|
||||
private Bearing generateRandomWindBearing() {
|
||||
|
||||
double randomBearingDegrees = generateRandomValueInBounds(windBearingLowerBound.degrees(), windBearingUpperBound.degrees());
|
||||
|
||||
return Bearing.fromDegrees(randomBearingDegrees);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a random value within a specified interval.
|
||||
* @param lowerBound The lower bound of the interval.
|
||||
* @param upperBound The upper bound of the interval.
|
||||
* @return A random value within the interval.
|
||||
*/
|
||||
private static double generateRandomValueInBounds(double lowerBound, double upperBound) {
|
||||
|
||||
float proportion = new Random().nextFloat();
|
||||
|
||||
double delta = upperBound - lowerBound;
|
||||
|
||||
double amount = delta * proportion;
|
||||
|
||||
double finalAmount = amount + lowerBound;
|
||||
|
||||
return finalAmount;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a new value within an interval, given a start value, chance to change, and change amount.
|
||||
* @param lowerBound Lower bound of interval.
|
||||
* @param upperBound Upper bound of interval.
|
||||
* @param currentValue The current value to change.
|
||||
* @param changeAmount The amount to change by.
|
||||
* @param chanceToChange The change to actually change the value.
|
||||
* @return The new value.
|
||||
*/
|
||||
private static double generateNextValueInBounds(double lowerBound, double upperBound, double currentValue, double changeAmount, double chanceToChange) {
|
||||
|
||||
float chance = new Random().nextFloat();
|
||||
|
||||
|
||||
if (chance <= chanceToChange) {
|
||||
currentValue += changeAmount;
|
||||
|
||||
} else if (chance <= (2 * chanceToChange)) {
|
||||
currentValue -= changeAmount;
|
||||
|
||||
}
|
||||
|
||||
currentValue = clamp(lowerBound, upperBound, currentValue);
|
||||
|
||||
return currentValue;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates the next Wind object, that is within the provided bounds. This randomly increases or decreases the wind's speed and bearing.
|
||||
* Generates the next Wind object, according to the implementation of the wind generator.
|
||||
* @param currentWind The current wind to change. This is not modified.
|
||||
* @return Generated wind object.
|
||||
*/
|
||||
public Wind generateNextWind(Wind currentWind) {
|
||||
|
||||
double windSpeed = generateNextWindSpeed(currentWind.getWindSpeed());
|
||||
Bearing windBearing = generateNextWindBearing(currentWind.getWindDirection());
|
||||
|
||||
return new Wind(windBearing, windSpeed);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates the next wind speed to use.
|
||||
* @param windSpeed Current wind speed, in knots.
|
||||
* @return Next wind speed, in knots.
|
||||
*/
|
||||
private double generateNextWindSpeed(double windSpeed) {
|
||||
|
||||
double chanceToChange = 0.2;
|
||||
double changeAmount = 0.1;
|
||||
|
||||
double nextWindSpeed = generateNextValueInBounds(
|
||||
windSpeedLowerBound,
|
||||
windSpeedUpperBound,
|
||||
windSpeed,
|
||||
changeAmount,
|
||||
chanceToChange);
|
||||
|
||||
return nextWindSpeed;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates the next wind speed to use.
|
||||
* @param windBearing Current wind bearing.
|
||||
* @return Next wind speed.
|
||||
*/
|
||||
private Bearing generateNextWindBearing(Bearing windBearing) {
|
||||
|
||||
double chanceToChange = 0.2;
|
||||
double changeAmount = 0.5;
|
||||
|
||||
double nextWindBearingDegrees = generateNextValueInBounds(
|
||||
windBearingLowerBound.degrees(),
|
||||
windBearingUpperBound.degrees(),
|
||||
windBearing.degrees(),
|
||||
changeAmount,
|
||||
chanceToChange);
|
||||
|
||||
return Bearing.fromDegrees(nextWindBearingDegrees);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Clamps a value to be within an interval.
|
||||
* @param lower Lower bound of the interval.
|
||||
* @param upper Upper bound of the interval.
|
||||
* @param value Value to clamp.
|
||||
* @return The clamped value.
|
||||
*/
|
||||
private static double clamp(double lower, double upper, double value) {
|
||||
|
||||
if (value > upper) {
|
||||
value = upper;
|
||||
|
||||
} else if (value < lower) {
|
||||
value = lower;
|
||||
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Wind generateNextWind(Wind currentWind);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package mock.model.commandFactory;
|
||||
|
||||
import mock.exceptions.CommandConstructionException;
|
||||
import mock.model.MockBoat;
|
||||
import mock.model.MockRace;
|
||||
import network.Messages.Enums.BoatActionEnum;
|
||||
import network.Messages.BoatAction;
|
||||
import shared.exceptions.BoatNotFoundException;
|
||||
|
||||
/**
|
||||
* Factory class for Command objects
|
||||
@@ -11,15 +13,28 @@ 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
|
||||
* @return The command to execute the given action.
|
||||
* @throws CommandConstructionException Thrown if the command cannot be constructed (e.g., unknown action type).
|
||||
*/
|
||||
public static Command createCommand(MockRace race, MockBoat boat, BoatActionEnum action) {
|
||||
switch(action) {
|
||||
public static Command createCommand(MockRace race, BoatAction action) throws CommandConstructionException {
|
||||
|
||||
MockBoat boat = null;
|
||||
try {
|
||||
boat = race.getBoat(action.getSourceID());
|
||||
|
||||
} catch (BoatNotFoundException e) {
|
||||
throw new CommandConstructionException("Could not create command for BoatAction: " + action + ". Boat with sourceID: " + action.getSourceID() + " not found.", e);
|
||||
|
||||
}
|
||||
|
||||
switch(action.getBoatAction()) {
|
||||
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
|
||||
case UPWIND: return new WindCommand(race, boat, true);
|
||||
case DOWNWIND: return new WindCommand(race, boat, false);
|
||||
|
||||
default: throw new CommandConstructionException("Could not create command for BoatAction: " + action + ". Unknown BoatAction.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
package mock.model.commandFactory;
|
||||
|
||||
import mock.model.MockRace;
|
||||
|
||||
import java.util.Stack;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
* Wraps multiple commands into a composite to execute queued commands during a frame.
|
||||
*/
|
||||
public class CompositeCommand implements Command {
|
||||
private Stack<Command> commands;
|
||||
private Queue<Command> commands;
|
||||
|
||||
public CompositeCommand() {
|
||||
this.commands = new Stack<>();
|
||||
this.commands = new ArrayDeque<>();
|
||||
}
|
||||
|
||||
public void addCommand(Command command) {
|
||||
commands.push(command);
|
||||
commands.add(command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(MockRace race) {
|
||||
while(!commands.isEmpty()) commands.pop().execute(race);
|
||||
public void execute() {
|
||||
while(!commands.isEmpty()) commands.remove().execute();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ public class TackGybeCommand implements Command {
|
||||
//The refactoring of MockRace will require changes to be made
|
||||
@Override
|
||||
public void execute() {
|
||||
|
||||
boat.setAutoVMG(false);
|
||||
|
||||
/*VMG newVMG = boat.getPolars().calculateVMG(
|
||||
race.getWindDirection(),
|
||||
race.getWindSpeed(),
|
||||
|
||||
@@ -18,6 +18,7 @@ public class VMGCommand implements Command {
|
||||
//The refactoring of MockRace will require changes to be made
|
||||
@Override
|
||||
public void execute() {
|
||||
boat.setAutoVMG(true);
|
||||
/*VMG newVMG = boat.getPolars().calculateVMG(
|
||||
race.getWindDirection(),
|
||||
race.getWindSpeed(),
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package mock.model.commandFactory;
|
||||
|
||||
import mock.model.MockBoat;
|
||||
import mock.model.MockRace;
|
||||
import shared.model.Bearing;
|
||||
|
||||
/**
|
||||
* Created by connortaylorbrown on 4/08/17.
|
||||
*/
|
||||
public class WindCommand implements Command {
|
||||
private MockRace race;
|
||||
private MockBoat boat;
|
||||
private int direction;
|
||||
|
||||
public WindCommand(MockRace race, MockBoat boat, boolean upwind) {
|
||||
this.race = race;
|
||||
this.boat = boat;
|
||||
this.direction = upwind? -1 : 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
|
||||
boat.setAutoVMG(false);
|
||||
|
||||
double wind = race.getWindDirection().degrees();
|
||||
double heading = boat.getBearing().degrees();
|
||||
|
||||
double offset = 3.0;
|
||||
|
||||
offset *= direction;
|
||||
double headWindDelta = wind - heading;
|
||||
if ((headWindDelta < 0) || (headWindDelta > 180)) offset *= -1;
|
||||
|
||||
boat.setBearing(Bearing.fromDegrees(heading + offset));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user