mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 06:18:44 +00:00
Refactor. Taken Rounding logic out of yacht and into game state.
tags: #story[1124]
This commit is contained in:
@@ -9,7 +9,7 @@ prints out event details, including time, involved boats and legs.
|
|||||||
|
|
||||||
- Configuration file
|
- Configuration file
|
||||||
|
|
||||||
We decided to store the team information including team names and boat velocity, as well as race configuration setting in external file.
|
We decided to store the team information including team names and boat currentVelocity, as well as race configuration setting in external file.
|
||||||
|
|
||||||
To read external files, "Json-simple" library has been used to parse information.
|
To read external files, "Json-simple" library has been used to parse information.
|
||||||
By using this library, we did not have to write our json parser and benefited from the flexibility of json files.
|
By using this library, we did not have to write our json parser and benefited from the flexibility of json files.
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ You can specify a config file using the using the -f flag, for example 'java -ja
|
|||||||
|
|
||||||
## The config file
|
## The config file
|
||||||
|
|
||||||
The teams/boats are specified in the config file under 'teams', each team requires a team name, and a velocity (in meters per second).
|
The teams/boats are specified in the config file under 'teams', each team requires a team name, and a currentVelocity (in meters per second).
|
||||||
|
|
||||||
The 'time-scale' option lets you change how long the race takes to complete. A time-scale of 1.0 is normal speed, 2.0 is 2x etc.
|
The 'time-scale' option lets you change how long the race takes to complete. A time-scale of 1.0 is normal speed, 2.0 is 2x etc.
|
||||||
|
|
||||||
|
|||||||
@@ -8,27 +8,35 @@ import seng302.gameServer.server.messages.BoatAction;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import seng302.gameServer.server.messages.MarkRoundingMessage;
|
import seng302.gameServer.server.messages.MarkRoundingMessage;
|
||||||
|
import seng302.gameServer.server.messages.MarkType;
|
||||||
import seng302.gameServer.server.messages.Message;
|
import seng302.gameServer.server.messages.Message;
|
||||||
|
import seng302.gameServer.server.messages.RoundingBoatStatus;
|
||||||
|
import seng302.model.GeoPoint;
|
||||||
import seng302.model.Player;
|
import seng302.model.Player;
|
||||||
|
import seng302.model.PolarTable;
|
||||||
import seng302.model.Yacht;
|
import seng302.model.Yacht;
|
||||||
|
import seng302.model.mark.CompoundMark;
|
||||||
|
import seng302.model.mark.Mark;
|
||||||
import seng302.model.mark.MarkOrder;
|
import seng302.model.mark.MarkOrder;
|
||||||
|
import seng302.utilities.GeoUtility;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Static class to hold information about the current state of the game (model)
|
* A Static class to hold information about the current state of the game (model)
|
||||||
|
* Also contains logic for updating itself on regular time intervals on its own thread
|
||||||
* Created by wmu16 on 10/07/17.
|
* Created by wmu16 on 10/07/17.
|
||||||
*/
|
*/
|
||||||
public class GameState implements Runnable {
|
public class GameState implements Runnable {
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
interface MarkPassingListener {
|
interface MarkPassingListener {
|
||||||
|
|
||||||
void markPassing(Message message);
|
void markPassing(Message message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Logger logger = LoggerFactory.getLogger(GameState.class);
|
private Logger logger = LoggerFactory.getLogger(GameState.class);
|
||||||
|
|
||||||
private static Integer STATE_UPDATES_PER_SECOND = 60;
|
private static final Integer STATE_UPDATES_PER_SECOND = 60;
|
||||||
public static Integer MAX_PLAYERS = 8;
|
public static Integer MAX_PLAYERS = 8;
|
||||||
|
public static Double ROUNDING_DISTANCE = 50d; // TODO: 14/08/17 wmu16 - Look into this value further
|
||||||
|
|
||||||
private static Long previousUpdateTime;
|
private static Long previousUpdateTime;
|
||||||
public static Double windDirection;
|
public static Double windDirection;
|
||||||
@@ -135,56 +143,17 @@ public class GameState implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Double getWindSpeedKnots() {
|
public static Double getWindSpeedKnots() {
|
||||||
return windSpeed / 1000 * 1.943844492; // TODO: 26/07/17 cir27 - remove magic numbers
|
return GeoUtility.mmsToKnots(windSpeed); // TODO: 26/07/17 cir27 - remove magic numbers
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Map<Integer, Yacht> getYachts() {
|
public static Map<Integer, Yacht> getYachts() {
|
||||||
return yachts;
|
return yachts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void updateBoat(Integer sourceId, BoatAction actionType) {
|
|
||||||
Yacht playerYacht = yachts.get(sourceId);
|
|
||||||
// System.out.println("-----------------------");
|
|
||||||
switch (actionType) {
|
|
||||||
case VMG:
|
|
||||||
playerYacht.turnToVMG();
|
|
||||||
// System.out.println("Snapping to VMG");
|
|
||||||
break;
|
|
||||||
case SAILS_IN:
|
|
||||||
playerYacht.toggleSailIn();
|
|
||||||
// System.out.println("Toggling Sails");
|
|
||||||
break;
|
|
||||||
case SAILS_OUT:
|
|
||||||
playerYacht.toggleSailIn();
|
|
||||||
// System.out.println("Toggling Sails");
|
|
||||||
break;
|
|
||||||
case TACK_GYBE:
|
|
||||||
playerYacht.tackGybe(windDirection);
|
|
||||||
// System.out.println("Tack/Gybe");
|
|
||||||
break;
|
|
||||||
case UPWIND:
|
|
||||||
playerYacht.turnUpwind();
|
|
||||||
// System.out.println("Moving upwind");
|
|
||||||
break;
|
|
||||||
case DOWNWIND:
|
|
||||||
playerYacht.turnDownwind();
|
|
||||||
// System.out.println("Moving downwind");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// printBoatStatus(playerYacht);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update() {
|
|
||||||
Long timeInterval = System.currentTimeMillis() - previousUpdateTime;
|
|
||||||
previousUpdateTime = System.currentTimeMillis();
|
|
||||||
for (Yacht yacht : yachts.values()) {
|
|
||||||
yacht.update(timeInterval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a new ID based off the size of current players + 1
|
* Generates a new ID based off the size of current players + 1
|
||||||
|
*
|
||||||
* @return a playerID to be allocated to a new connetion
|
* @return a playerID to be allocated to a new connetion
|
||||||
*/
|
*/
|
||||||
public static Integer getUniquePlayerID() {
|
public static Integer getUniquePlayerID() {
|
||||||
@@ -199,7 +168,7 @@ public class GameState implements Runnable {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
||||||
while(true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(1000 / STATE_UPDATES_PER_SECOND);
|
Thread.sleep(1000 / STATE_UPDATES_PER_SECOND);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
@@ -209,28 +178,317 @@ public class GameState implements Runnable {
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
//RACING
|
|
||||||
if (currentStage == GameStages.RACING) {
|
if (currentStage == GameStages.RACING) {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void printBoatStatus(Yacht playerYacht) {
|
public static void updateBoat(Integer sourceId, BoatAction actionType) {
|
||||||
System.out.println("-----------------------");
|
Yacht playerYacht = yachts.get(sourceId);
|
||||||
System.out.println("Sails are in: " + playerYacht.getSailIn());
|
switch (actionType) {
|
||||||
System.out.println("Heading: " + playerYacht.getHeading());
|
case VMG:
|
||||||
System.out.println("Velocity: " + playerYacht.getVelocityMMS() / 1000);
|
playerYacht.turnToVMG();
|
||||||
System.out.println("Lat: " + playerYacht.getLocation().getLat());
|
break;
|
||||||
System.out.println("Lng: " + playerYacht.getLocation().getLng());
|
case SAILS_IN:
|
||||||
System.out.println("-----------------------\n");
|
playerYacht.toggleSailIn();
|
||||||
|
break;
|
||||||
|
case SAILS_OUT:
|
||||||
|
playerYacht.toggleSailIn();
|
||||||
|
break;
|
||||||
|
case TACK_GYBE:
|
||||||
|
playerYacht.tackGybe(windDirection);
|
||||||
|
break;
|
||||||
|
case UPWIND:
|
||||||
|
playerYacht.turnUpwind();
|
||||||
|
break;
|
||||||
|
case DOWNWIND:
|
||||||
|
playerYacht.turnDownwind();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called periodically in this GameState thread to update the GameState values
|
||||||
|
*/
|
||||||
|
public void update() {
|
||||||
|
Double timeInterval = (System.currentTimeMillis() - previousUpdateTime) / 1000000.0;
|
||||||
|
previousUpdateTime = System.currentTimeMillis();
|
||||||
|
for (Yacht yacht : yachts.values()) {
|
||||||
|
updateVelocity(yacht);
|
||||||
|
yacht.runAutoPilot();
|
||||||
|
yacht.updateLocation(timeInterval);
|
||||||
|
if (!yacht.getFinishedRace()) {
|
||||||
|
checkForLegProgression(yacht);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void updateVelocity(Yacht yacht) {
|
||||||
|
Double velocity = yacht.getCurrentVelocity();
|
||||||
|
Double trueWindAngle = Math.abs(windDirection - yacht.getHeading());
|
||||||
|
Double boatSpeedInKnots = PolarTable.getBoatSpeed(getWindSpeedKnots(), trueWindAngle);
|
||||||
|
Double maxBoatSpeed = GeoUtility.knotsToMMS(boatSpeedInKnots);
|
||||||
|
yacht.setCurrentMaxVelocity(maxBoatSpeed);
|
||||||
|
|
||||||
|
if (yacht.getSailIn() && yacht.getCurrentVelocity() <= maxBoatSpeed && maxBoatSpeed != 0d) {
|
||||||
|
if (velocity < maxBoatSpeed) {
|
||||||
|
yacht.changeVelocity(maxBoatSpeed / 15);
|
||||||
|
}
|
||||||
|
if (velocity > maxBoatSpeed) {
|
||||||
|
yacht.setCurrentVelocity(maxBoatSpeed);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (velocity > 0d) {
|
||||||
|
if (maxBoatSpeed != 0d) {
|
||||||
|
yacht.changeVelocity(-maxBoatSpeed / 600);
|
||||||
|
} else {
|
||||||
|
yacht.changeVelocity(-velocity / 100);
|
||||||
|
}
|
||||||
|
if (velocity < 0) {
|
||||||
|
yacht.setCurrentVelocity(0d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the distance to the next mark (closest of the two if a gate mark). For purposes of
|
||||||
|
* mark rounding
|
||||||
|
*
|
||||||
|
* @return A distance in metres. Returns -1 if there is no next mark
|
||||||
|
* @throws IndexOutOfBoundsException If the next mark is null (ie the last mark in the race)
|
||||||
|
* Check first using {@link seng302.model.mark.MarkOrder#isLastMark(Integer)}
|
||||||
|
*/
|
||||||
|
private Double calcDistanceToCurrentMark(Yacht yacht) throws IndexOutOfBoundsException {
|
||||||
|
Integer currentMarkSeqID = yacht.getCurrentMarkSeqID();
|
||||||
|
CompoundMark currentMark = markOrder.getCurrentMark(currentMarkSeqID);
|
||||||
|
GeoPoint location = yacht.getLocation();
|
||||||
|
|
||||||
|
if (currentMark.isGate()) {
|
||||||
|
Mark sub1 = currentMark.getSubMark(1);
|
||||||
|
Mark sub2 = currentMark.getSubMark(2);
|
||||||
|
Double distance1 = GeoUtility.getDistance(location, sub1);
|
||||||
|
Double distance2 = GeoUtility.getDistance(location, sub2);
|
||||||
|
if (distance1 < distance2) {
|
||||||
|
yacht.setClosestCurrentMark(sub1);
|
||||||
|
return distance1;
|
||||||
|
} else {
|
||||||
|
yacht.setClosestCurrentMark(sub2);
|
||||||
|
return distance2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
yacht.setClosestCurrentMark(currentMark.getSubMark(1));
|
||||||
|
return GeoUtility.getDistance(location, currentMark.getSubMark(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 4 Different cases of progression in the race 1 - Passing the start line 2 - Passing any
|
||||||
|
* in-race Gate 3 - Passing any in-race Mark 4 - Passing the finish line
|
||||||
|
* @param yacht the current yacht to check for progression
|
||||||
|
*/
|
||||||
|
private void checkForLegProgression(Yacht yacht) {
|
||||||
|
Integer currentMarkSeqID = yacht.getCurrentMarkSeqID();
|
||||||
|
CompoundMark currentMark = markOrder.getCurrentMark(currentMarkSeqID);
|
||||||
|
|
||||||
|
Boolean hasProgressed;
|
||||||
|
if (currentMarkSeqID == 0) {
|
||||||
|
hasProgressed = checkStartLineCrossing(yacht);
|
||||||
|
} else if (markOrder.isLastMark(currentMarkSeqID)) {
|
||||||
|
hasProgressed = checkFinishLineCrossing(yacht);
|
||||||
|
} else if (currentMark.isGate()) {
|
||||||
|
hasProgressed = checkGateRounding(yacht);
|
||||||
|
} else {
|
||||||
|
hasProgressed = checkMarkRounding(yacht);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasProgressed) {
|
||||||
|
sendMarkRoundingMessage(yacht);
|
||||||
|
// logMarkRounding(yacht);
|
||||||
|
yacht.setHasPassedLine(false);
|
||||||
|
yacht.setHasEnteredRoundingZone(false);
|
||||||
|
yacht.setHasPassedThroughGate(false);
|
||||||
|
if (!markOrder.isLastMark(currentMarkSeqID)) {
|
||||||
|
yacht.incrementMarkSeqID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we pass the start line gate in the correct direction, progress
|
||||||
|
*
|
||||||
|
* @param yacht The current yacht to check for
|
||||||
|
*/
|
||||||
|
private Boolean checkStartLineCrossing(Yacht yacht) {
|
||||||
|
Integer currentMarkSeqID = yacht.getCurrentMarkSeqID();
|
||||||
|
CompoundMark currentMark = markOrder.getCurrentMark(currentMarkSeqID);
|
||||||
|
GeoPoint lastLocation = yacht.getLastLocation();
|
||||||
|
GeoPoint location = yacht.getLocation();
|
||||||
|
|
||||||
|
Mark mark1 = currentMark.getSubMark(1);
|
||||||
|
Mark mark2 = currentMark.getSubMark(2);
|
||||||
|
CompoundMark nextMark = markOrder.getNextMark(currentMarkSeqID);
|
||||||
|
|
||||||
|
Integer crossedLine = GeoUtility.checkCrossedLine(mark1, mark2, lastLocation, location);
|
||||||
|
if (crossedLine > 0) {
|
||||||
|
Boolean isClockwiseCross = GeoUtility.isClockwise(mark1, mark2, nextMark.getMidPoint());
|
||||||
|
if (crossedLine == 2 && isClockwiseCross || crossedLine == 1 && !isClockwiseCross) {
|
||||||
|
yacht.setClosestCurrentMark(mark1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This algorithm checks for mark rounding. And increments the currentMarSeqID number attribute
|
||||||
|
* of the yacht if so. A visual representation of this algorithm can be seen on the Wiki under
|
||||||
|
* 'mark passing algorithm'
|
||||||
|
*
|
||||||
|
* @param yacht The current yacht to check for
|
||||||
|
*/
|
||||||
|
private Boolean checkMarkRounding(Yacht yacht) {
|
||||||
|
Integer currentMarkSeqID = yacht.getCurrentMarkSeqID();
|
||||||
|
CompoundMark currentMark = markOrder.getCurrentMark(currentMarkSeqID);
|
||||||
|
GeoPoint lastLocation = yacht.getLastLocation();
|
||||||
|
GeoPoint location = yacht.getLocation();
|
||||||
|
GeoPoint nextPoint = markOrder.getNextMark(currentMarkSeqID).getMidPoint();
|
||||||
|
GeoPoint prevPoint = markOrder.getPreviousMark(currentMarkSeqID).getMidPoint();
|
||||||
|
GeoPoint midPoint = GeoUtility.getDirtyMidPoint(nextPoint, prevPoint);
|
||||||
|
|
||||||
|
if (calcDistanceToCurrentMark(yacht) < ROUNDING_DISTANCE) {
|
||||||
|
yacht.setHasEnteredRoundingZone(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//In case current mark is a gate, loop through all marks just in case
|
||||||
|
for (Mark thisCurrentMark : currentMark.getMarks()) {
|
||||||
|
if (GeoUtility.isPointInTriangle(lastLocation, location, midPoint, thisCurrentMark)) {
|
||||||
|
yacht.setHasPassedLine(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return yacht.hasPassedLine() && yacht.hasEnteredRoundingZone();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a gate line has been crossed and in the correct direction
|
||||||
|
*
|
||||||
|
* @param yacht The current yacht to check for
|
||||||
|
*/
|
||||||
|
private Boolean checkGateRounding(Yacht yacht) {
|
||||||
|
Integer currentMarkSeqID = yacht.getCurrentMarkSeqID();
|
||||||
|
CompoundMark currentMark = markOrder.getCurrentMark(currentMarkSeqID);
|
||||||
|
GeoPoint lastLocation = yacht.getLastLocation();
|
||||||
|
GeoPoint location = yacht.getLocation();
|
||||||
|
|
||||||
|
Mark mark1 = currentMark.getSubMark(1);
|
||||||
|
Mark mark2 = currentMark.getSubMark(2);
|
||||||
|
CompoundMark prevMark = markOrder.getPreviousMark(currentMarkSeqID);
|
||||||
|
CompoundMark nextMark = markOrder.getNextMark(currentMarkSeqID);
|
||||||
|
|
||||||
|
Integer crossedLine = GeoUtility.checkCrossedLine(mark1, mark2, lastLocation, location);
|
||||||
|
|
||||||
|
//We have crossed the line
|
||||||
|
if (crossedLine > 0) {
|
||||||
|
Boolean isClockwiseCross = GeoUtility.isClockwise(mark1, mark2, prevMark.getMidPoint());
|
||||||
|
|
||||||
|
//Check we cross the line in the correct direction
|
||||||
|
if (crossedLine == 1 && isClockwiseCross || crossedLine == 2 && !isClockwiseCross) {
|
||||||
|
yacht.setHasPassedThroughGate(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Boolean prevMarkSide = GeoUtility.isClockwise(mark1, mark2, prevMark.getMidPoint());
|
||||||
|
Boolean nextMarkSide = GeoUtility.isClockwise(mark1, mark2, nextMark.getMidPoint());
|
||||||
|
|
||||||
|
if (yacht.hasPassedThroughGate()) {
|
||||||
|
//Check if we need to round this gate after passing through
|
||||||
|
if (prevMarkSide == nextMarkSide) {
|
||||||
|
return checkMarkRounding(yacht);
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we pass the finish gate in the correct direction
|
||||||
|
*
|
||||||
|
* @param yacht The current yacht to check for
|
||||||
|
*/
|
||||||
|
private Boolean checkFinishLineCrossing(Yacht yacht) {
|
||||||
|
Integer currentMarkSeqID = yacht.getCurrentMarkSeqID();
|
||||||
|
CompoundMark currentMark = markOrder.getCurrentMark(currentMarkSeqID);
|
||||||
|
GeoPoint lastLocation = yacht.getLastLocation();
|
||||||
|
GeoPoint location = yacht.getLocation();
|
||||||
|
|
||||||
|
Mark mark1 = currentMark.getSubMark(1);
|
||||||
|
Mark mark2 = currentMark.getSubMark(2);
|
||||||
|
CompoundMark prevMark = markOrder.getPreviousMark(currentMarkSeqID);
|
||||||
|
|
||||||
|
Integer crossedLine = GeoUtility.checkCrossedLine(mark1, mark2, lastLocation, location);
|
||||||
|
if (crossedLine > 0) {
|
||||||
|
Boolean isClockwiseCross = GeoUtility.isClockwise(mark1, mark2, prevMark.getMidPoint());
|
||||||
|
if (crossedLine == 1 && isClockwiseCross || crossedLine == 2 && !isClockwiseCross) {
|
||||||
|
yacht.setClosestCurrentMark(mark1);
|
||||||
|
yacht.setIsFinished(true);
|
||||||
|
logger.debug(yacht.getSourceId() + " finished");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendMarkRoundingMessage(Yacht yacht) {
|
||||||
|
Integer sourceID = yacht.getSourceId();
|
||||||
|
Integer currentMarkSeqID = yacht.getCurrentMarkSeqID();
|
||||||
|
CompoundMark currentMark = markOrder.getCurrentMark(currentMarkSeqID);
|
||||||
|
MarkType markType = (currentMark.isGate()) ? MarkType.GATE : MarkType.ROUNDING_MARK;
|
||||||
|
Mark roundingMark = yacht.getClosestCurrentMark();
|
||||||
|
|
||||||
|
// TODO: 13/8/17 figure out the rounding side, rounded mark source ID and boat status.
|
||||||
|
Message markRoundingMessage = new MarkRoundingMessage(0, 0,
|
||||||
|
sourceID, RoundingBoatStatus.RACING, roundingMark.getRoundingSide(), markType,
|
||||||
|
roundingMark.getSourceID());
|
||||||
|
|
||||||
|
for (MarkPassingListener mpl : markListeners) {
|
||||||
|
mpl.markPassing(markRoundingMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void logMarkRounding(Yacht yacht) {
|
||||||
|
Mark roundingMark = yacht.getClosestCurrentMark();
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
String.format("Sending Mark Rounding Message:\n"
|
||||||
|
+ "AckNumber %d\n"
|
||||||
|
+ "RaceID %d\n"
|
||||||
|
+ "BoatSourceID %d\n"
|
||||||
|
+ "BoatStatus %s\n"
|
||||||
|
+ "Rounding Side %s\n"
|
||||||
|
+ "MarkSeqID %d",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
yacht.getSourceId(),
|
||||||
|
RoundingBoatStatus.RACING.name(),
|
||||||
|
roundingMark.getRoundingSide().getName(),
|
||||||
|
roundingMark.getSourceID()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void addMarkPassListener(MarkPassingListener listener) {
|
public static void addMarkPassListener(MarkPassingListener listener) {
|
||||||
markListeners.add(listener);
|
markListeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void removeMarkPassListenr(MarkPassingListener listener) {
|
|
||||||
markListeners.remove(listener);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ public class MainServerThread extends Observable implements Runnable, ClientConn
|
|||||||
serverLog("IO error in server thread handler upon trying to make new server socket", 0);
|
serverLog("IO error in server thread handler upon trying to make new server socket", 0);
|
||||||
}
|
}
|
||||||
PolarTable.parsePolarFile(getClass().getResourceAsStream("/config/acc_polars.csv"));
|
PolarTable.parsePolarFile(getClass().getResourceAsStream("/config/acc_polars.csv"));
|
||||||
|
GameState.addMarkPassListener(this::broadcastMessage);
|
||||||
terminated = false;
|
terminated = false;
|
||||||
thread = new Thread(this);
|
thread = new Thread(this);
|
||||||
thread.start();
|
thread.start();
|
||||||
@@ -89,7 +90,7 @@ public class MainServerThread extends Observable implements Runnable, ClientConn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void broadcastMessage(Message message) {
|
private void broadcastMessage(Message message) {
|
||||||
for (ServerToClientThread serverToClientThread : serverToClientThreads) {
|
for (ServerToClientThread serverToClientThread : serverToClientThreads) {
|
||||||
serverToClientThread.sendMessage(message);
|
serverToClientThread.sendMessage(message);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -308,7 +308,7 @@ public class ServerToClientThread implements Runnable, Observer {
|
|||||||
yacht.getLocation().getLat(),
|
yacht.getLocation().getLat(),
|
||||||
yacht.getLocation().getLng(),
|
yacht.getLocation().getLng(),
|
||||||
yacht.getHeading(),
|
yacht.getHeading(),
|
||||||
yacht.getVelocity().longValue());
|
yacht.getCurrentVelocity().longValue());
|
||||||
|
|
||||||
sendMessage(boatLocationMessage);
|
sendMessage(boatLocationMessage);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class MarkRoundingMessage extends Message{
|
|||||||
* The purpose of this is to record the time when yachts cross marks
|
* The purpose of this is to record the time when yachts cross marks
|
||||||
* @param ackNumber ackNumber
|
* @param ackNumber ackNumber
|
||||||
* @param raceId raceId
|
* @param raceId raceId
|
||||||
* @param sourceId sourceId
|
* @param sourceId boatSourceId
|
||||||
* @param roundingBoatStatus roundingBoatStatus
|
* @param roundingBoatStatus roundingBoatStatus
|
||||||
* @param roundingSide roundingSide
|
* @param roundingSide roundingSide
|
||||||
* @param markId markId
|
* @param markId markId
|
||||||
|
|||||||
@@ -15,11 +15,6 @@ import javafx.scene.paint.Color;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import seng302.gameServer.GameState;
|
import seng302.gameServer.GameState;
|
||||||
import seng302.gameServer.server.messages.MarkRoundingMessage;
|
|
||||||
import seng302.gameServer.server.messages.MarkType;
|
|
||||||
import seng302.gameServer.server.messages.Message;
|
|
||||||
import seng302.gameServer.server.messages.RoundingBoatStatus;
|
|
||||||
import seng302.gameServer.server.messages.RoundingSide;
|
|
||||||
import seng302.model.mark.CompoundMark;
|
import seng302.model.mark.CompoundMark;
|
||||||
import seng302.model.mark.Mark;
|
import seng302.model.mark.Mark;
|
||||||
import seng302.utilities.GeoUtility;
|
import seng302.utilities.GeoUtility;
|
||||||
@@ -39,8 +34,6 @@ public class Yacht extends Observable {
|
|||||||
|
|
||||||
private Logger logger = LoggerFactory.getLogger(Yacht.class);
|
private Logger logger = LoggerFactory.getLogger(Yacht.class);
|
||||||
|
|
||||||
private static final Double ROUNDING_DISTANCE = 50d; // TODO: 3/08/17 wmu16 - Look into this value further
|
|
||||||
|
|
||||||
|
|
||||||
//BOTH AFAIK
|
//BOTH AFAIK
|
||||||
private String boatType;
|
private String boatType;
|
||||||
@@ -64,7 +57,8 @@ public class Yacht extends Observable {
|
|||||||
private Boolean sailIn;
|
private Boolean sailIn;
|
||||||
private GeoPoint location;
|
private GeoPoint location;
|
||||||
private Integer boatStatus;
|
private Integer boatStatus;
|
||||||
private Double velocity;
|
private Double currentVelocity;
|
||||||
|
private Double currentMaxVelocity;
|
||||||
private Boolean isAuto;
|
private Boolean isAuto;
|
||||||
private Double autoHeading;
|
private Double autoHeading;
|
||||||
|
|
||||||
@@ -98,7 +92,7 @@ public class Yacht extends Observable {
|
|||||||
this.location = new GeoPoint(57.670341, 11.826856);
|
this.location = new GeoPoint(57.670341, 11.826856);
|
||||||
this.lastLocation = location;
|
this.lastLocation = location;
|
||||||
this.heading = 120.0; //In degrees
|
this.heading = 120.0; //In degrees
|
||||||
this.velocity = 0d; //in mms-1
|
this.currentVelocity = 0d; //in mms-1
|
||||||
|
|
||||||
this.hasEnteredRoundingZone = false;
|
this.hasEnteredRoundingZone = false;
|
||||||
this.hasPassedLine = false;
|
this.hasPassedLine = false;
|
||||||
@@ -106,51 +100,23 @@ public class Yacht extends Observable {
|
|||||||
this.finishedRace = false;
|
this.finishedRace = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param timeInterval since last update in milliseconds
|
* Changes the boats current currentVelocity by a set amount, positive or negative
|
||||||
|
* @param velocityChange The ammount to change the currentVelocity by, in mms-1
|
||||||
*/
|
*/
|
||||||
public void update(Long timeInterval) {
|
public void changeVelocity(Double velocityChange) {
|
||||||
|
currentVelocity += velocityChange;
|
||||||
|
}
|
||||||
|
|
||||||
Double secondsElapsed = timeInterval / 1000000.0;
|
/**
|
||||||
Double windSpeedKnots = GameState.getWindSpeedKnots();
|
* Updates the boat to a new GeoPoint whilst preserving the last location
|
||||||
Double trueWindAngle = Math.abs(GameState.getWindDirection() - heading);
|
*
|
||||||
Double boatSpeedInKnots = PolarTable.getBoatSpeed(windSpeedKnots, trueWindAngle);
|
* @param secondsElapsed The seconds elapsed since the last update of this yacht
|
||||||
Double maxBoatSpeed = boatSpeedInKnots / 1.943844492 * 1000;
|
*/
|
||||||
if (sailIn && velocity <= maxBoatSpeed && maxBoatSpeed != 0d) {
|
public void updateLocation(Double secondsElapsed) {
|
||||||
|
|
||||||
if (velocity < maxBoatSpeed) {
|
|
||||||
velocity += maxBoatSpeed / 15; // Acceleration
|
|
||||||
}
|
|
||||||
if (velocity > maxBoatSpeed) {
|
|
||||||
velocity = maxBoatSpeed; // Prevent the boats from exceeding top speed
|
|
||||||
}
|
|
||||||
|
|
||||||
} else { // Deceleration
|
|
||||||
|
|
||||||
if (velocity > 0d) {
|
|
||||||
if (maxBoatSpeed != 0d) {
|
|
||||||
velocity -= maxBoatSpeed / 600;
|
|
||||||
} else {
|
|
||||||
velocity -= velocity / 100;
|
|
||||||
}
|
|
||||||
if (velocity < 0) {
|
|
||||||
velocity = 0d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
runAutoPilot();
|
|
||||||
|
|
||||||
//UPDATE BOAT LOCATION
|
|
||||||
lastLocation = location;
|
lastLocation = location;
|
||||||
location = GeoUtility.getGeoCoordinate(location, heading, velocity * secondsElapsed);
|
location = GeoUtility.getGeoCoordinate(location, heading, currentVelocity * secondsElapsed);
|
||||||
|
|
||||||
//CHECK FOR MARK ROUNDING
|
|
||||||
if (!finishedRace) {
|
|
||||||
checkForLegProgression();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: 3/08/17 wmu16 - Implement line cross check here
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -162,197 +128,6 @@ public class Yacht extends Observable {
|
|||||||
super.addObserver(o);
|
super.addObserver(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendMarkRoundingMessage() {
|
|
||||||
CompoundMark currentMark = GameState.getMarkOrder().getCurrentMark(currentMarkSeqID);
|
|
||||||
MarkType markType = (currentMark.isGate()) ? MarkType.GATE : MarkType.ROUNDING_MARK;
|
|
||||||
|
|
||||||
// TODO: 13/8/17 figure out the rounding side, rounded mark source ID and boat status.
|
|
||||||
Message markRoundingMessage = new MarkRoundingMessage(0, 0,
|
|
||||||
sourceId, RoundingBoatStatus.RACING, closestCurrentMark.getRoundingSide(), markType,
|
|
||||||
closestCurrentMark.getSourceID());
|
|
||||||
setChanged();
|
|
||||||
notifyObservers(markRoundingMessage);
|
|
||||||
logMarkRounding(currentMark);
|
|
||||||
|
|
||||||
hasPassedLine = false;
|
|
||||||
hasEnteredRoundingZone = false;
|
|
||||||
hasPassedThroughGate = false;
|
|
||||||
currentMarkSeqID++;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logMarkRounding(CompoundMark currentMark) {
|
|
||||||
logger.debug(
|
|
||||||
String.format("Sending Mark Rounding Message:\n"
|
|
||||||
+ "AckNumber %d\n"
|
|
||||||
+ "RaceID %d\n"
|
|
||||||
+ "BoatSourceID %d\n"
|
|
||||||
+ "BoatStatus %s\n"
|
|
||||||
+ "Rounding Side %s\n"
|
|
||||||
+ "MarkSeqID %d",
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
sourceId,
|
|
||||||
RoundingBoatStatus.RACING.name(),
|
|
||||||
closestCurrentMark.getRoundingSide().getName(),
|
|
||||||
currentMark.getSubMark(1).getSourceID()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the distance to the next mark (closest of the two if a gate mark). For purposes of
|
|
||||||
* mark rounding
|
|
||||||
*
|
|
||||||
* @return A distance in metres. Returns -1 if there is no next mark
|
|
||||||
* @throws IndexOutOfBoundsException If the next mark is null (ie the last mark in the race)
|
|
||||||
* Check first using {@link seng302.model.mark.MarkOrder#isLastMark(Integer)}
|
|
||||||
*/
|
|
||||||
public Double calcDistanceToCurrentMark() throws IndexOutOfBoundsException {
|
|
||||||
CompoundMark nextMark = GameState.getMarkOrder().getCurrentMark(currentMarkSeqID);
|
|
||||||
|
|
||||||
if (nextMark.isGate()) {
|
|
||||||
Mark sub1 = nextMark.getSubMark(1);
|
|
||||||
Mark sub2 = nextMark.getSubMark(2);
|
|
||||||
Double distance1 = GeoUtility.getDistance(location, sub1);
|
|
||||||
Double distance2 = GeoUtility.getDistance(location, sub2);
|
|
||||||
if (distance1 < distance2) {
|
|
||||||
closestCurrentMark = sub1;
|
|
||||||
return distance1;
|
|
||||||
} else {
|
|
||||||
closestCurrentMark = sub2;
|
|
||||||
return distance2;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
closestCurrentMark = nextMark.getSubMark(1);
|
|
||||||
return GeoUtility.getDistance(location, nextMark.getSubMark(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 4 Different cases of progression in the race 1 - Passing the start line 2 - Passing any
|
|
||||||
* in-race Gate 3 - Passing any in-race Mark 4 - Passing the finish line
|
|
||||||
*/
|
|
||||||
private void checkForLegProgression() {
|
|
||||||
CompoundMark currentMark = GameState.getMarkOrder().getCurrentMark(currentMarkSeqID);
|
|
||||||
if (currentMarkSeqID == 0) {
|
|
||||||
checkStartLineCrossing(currentMark);
|
|
||||||
} else if (GameState.getMarkOrder().isLastMark(currentMarkSeqID)) {
|
|
||||||
checkFinishLineCrossing(currentMark);
|
|
||||||
} else if (currentMark.isGate()) {
|
|
||||||
checkGateRounding(currentMark);
|
|
||||||
} else {
|
|
||||||
checkMarkRounding(currentMark);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If we pass the start line gate in the correct direction, progress
|
|
||||||
*
|
|
||||||
* @param currentMark The current gate
|
|
||||||
*/
|
|
||||||
private void checkStartLineCrossing(CompoundMark currentMark) {
|
|
||||||
Mark mark1 = currentMark.getSubMark(1);
|
|
||||||
Mark mark2 = currentMark.getSubMark(2);
|
|
||||||
CompoundMark nextMark = GameState.getMarkOrder().getNextMark(currentMarkSeqID);
|
|
||||||
|
|
||||||
Integer crossedLine = GeoUtility.checkCrossedLine(mark1, mark2, lastLocation, location);
|
|
||||||
if (crossedLine > 0) {
|
|
||||||
Boolean isClockwiseCross = GeoUtility.isClockwise(mark1, mark2, nextMark.getMidPoint());
|
|
||||||
if (crossedLine == 2 && isClockwiseCross || crossedLine == 1 && !isClockwiseCross) {
|
|
||||||
closestCurrentMark = mark1;
|
|
||||||
sendMarkRoundingMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This algorithm checks for mark rounding. And increments the currentMarSeqID number attribute
|
|
||||||
* of the yacht if so. A visual representation of this algorithm can be seen on the Wiki under
|
|
||||||
* 'mark passing algorithm'
|
|
||||||
*/
|
|
||||||
private void checkMarkRounding(CompoundMark currentMark) {
|
|
||||||
distanceToCurrentMark = calcDistanceToCurrentMark();
|
|
||||||
GeoPoint nextPoint = GameState.getMarkOrder().getNextMark(currentMarkSeqID).getMidPoint();
|
|
||||||
GeoPoint prevPoint = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID)
|
|
||||||
.getMidPoint();
|
|
||||||
GeoPoint midPoint = GeoUtility.getDirtyMidPoint(nextPoint, prevPoint);
|
|
||||||
|
|
||||||
//1 TEST FOR ENTERING THE ROUNDING DISTANCE
|
|
||||||
if (distanceToCurrentMark < ROUNDING_DISTANCE) {
|
|
||||||
hasEnteredRoundingZone = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//In case current mark is a gate, loop through all marks just in case
|
|
||||||
for (Mark thisCurrentMark : currentMark.getMarks()) {
|
|
||||||
if (GeoUtility.isPointInTriangle(lastLocation, location, midPoint, thisCurrentMark)) {
|
|
||||||
hasPassedLine = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasPassedLine && hasEnteredRoundingZone) {
|
|
||||||
sendMarkRoundingMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a gate line has been crossed and in the correct direction
|
|
||||||
*
|
|
||||||
* @param currentMark The current gate
|
|
||||||
*/
|
|
||||||
private void checkGateRounding(CompoundMark currentMark) {
|
|
||||||
Mark mark1 = currentMark.getSubMark(1);
|
|
||||||
Mark mark2 = currentMark.getSubMark(2);
|
|
||||||
CompoundMark prevMark = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID);
|
|
||||||
CompoundMark nextMark = GameState.getMarkOrder().getNextMark(currentMarkSeqID);
|
|
||||||
|
|
||||||
Integer crossedLine = GeoUtility.checkCrossedLine(mark1, mark2, lastLocation, location);
|
|
||||||
|
|
||||||
//We have crossed the line
|
|
||||||
if (crossedLine > 0) {
|
|
||||||
Boolean isClockwiseCross = GeoUtility.isClockwise(mark1, mark2, prevMark.getMidPoint());
|
|
||||||
|
|
||||||
//Check we cross the line in the correct direction
|
|
||||||
if (crossedLine == 1 && isClockwiseCross || crossedLine == 2 && !isClockwiseCross) {
|
|
||||||
hasPassedThroughGate = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Boolean prevMarkSide = GeoUtility.isClockwise(mark1, mark2, prevMark.getMidPoint());
|
|
||||||
Boolean nextMarkSide = GeoUtility.isClockwise(mark1, mark2, nextMark.getMidPoint());
|
|
||||||
|
|
||||||
if (hasPassedThroughGate) {
|
|
||||||
//Check if we need to round this gate after passing through
|
|
||||||
if (prevMarkSide == nextMarkSide) {
|
|
||||||
checkMarkRounding(currentMark);
|
|
||||||
} else {
|
|
||||||
sendMarkRoundingMessage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If we pass the finish gate in the correct direction
|
|
||||||
*
|
|
||||||
* @param currentMark The current gate
|
|
||||||
*/
|
|
||||||
private void checkFinishLineCrossing(CompoundMark currentMark) {
|
|
||||||
Mark mark1 = currentMark.getSubMark(1);
|
|
||||||
Mark mark2 = currentMark.getSubMark(2);
|
|
||||||
CompoundMark prevMark = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID);
|
|
||||||
|
|
||||||
Integer crossedLine = GeoUtility.checkCrossedLine(mark1, mark2, lastLocation, location);
|
|
||||||
if (crossedLine > 0) {
|
|
||||||
Boolean isClockwiseCross = GeoUtility.isClockwise(mark1, mark2, prevMark.getMidPoint());
|
|
||||||
if (crossedLine == 1 && isClockwiseCross || crossedLine == 2 && !isClockwiseCross) {
|
|
||||||
closestCurrentMark = mark1;
|
|
||||||
sendMarkRoundingMessage();
|
|
||||||
finishedRace = true;
|
|
||||||
logger.debug(sourceId + " finished");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adjusts the heading of the boat by a given amount, while recording the boats last heading.
|
* Adjusts the heading of the boat by a given amount, while recording the boats last heading.
|
||||||
@@ -603,7 +378,7 @@ public class Yacht extends Observable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public double getVelocityMMS() {
|
public double getVelocityMMS() {
|
||||||
return velocity;
|
return currentVelocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlyLongProperty timeTillNextProperty() {
|
public ReadOnlyLongProperty timeTillNextProperty() {
|
||||||
@@ -611,7 +386,7 @@ public class Yacht extends Observable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Double getVelocityKnots() {
|
public Double getVelocityKnots() {
|
||||||
return velocity / 1000 * 1.943844492; // TODO: 26/07/17 cir27 - remove magic number
|
return currentVelocity / 1000 * 1.943844492; // TODO: 26/07/17 cir27 - remove magic number
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getTimeTillNext() {
|
public Long getTimeTillNext() {
|
||||||
@@ -685,23 +460,82 @@ public class Yacht extends Observable {
|
|||||||
this.colour = colour;
|
this.colour = colour;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setIsFinished(Boolean isFinished) {
|
||||||
public Double getVelocity() {
|
finishedRace = isFinished;
|
||||||
return velocity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVelocity(Double velocity) {
|
public Boolean getFinishedRace() {
|
||||||
this.velocity = velocity;
|
return finishedRace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getCurrentVelocity() {
|
||||||
|
return currentVelocity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentVelocity(Double currentVelocity) {
|
||||||
|
this.currentVelocity = currentVelocity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getCurrentMarkSeqID() {
|
||||||
|
return currentMarkSeqID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeoPoint getLastLocation() {
|
||||||
|
return lastLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mark getClosestCurrentMark() {
|
||||||
|
return closestCurrentMark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClosestCurrentMark(Mark closestCurrentMark) {
|
||||||
|
this.closestCurrentMark = closestCurrentMark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasEnteredRoundingZone(Boolean hasEnteredRoundingZone) {
|
||||||
|
this.hasEnteredRoundingZone = hasEnteredRoundingZone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasPassedLine(Boolean hasPassedLine) {
|
||||||
|
this.hasPassedLine = hasPassedLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasPassedThroughGate(Boolean hasPassedThroughGate) {
|
||||||
|
this.hasPassedThroughGate = hasPassedThroughGate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incrementMarkSeqID() {
|
||||||
|
currentMarkSeqID++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean hasEnteredRoundingZone() {
|
||||||
|
return hasEnteredRoundingZone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean hasPassedThroughGate() {
|
||||||
|
return hasPassedThroughGate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean hasPassedLine() {
|
||||||
|
return hasPassedLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Double getDistanceToCurrentMark() {
|
public Double getDistanceToCurrentMark() {
|
||||||
return distanceToCurrentMark;
|
return distanceToCurrentMark;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Double getCurrentMaxVelocity() {
|
||||||
|
return currentMaxVelocity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentMaxVelocity(Double currentMaxVelocity) {
|
||||||
|
this.currentMaxVelocity = currentMaxVelocity;
|
||||||
|
}
|
||||||
|
|
||||||
public void updateLocation(double lat, double lng, double heading, double velocity) {
|
public void updateLocation(double lat, double lng, double heading, double velocity) {
|
||||||
setLocation(lat, lng);
|
setLocation(lat, lng);
|
||||||
this.heading = heading;
|
this.heading = heading;
|
||||||
this.velocity = velocity;
|
this.currentVelocity = velocity;
|
||||||
updateVelocityProperty(velocity);
|
updateVelocityProperty(velocity);
|
||||||
for (YachtLocationListener yll : locationListeners) {
|
for (YachtLocationListener yll : locationListeners) {
|
||||||
yll.notifyLocation(this, lat, lng, heading, velocity);
|
yll.notifyLocation(this, lat, lng, heading, velocity);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import seng302.model.GeoPoint;
|
|||||||
public class GeoUtility {
|
public class GeoUtility {
|
||||||
|
|
||||||
private static double EARTH_RADIUS = 6378.137;
|
private static double EARTH_RADIUS = 6378.137;
|
||||||
|
private static Double MS_TO_KNOTS = 1.943844492;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the euclidean distance between two markers on the canvas using xy coordinates
|
* Calculates the euclidean distance between two markers on the canvas using xy coordinates
|
||||||
@@ -234,4 +235,20 @@ public class GeoUtility {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param boatSpeedInKnots Speed in knots
|
||||||
|
* @return The Boat speed in millimeters per second
|
||||||
|
*/
|
||||||
|
public static Double knotsToMMS(Double boatSpeedInKnots) {
|
||||||
|
return boatSpeedInKnots / MS_TO_KNOTS * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param boatSpeedInMMS Speed in millimeters per second
|
||||||
|
* @return The Boat speed in knots
|
||||||
|
*/
|
||||||
|
public static Double mmsToKnots(Double boatSpeedInMMS) {
|
||||||
|
return boatSpeedInMMS / 1000 * MS_TO_KNOTS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,37 +4,37 @@
|
|||||||
<team>
|
<team>
|
||||||
<name>Oracle Team USA</name>
|
<name>Oracle Team USA</name>
|
||||||
<alias>USA</alias>
|
<alias>USA</alias>
|
||||||
<velocity>0.0</velocity>
|
<currentVelocity>0.0</currentVelocity>
|
||||||
<id>102</id>
|
<id>102</id>
|
||||||
</team>
|
</team>
|
||||||
<team>
|
<team>
|
||||||
<name>Artemis Racing</name>
|
<name>Artemis Racing</name>
|
||||||
<alias>ART</alias>
|
<alias>ART</alias>
|
||||||
<velocity>0.0</velocity>
|
<currentVelocity>0.0</currentVelocity>
|
||||||
<id>101</id>
|
<id>101</id>
|
||||||
</team>
|
</team>
|
||||||
<team>
|
<team>
|
||||||
<name>Emirates Team New Zealand</name>
|
<name>Emirates Team New Zealand</name>
|
||||||
<alias>NZL</alias>
|
<alias>NZL</alias>
|
||||||
<velocity>0.0</velocity>
|
<currentVelocity>0.0</currentVelocity>
|
||||||
<id>103</id>
|
<id>103</id>
|
||||||
</team>
|
</team>
|
||||||
<team>
|
<team>
|
||||||
<name>Land Rover BAR</name>
|
<name>Land Rover BAR</name>
|
||||||
<alias>BAR</alias>
|
<alias>BAR</alias>
|
||||||
<velocity>0.0</velocity>
|
<currentVelocity>0.0</currentVelocity>
|
||||||
<id>104</id>
|
<id>104</id>
|
||||||
</team>
|
</team>
|
||||||
<team>
|
<team>
|
||||||
<name>SoftBank Team Japan</name>
|
<name>SoftBank Team Japan</name>
|
||||||
<alias>JAP</alias>
|
<alias>JAP</alias>
|
||||||
<velocity>0.0</velocity>
|
<currentVelocity>0.0</currentVelocity>
|
||||||
<id>105</id>
|
<id>105</id>
|
||||||
</team>
|
</team>
|
||||||
<team>
|
<team>
|
||||||
<name>Groupama Team France</name>
|
<name>Groupama Team France</name>
|
||||||
<alias>FRC</alias>
|
<alias>FRC</alias>
|
||||||
<velocity>0.0</velocity>
|
<currentVelocity>0.0</currentVelocity>
|
||||||
<id>106</id>
|
<id>106</id>
|
||||||
</team>
|
</team>
|
||||||
</teams>
|
</teams>
|
||||||
@@ -177,4 +177,16 @@ public class GeoUtilityTest {
|
|||||||
assertEquals(57.6709285, result.getLat(), result.getLat() * toleranceRate);
|
assertEquals(57.6709285, result.getLat(), result.getLat() * toleranceRate);
|
||||||
assertEquals(11.836164, result.getLng(), result.getLng() * toleranceRate);
|
assertEquals(11.836164, result.getLng(), result.getLng() * toleranceRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testKnotsToMMS() {
|
||||||
|
Double result = GeoUtility.knotsToMMS(1.94384);
|
||||||
|
assertEquals(1000, result, result * toleranceRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMMSToKnots() {
|
||||||
|
Double result = GeoUtility.mmsToKnots(1000.0);
|
||||||
|
assertEquals(1.94384, result, result * toleranceRate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user