From 7f0329dda6ac175d5e08adc825ae8d41761cea88 Mon Sep 17 00:00:00 2001 From: William Muir Date: Mon, 7 Aug 2017 00:23:54 +1200 Subject: [PATCH 01/11] WIP: Implemented basic mark rounding algorithm. Removed RacePosition class. Instead marks are just grabbed from the mark order class when necessary. No marks are stored as an attribute in the yacht class but the 'currentMarkSeqID' which is used to get current, and surrounding marks. Works for all marks in between but not including starting and finishing gate as no angle can be made with them. Still to work out how to implement this #story[1124] --- .../java/seng302/gameServer/GameState.java | 13 ++- src/main/java/seng302/model/Yacht.java | 91 ++++++++++++++----- .../java/seng302/model/mark/MarkOrder.java | 64 +++++++------ .../java/seng302/model/mark/RacePosition.java | 55 ----------- src/test/java/seng302/model/YachtTest.java | 18 ++-- .../java/seng302/models/MarkOrderTest.java | 66 ++++++-------- 6 files changed, 148 insertions(+), 159 deletions(-) delete mode 100644 src/main/java/seng302/model/mark/RacePosition.java diff --git a/src/main/java/seng302/gameServer/GameState.java b/src/main/java/seng302/gameServer/GameState.java index ea8c1004..913f4f37 100644 --- a/src/main/java/seng302/gameServer/GameState.java +++ b/src/main/java/seng302/gameServer/GameState.java @@ -4,9 +4,12 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import seng302.gameServer.server.messages.BoatActionType; import seng302.model.Player; import seng302.model.Yacht; +import seng302.model.mark.MarkOrder; /** * A Static class to hold information about the current state of the game (model) @@ -14,6 +17,8 @@ import seng302.model.Yacht; */ public class GameState implements Runnable { + private Logger logger = LoggerFactory.getLogger(MarkOrder.class); + private static Integer STATE_UPDATES_PER_SECOND = 60; private static Long previousUpdateTime; @@ -25,6 +30,7 @@ public class GameState implements Runnable { private static Map yachts; private static Boolean isRaceStarted; private static GameStages currentStage; + private static MarkOrder markOrder; private static long startTime; private static Map playerStringMap = new HashMap<>(); @@ -53,8 +59,9 @@ public class GameState implements Runnable { //set this when game stage changes to prerace previousUpdateTime = System.currentTimeMillis(); yachts = new HashMap<>(); + markOrder = new MarkOrder(); //This could be instantiated at some point with a select map? - new Thread(this).start(); + new Thread(this).start(); //Run the auto updates on the game state } public static String getHostIpAddress() { @@ -100,6 +107,10 @@ public class GameState implements Runnable { GameState.currentStage = currentStage; } + public static MarkOrder getMarkOrder() { + return markOrder; + } + public static long getStartTime(){ return startTime; } diff --git a/src/main/java/seng302/model/Yacht.java b/src/main/java/seng302/model/Yacht.java index d7cd1f9e..1c9e7ec6 100644 --- a/src/main/java/seng302/model/Yacht.java +++ b/src/main/java/seng302/model/Yacht.java @@ -28,7 +28,7 @@ public class Yacht { void notifyLocation(Yacht yacht, double lat, double lon, double heading, double velocity); } - private static final Double ROUNDING_DISTANCE = 15d; // TODO: 3/08/17 wmu16 - Look into this value further + private static final Double ROUNDING_DISTANCE = 50d; // TODO: 3/08/17 wmu16 - Look into this value further //BOTH AFAIK private String boatType; @@ -39,11 +39,10 @@ public class Yacht { private String country; private Long estimateTimeAtFinish; - private Long lastMark; + private Integer currentMarkSeqID = 1; private Long markRoundTime; private Double distanceToNextMark; private Long timeTillNext; - private CompoundMark nextMark; private Double heading; private Integer legNumber = 0; @@ -58,7 +57,6 @@ public class Yacht { private GeoPoint lastLocation; //For purposes of mark rounding calculations private Boolean hasEnteredRoundingZone; //The distance that the boat must be from the mark to round private Boolean hasPassedFirstLine; //The line extrapolated from the next mark to the current mark - private Boolean hasPassedSecondLine; //The line extrapolated from the last mark to the current mark //CLIENT SIDE private List locationListeners = new ArrayList<>(); @@ -85,7 +83,6 @@ public class Yacht { this.hasEnteredRoundingZone = false; this.hasPassedFirstLine = false; - this.hasPassedSecondLine = false; } /** @@ -125,24 +122,24 @@ public class Yacht { location = GeoUtility.getGeoCoordinate(location, heading, velocity * secondsElapsed); //CHECK FOR MARK ROUNDING - distanceToNextMark = calcDistanceToNextMark(); - if (distanceToNextMark < ROUNDING_DISTANCE) { - hasEnteredRoundingZone = true; - } + //Algorithm wont currently work on last gate and start gate + checkForMarkRounding(); // TODO: 3/08/17 wmu16 - Implement line cross check here } /** - * Calculates the distance to the next mark (closest of the two if a gate mark). - * + * 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 calcDistanceToNextMark() { - if (nextMark == null) { - return -1d; - } else if (nextMark.isGate()) { + public Double calcDistanceToNextMark() 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); @@ -153,6 +150,62 @@ public class Yacht { } } + + /** + * This algorithm checks for mark rounding. And increments the currentMarSeqID number attribute + * of the yacht if so. + * The algorithm works by using the last mark, the current mark, the next mark and the change in + * boats location, like so: + * -Condition 1: + * The boat has entered the mark rounding distance + * -Condition 2: + * The boat has passed the line extending from the last mark to the current mark + * -Condition 3: + * The boat has passed the line extending from the next mark to the current mark + * + * A more visual representation of this algorithm can be seen on the Wiki under + * 'mark passing algorithm' + */ + private void checkForMarkRounding() { + if (!GameState.getMarkOrder().isLastMark(currentMarkSeqID) && currentMarkSeqID != 0) { + distanceToNextMark = calcDistanceToNextMark(); +// System.out.println("distanceToNextMark = " + distanceToNextMark); + CompoundMark nextMark = GameState.getMarkOrder().getNextMark(currentMarkSeqID); + CompoundMark currentMark = GameState.getMarkOrder().getCurrentMark(currentMarkSeqID); + CompoundMark prevMark = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID); + + //1 TEST FOR ENTERING THE ROUDNING DISTANCE + if (distanceToNextMark < ROUNDING_DISTANCE) { + hasEnteredRoundingZone = true; +// System.out.println("Entered rounding zone!"); + } + + //If the current mark is a gate mark we need to check both its marks for rounding, thus + //we loop + for (Mark thisCurrentMark : currentMark.getMarks()) { + + //2 TEST FOR CROSSING NEXT - CURRENT LINE FIRST + if (GeoUtility.isPointInTriangle(lastLocation, location, nextMark.getMarks().get(0), + thisCurrentMark)) { + hasPassedFirstLine = true; + System.out.println("Passed first line!"); + } + + //3 TEST FOR CROSSING PREV - CURRENT LINE SECOND + if (GeoUtility.isPointInTriangle(lastLocation, location, prevMark.getMarks().get(0), + thisCurrentMark)) { + if (hasPassedFirstLine && hasEnteredRoundingZone) { + currentMarkSeqID++; + hasPassedFirstLine = false; + hasEnteredRoundingZone = false; + System.out.println("SUCCESFUL ROUDNING!"); + break; + } + } + } + } + } + public void adjustHeading(Double amount) { Double newVal = heading + amount; lastHeading = heading; @@ -357,14 +410,6 @@ public class Yacht { this.lastMarkRounded = lastMarkRounded; } - public void setNextMark(CompoundMark nextMark) { - this.nextMark = nextMark; - } - - public CompoundMark getNextMark(){ - return nextMark; - } - public GeoPoint getLocation() { return location; } diff --git a/src/main/java/seng302/model/mark/MarkOrder.java b/src/main/java/seng302/model/mark/MarkOrder.java index ca2b4356..141d5c6d 100644 --- a/src/main/java/seng302/model/mark/MarkOrder.java +++ b/src/main/java/seng302/model/mark/MarkOrder.java @@ -24,7 +24,7 @@ import java.util.Map; * Class to hold the order of the marks in the race. */ public class MarkOrder { - private List raceMarkOrder; + private List raceMarkOrder; private Logger logger = LoggerFactory.getLogger(MarkOrder.class); public MarkOrder(){ @@ -35,7 +35,7 @@ public class MarkOrder { * @return An ordered list of marks in the race * OR null if the mark order could not be loaded */ - public List getMarkOrder(){ + public List getMarkOrder(){ if (raceMarkOrder == null){ logger.warn("Race order accessed but not instantiated"); return null; @@ -45,26 +45,35 @@ public class MarkOrder { } /** - * Returns the mark in the race after the previous mark - * @param position The current race position - * @return the next race position - * OR null if there is no position + * @param seqID The seqID of the current mark the boat is heading to + * @return A Boolean indicating if this coming mark is the last one (finish line) */ - public RacePosition getNextPosition(RacePosition position){ - Mark previousMark = position.getNextMark(); - Mark nextMark; + public Boolean isLastMark(Integer seqID) { + return seqID == raceMarkOrder.size() - 1; + } - if (position.getPositionIndex() + 1 >= raceMarkOrder.size() - 1){ - RacePosition nextRacePosition = new RacePosition(raceMarkOrder.size() - 1, null, previousMark); - nextRacePosition.setFinishingLeg(); + /** + * @param currentSeqID The seqID of the current mark the boat is heading to + * @return The mark last passed + * @throws IndexOutOfBoundsException if there is no next mark. + * Check seqID != 0 first + */ + public CompoundMark getPreviousMark(Integer currentSeqID) throws IndexOutOfBoundsException{ + return raceMarkOrder.get(currentSeqID - 1); + } - return nextRacePosition; - } + public CompoundMark getCurrentMark(Integer currentSeqID) { + return raceMarkOrder.get(currentSeqID); + } - Integer nextPositionIndex = position.getPositionIndex() + 1; - RacePosition nextRacePosition = new RacePosition(nextPositionIndex, raceMarkOrder.get(nextPositionIndex), previousMark); - - return nextRacePosition; + /** + * @param currentSeqID The seqID of the current mark the boat is heading to + * @return The mark following the mark that the boat is heading to + * @throws IndexOutOfBoundsException if there is no next mark. + * Check using {@link #isLastMark(Integer)} + */ + public CompoundMark getNextMark(Integer currentSeqID) throws IndexOutOfBoundsException{ + return raceMarkOrder.get(currentSeqID + 1); } /** @@ -72,7 +81,7 @@ public class MarkOrder { * @param xml An AC35 RaceXML * @return An ordered list of marks in the race */ - private List loadRaceOrderFromXML(String xml){ + private List loadRaceOrderFromXML(String xml){ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db; @@ -92,11 +101,11 @@ public class MarkOrder { logger.debug("Loaded RaceXML for mark order"); List corners = data.getMarkSequence(); Map marks = data.getCompoundMarks(); - List course = new ArrayList<>(); + List course = new ArrayList<>(); for (Corner corner : corners){ CompoundMark compoundMark = marks.get(corner.getCompoundMarkID()); - course.add(compoundMark.getMarks().get(0)); + course.add(compoundMark); } return course; @@ -105,17 +114,6 @@ public class MarkOrder { return null; } - /** - * @return The first position in the race - */ - public RacePosition getFirstPosition(){ - if (raceMarkOrder.size() > 0){ - return new RacePosition(-1, raceMarkOrder.get(0), null); - } - - return null; - } - /** * Load the raceXML and mark order */ @@ -132,4 +130,4 @@ public class MarkOrder { } raceMarkOrder = loadRaceOrderFromXML(raceXML); } -} +} \ No newline at end of file diff --git a/src/main/java/seng302/model/mark/RacePosition.java b/src/main/java/seng302/model/mark/RacePosition.java deleted file mode 100644 index fc160b10..00000000 --- a/src/main/java/seng302/model/mark/RacePosition.java +++ /dev/null @@ -1,55 +0,0 @@ -package seng302.model.mark; - -/** - * Represents a boats position between two marks - */ -public class RacePosition { - private Integer positionIndex; - private Mark nextMark; - private Mark previousMark; - private Boolean isFinishingLeg; - - public RacePosition(Integer positionIndex, Mark nextMark, Mark previousMark){ - this.positionIndex = positionIndex; - this.nextMark = nextMark; - this.previousMark = previousMark; - isFinishingLeg = false; - } - - /** - * @return The position of the boat (0...number of marks in race - 1) - */ - public Integer getPositionIndex(){ - return positionIndex; - } - - /** - * @return The mark the boat is heading to - * will return NULL if this is the finishing legg - */ - public Mark getNextMark(){ - return nextMark; - } - - /** - * @return The mark the boat is heading away from, - * Will return NULL if this is the starting leg - */ - public Mark getPreviousMark(){ - return previousMark; - } - - /** - * Sets a flag that this is the last leg in the race - */ - public void setFinishingLeg(){ - isFinishingLeg = true; - } - - /** - * @return true if this is the last leg in the race - */ - public boolean getIsFinishingLeg() { - return isFinishingLeg; - } -} diff --git a/src/test/java/seng302/model/YachtTest.java b/src/test/java/seng302/model/YachtTest.java index 7937b43c..739c8a28 100644 --- a/src/test/java/seng302/model/YachtTest.java +++ b/src/test/java/seng302/model/YachtTest.java @@ -38,17 +38,19 @@ public class YachtTest { Mark subMark2 = new Mark("H", 57.670822, 11.843392, 0); compoundMark.addSubMarks(subMark1, subMark2); - yacht.setNextMark(compoundMark); } - @Test - public void testDistanceToNextMark() { - Double actual, expected; - actual = yacht.calcDistanceToNextMark(); - expected = 927d; - assertEquals(expected, actual, expected * toleranceRatio); - } + //This will no longer work as we cant set the next mark any more as we no longer hold it in + //yacht class as an attribute + +// @Test +// public void testDistanceToNextMark() { +// Double actual, expected; +// actual = yacht.calcDistanceToNextMark(); +// expected = 927d; +// assertEquals(expected, actual, expected * toleranceRatio); +// } } \ No newline at end of file diff --git a/src/test/java/seng302/models/MarkOrderTest.java b/src/test/java/seng302/models/MarkOrderTest.java index 8db06ec8..42531563 100644 --- a/src/test/java/seng302/models/MarkOrderTest.java +++ b/src/test/java/seng302/models/MarkOrderTest.java @@ -3,19 +3,22 @@ package seng302.models; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import seng302.model.mark.CompoundMark; import seng302.model.mark.Mark; import seng302.model.mark.MarkOrder; -import seng302.model.mark.RacePosition; import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertTrue; public class MarkOrderTest { private static MarkOrder markOrder; + private static Integer currentSeqID; @BeforeClass public static void setup(){ markOrder = new MarkOrder(); + currentSeqID = 0; } /** @@ -26,54 +29,39 @@ public class MarkOrderTest { assertTrue(markOrder != null); } - /** - * Test if .getNextMark() returns null if it is called with the final mark in the race - */ + @Test - public void testNextMarkAtEnd(){ - // There are no marks in the XML, therefore this can't be tested - if (markOrder.getMarkOrder().size() == 0){ - return; - } + public void testIsLastMark() { + currentSeqID = 0; + assertFalse(markOrder.isLastMark(currentSeqID)); - Mark lastMark = markOrder.getMarkOrder().get(markOrder.getMarkOrder().size() - 1); - Integer lastIndex = markOrder.getMarkOrder().size() - 1; - - RacePosition lastRacePosition = new RacePosition(lastIndex, lastMark, null); - - assertEquals(null, markOrder.getNextPosition(lastRacePosition).getNextMark()); + currentSeqID = markOrder.getMarkOrder().size() - 1; + assertTrue(markOrder.isLastMark(currentSeqID)); } - /** - * Test if .getNextMark() method returns the next mark in the race - */ @Test - public void testNextMark(){ - // There are not enough marks for this to be tested - if (markOrder.getMarkOrder().size() < 2){ - return; - } + public void testGetNextMark() { + currentSeqID = 4; + CompoundMark nextMark = markOrder.getMarkOrder().get(4 + 1); + assertEquals(nextMark, markOrder.getNextMark(currentSeqID)); - RacePosition firstRacePos = new RacePosition(0, markOrder.getMarkOrder().get(0), null); - - assertEquals(markOrder.getMarkOrder().get(1).getName(), markOrder.getNextPosition(firstRacePos).getNextMark().getName()); + currentSeqID = 3; + nextMark = markOrder.getMarkOrder().get(3 + 1); + assertEquals(nextMark, markOrder.getNextMark(currentSeqID)); } - /** - * Test if a whole race can be completed - */ @Test - public void testMarkSequence(){ - RacePosition current = markOrder.getFirstPosition(); + public void testGetCurrentMark() { + currentSeqID = 0; + CompoundMark currentMark = markOrder.getMarkOrder().get(0); + assertEquals(currentMark, markOrder.getCurrentMark(0)); + } - while (!current.getIsFinishingLeg()){ - - current = markOrder.getNextPosition(current); - - if (current.getIsFinishingLeg()){ - assertEquals(null, current.getNextMark()); - } - } + @Test + public void testGetPreviousMark() { + currentSeqID = 1; + CompoundMark prevMark = markOrder.getMarkOrder().get(0); + assertEquals(prevMark, markOrder.getPreviousMark(currentSeqID)); } @AfterClass From 4375b7325755d8a26b5678280fa358193278caea Mon Sep 17 00:00:00 2001 From: William Muir Date: Mon, 7 Aug 2017 17:28:12 +1200 Subject: [PATCH 02/11] Implemented algorithm for checking if boat passes through a mark. Mark rounding works for whole course (WITH BUGS) Still some gate logic to work out. Moved gate function to GeoUtil class tags: #story[1124] #pair[hyi25, wmu16] --- src/main/java/seng302/model/Yacht.java | 30 ++++++++++++++--- .../java/seng302/utilities/GeoUtility.java | 32 +++++++++++++++++++ .../java/seng302/visualiser/GameClient.java | 1 - .../controllers/RaceViewController.java | 2 +- .../seng302/utilities/GeoUtilityTest.java | 20 ++++++++++++ 5 files changed, 78 insertions(+), 7 deletions(-) diff --git a/src/main/java/seng302/model/Yacht.java b/src/main/java/seng302/model/Yacht.java index 1c9e7ec6..34bbb4f0 100644 --- a/src/main/java/seng302/model/Yacht.java +++ b/src/main/java/seng302/model/Yacht.java @@ -10,6 +10,8 @@ import javafx.beans.property.ReadOnlyDoubleWrapper; import javafx.beans.property.ReadOnlyLongProperty; import javafx.beans.property.ReadOnlyLongWrapper; import javafx.scene.paint.Color; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import seng302.gameServer.GameState; import seng302.model.mark.CompoundMark; import seng302.model.mark.Mark; @@ -28,8 +30,11 @@ public class Yacht { void notifyLocation(Yacht yacht, double lat, double lon, double heading, double velocity); } + 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 private String boatType; private Integer sourceId; @@ -39,7 +44,7 @@ public class Yacht { private String country; private Long estimateTimeAtFinish; - private Integer currentMarkSeqID = 1; + private Integer currentMarkSeqID = 0; private Long markRoundTime; private Double distanceToNextMark; private Long timeTillNext; @@ -119,6 +124,7 @@ public class Yacht { } //UPDATE BOAT LOCATION + lastLocation = location; location = GeoUtility.getGeoCoordinate(location, heading, velocity * secondsElapsed); //CHECK FOR MARK ROUNDING @@ -167,11 +173,22 @@ public class Yacht { * 'mark passing algorithm' */ private void checkForMarkRounding() { - if (!GameState.getMarkOrder().isLastMark(currentMarkSeqID) && currentMarkSeqID != 0) { + CompoundMark currentMark = GameState.getMarkOrder().getCurrentMark(currentMarkSeqID); + + if (GameState.getMarkOrder().isLastMark(currentMarkSeqID) || currentMarkSeqID == 0) { + if (GeoUtility.checkCrossedLine(currentMark.getSubMark(1), + currentMark.getSubMark(2), lastLocation, location)) { + System.out.println( + "(" + currentMarkSeqID + ") Passed gate: " + currentMark.getMarks().get(0) + .getName() + + " ID(" + currentMark.getId() + ")"); + currentMarkSeqID++; + } + } else { + //ALL OTHER MARKS distanceToNextMark = calcDistanceToNextMark(); // System.out.println("distanceToNextMark = " + distanceToNextMark); CompoundMark nextMark = GameState.getMarkOrder().getNextMark(currentMarkSeqID); - CompoundMark currentMark = GameState.getMarkOrder().getCurrentMark(currentMarkSeqID); CompoundMark prevMark = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID); //1 TEST FOR ENTERING THE ROUDNING DISTANCE @@ -188,7 +205,6 @@ public class Yacht { if (GeoUtility.isPointInTriangle(lastLocation, location, nextMark.getMarks().get(0), thisCurrentMark)) { hasPassedFirstLine = true; - System.out.println("Passed first line!"); } //3 TEST FOR CROSSING PREV - CURRENT LINE SECOND @@ -198,7 +214,10 @@ public class Yacht { currentMarkSeqID++; hasPassedFirstLine = false; hasEnteredRoundingZone = false; - System.out.println("SUCCESFUL ROUDNING!"); + System.out.println( + "(" + currentMarkSeqID + ") Passed mark: " + currentMark.getMarks() + .get(0).getName() + + " ID(" + currentMark.getId() + ")"); break; } } @@ -206,6 +225,7 @@ public class Yacht { } } + public void adjustHeading(Double amount) { Double newVal = heading + amount; lastHeading = heading; diff --git a/src/main/java/seng302/utilities/GeoUtility.java b/src/main/java/seng302/utilities/GeoUtility.java index 9aa61b9d..81acebe6 100644 --- a/src/main/java/seng302/utilities/GeoUtility.java +++ b/src/main/java/seng302/utilities/GeoUtility.java @@ -1,7 +1,10 @@ package seng302.utilities; import javafx.geometry.Point2D; +import seng302.gameServer.GameState; import seng302.model.GeoPoint; +import seng302.model.mark.CompoundMark; +import seng302.model.mark.Mark; public class GeoUtility { @@ -126,6 +129,35 @@ public class GeoUtility { } + /** + * Checks if a point passes across a line, either direction + * See the wiki Mark Rounding algorithm for more info + * + * @param mark1 One mark of the line + * @param mark2 The second mark of the line + * @param lastLocation The last location of the point crossing this line + * @param location The current location of the point crossing this line + * @return True if crossed since last location --> current location, false otherwise + */ + public static Boolean checkCrossedLine(GeoPoint mark1, GeoPoint mark2, GeoPoint lastLocation, + GeoPoint location) { + //START GATE OR FINISH GATE + Double alpha = GeoUtility.getBearing(mark1, lastLocation); + Double beta = GeoUtility.getBearing(mark1, mark2); + Double theta = GeoUtility.getBearing(mark1, location); + alpha = (alpha > 180) ? 360 - alpha : alpha; + beta = (beta > 180) ? 360 - beta : beta; + theta = (theta > 180) ? 360 - theta : theta; + + if (alpha < beta && theta > beta) { + if (!GeoUtility.isPointInTriangle(mark1, lastLocation, location, mark2)) { + return true; + } + } + return false; + } + + /** * Given a point and a vector (angle and vector length) Will create a new point, that vector * away from the origin point diff --git a/src/main/java/seng302/visualiser/GameClient.java b/src/main/java/seng302/visualiser/GameClient.java index 915eef37..b00a6cda 100644 --- a/src/main/java/seng302/visualiser/GameClient.java +++ b/src/main/java/seng302/visualiser/GameClient.java @@ -174,7 +174,6 @@ public class GameClient { break; case BOAT_XML: - System.out.println("GOT SUM BOATS YAY :)"); allBoatsMap = XMLParser.parseBoats( StreamParser.extractXmlMessage(packet) ); diff --git a/src/main/java/seng302/visualiser/controllers/RaceViewController.java b/src/main/java/seng302/visualiser/controllers/RaceViewController.java index a6df4f61..2a166081 100644 --- a/src/main/java/seng302/visualiser/controllers/RaceViewController.java +++ b/src/main/java/seng302/visualiser/controllers/RaceViewController.java @@ -312,7 +312,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel updateRaceTime(); updateWindDirection(); updateOrder(); - updateSparkLine(); +// updateSparkLine(); } }, 0, 1000); } diff --git a/src/test/java/seng302/utilities/GeoUtilityTest.java b/src/test/java/seng302/utilities/GeoUtilityTest.java index 8382bb8c..4bd1e128 100644 --- a/src/test/java/seng302/utilities/GeoUtilityTest.java +++ b/src/test/java/seng302/utilities/GeoUtilityTest.java @@ -9,9 +9,11 @@ import javafx.geometry.Point2D; import org.junit.Before; import org.junit.Test; import seng302.model.GeoPoint; +import seng302.model.mark.CompoundMark; import seng302.utilities.GeoUtility; /** + * http://www.geoplaner.com/ For plotting geo points for visualisation * To test methods in GeoUtility. * Use this site to calculate distances * https://rechneronline.de/geo-coordinates/#distance @@ -150,4 +152,22 @@ public class GeoUtilityTest { assertFalse(GeoUtility.isPointInTriangle(v1, v2, v3, p2)); } + + + @Test + public void testCheckCrossedGate() { + GeoPoint mark1 = new GeoPoint(37.40937, -122.62233); + GeoPoint mark2 = new GeoPoint(37.40938, -122.62154); + GeoPoint location1 = new GeoPoint(37.40964, -122.62196); + GeoPoint location2 = new GeoPoint(37.40910, -122.62189); + GeoPoint location3 = new GeoPoint(37.40949, -122.62202); + GeoPoint location4 = new GeoPoint(34.40955, -122.62176); + GeoPoint location5 = new GeoPoint(37.40927, -122.62152); + GeoPoint location6 = new GeoPoint(34.40933, -122.62163); + + assertTrue(GeoUtility.checkCrossedLine(mark1, mark2, location1, location2)); + assertFalse(GeoUtility.checkCrossedLine(mark1, mark2, location4, location3)); + assertFalse(GeoUtility.checkCrossedLine(mark1, mark2, location1, location3)); + assertFalse(GeoUtility.checkCrossedLine(mark1, mark2, location5, location6)); + } } \ No newline at end of file From ed0a7833749e77371b8928bc195ad4d4d17658ab Mon Sep 17 00:00:00 2001 From: Haoming Yin Date: Tue, 8 Aug 2017 10:42:36 +1200 Subject: [PATCH 03/11] Fixed the bug that boats could round over a gate but still "across" it. Added unit test to ensure the algorithm works. tags: #story[1124] --- src/main/java/seng302/model/Yacht.java | 2 +- .../java/seng302/utilities/GeoUtility.java | 58 ++++++++++--------- .../seng302/utilities/GeoUtilityTest.java | 20 +++---- 3 files changed, 43 insertions(+), 37 deletions(-) diff --git a/src/main/java/seng302/model/Yacht.java b/src/main/java/seng302/model/Yacht.java index 34bbb4f0..f0adde0a 100644 --- a/src/main/java/seng302/model/Yacht.java +++ b/src/main/java/seng302/model/Yacht.java @@ -177,7 +177,7 @@ public class Yacht { if (GameState.getMarkOrder().isLastMark(currentMarkSeqID) || currentMarkSeqID == 0) { if (GeoUtility.checkCrossedLine(currentMark.getSubMark(1), - currentMark.getSubMark(2), lastLocation, location)) { + currentMark.getSubMark(2), lastLocation, location) > 0) { System.out.println( "(" + currentMarkSeqID + ") Passed gate: " + currentMark.getMarks().get(0) .getName() diff --git a/src/main/java/seng302/utilities/GeoUtility.java b/src/main/java/seng302/utilities/GeoUtility.java index 81acebe6..ab87c3be 100644 --- a/src/main/java/seng302/utilities/GeoUtility.java +++ b/src/main/java/seng302/utilities/GeoUtility.java @@ -1,10 +1,7 @@ package seng302.utilities; import javafx.geometry.Point2D; -import seng302.gameServer.GameState; import seng302.model.GeoPoint; -import seng302.model.mark.CompoundMark; -import seng302.model.mark.Mark; public class GeoUtility { @@ -96,7 +93,6 @@ public class GeoUtility { return new GeoPoint(Math.toDegrees(endLat), Math.toDegrees(endLng)); } - /** * Performs the line function on two points of a line and a test point to test which side of the * line that point is on. If the return value is return 1, then the point is on one side of the @@ -128,36 +124,32 @@ public class GeoUtility { } } - /** - * Checks if a point passes across a line, either direction - * See the wiki Mark Rounding algorithm for more info + * Checks if the line formed by lastLocation and location doesn't intersect the line segment + * formed by mark1 and mark2 See the wiki Mark Rounding algorithm for more info * * @param mark1 One mark of the line * @param mark2 The second mark of the line * @param lastLocation The last location of the point crossing this line * @param location The current location of the point crossing this line - * @return True if crossed since last location --> current location, false otherwise + * @return 0 if two line segment doesn't intersect, otherwise 1 if they intersect and + * lastLocation is on RHS of the line segment (mark1 -> mark2) or 2 if lastLocation on LHS of + * the line segment (mark1 -> mark2) */ - public static Boolean checkCrossedLine(GeoPoint mark1, GeoPoint mark2, GeoPoint lastLocation, + public static Integer checkCrossedLine(GeoPoint mark1, GeoPoint mark2, GeoPoint lastLocation, GeoPoint location) { - //START GATE OR FINISH GATE - Double alpha = GeoUtility.getBearing(mark1, lastLocation); - Double beta = GeoUtility.getBearing(mark1, mark2); - Double theta = GeoUtility.getBearing(mark1, location); - alpha = (alpha > 180) ? 360 - alpha : alpha; - beta = (beta > 180) ? 360 - beta : beta; - theta = (theta > 180) ? 360 - theta : theta; + boolean enteredDirection = isClockwise(mark1, mark2, lastLocation); + boolean exitedDirection = isClockwise(mark1, mark2, location); + if (enteredDirection != exitedDirection) { + if (!isPointInTriangle(mark1, lastLocation, location, mark2) + && !isPointInTriangle(mark2, lastLocation, location, mark1)) { - if (alpha < beta && theta > beta) { - if (!GeoUtility.isPointInTriangle(mark1, lastLocation, location, mark2)) { - return true; + return enteredDirection ? 1 : 2; } } - return false; + return 0; } - /** * Given a point and a vector (angle and vector length) Will create a new point, that vector * away from the origin point @@ -187,10 +179,24 @@ public class GeoUtility { * @param bearing2 the bearing of v2 * @return the difference of bearing from v1 to v2 */ - private static double getBearingDiff(double bearing1, double bearing2) { + private static Double getBearingDiff(double bearing1, double bearing2) { return ((360 - bearing1) + bearing2) % 360; } + /** + * Check if a geo point ins on the right hand side of the line segment, which + * formed by two geo points v1 -> v2. (Algorithm: point is clockwise to the + * line if the bearing difference is less than 180 deg.) + * + * @param v1 one end of the line segment + * @param v2 another end of the line segment + * @param point the point to be tested + * @return true if the point is on the RHS of the line + */ + private static Boolean isClockwise(GeoPoint v1, GeoPoint v2, GeoPoint point) { + return getBearingDiff(getBearing(v1, v2), getBearing(v1, point)) < 180; + } + /** * Given three geo points to form a triangle, the method returns true if the fourth point is * inside the triangle @@ -201,15 +207,15 @@ public class GeoUtility { * @param point the point to be tested * @return true if the fourth point is inside the triangle */ - public static boolean isPointInTriangle(GeoPoint v1, GeoPoint v2, GeoPoint v3, GeoPoint point) { + public static Boolean isPointInTriangle(GeoPoint v1, GeoPoint v2, GeoPoint v3, GeoPoint point) { // true, if diff of bearing from (v1->v2) to (v1->p) is less than 180 deg - boolean sideFlag = getBearingDiff(getBearing(v1, v2), getBearing(v1, point)) < 180; + boolean isCW = isClockwise(v1, v2, point); - if ((getBearingDiff(getBearing(v2, v3), getBearing(v2, point)) < 180) != sideFlag) { + if (isClockwise(v2, v3, point) != isCW) { return false; } - if ((getBearingDiff(getBearing(v3, v1), getBearing(v3, point)) < 180) != sideFlag) { + if (isClockwise(v3, v1, point) != isCW) { return false; } diff --git a/src/test/java/seng302/utilities/GeoUtilityTest.java b/src/test/java/seng302/utilities/GeoUtilityTest.java index 4bd1e128..109b1723 100644 --- a/src/test/java/seng302/utilities/GeoUtilityTest.java +++ b/src/test/java/seng302/utilities/GeoUtilityTest.java @@ -6,11 +6,8 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import javafx.geometry.Point2D; -import org.junit.Before; import org.junit.Test; import seng302.model.GeoPoint; -import seng302.model.mark.CompoundMark; -import seng302.utilities.GeoUtility; /** * http://www.geoplaner.com/ For plotting geo points for visualisation @@ -161,13 +158,16 @@ public class GeoUtilityTest { GeoPoint location1 = new GeoPoint(37.40964, -122.62196); GeoPoint location2 = new GeoPoint(37.40910, -122.62189); GeoPoint location3 = new GeoPoint(37.40949, -122.62202); - GeoPoint location4 = new GeoPoint(34.40955, -122.62176); - GeoPoint location5 = new GeoPoint(37.40927, -122.62152); - GeoPoint location6 = new GeoPoint(34.40933, -122.62163); + GeoPoint location4 = new GeoPoint(37.40927, -122.62152); - assertTrue(GeoUtility.checkCrossedLine(mark1, mark2, location1, location2)); - assertFalse(GeoUtility.checkCrossedLine(mark1, mark2, location4, location3)); - assertFalse(GeoUtility.checkCrossedLine(mark1, mark2, location1, location3)); - assertFalse(GeoUtility.checkCrossedLine(mark1, mark2, location5, location6)); + // M1 -> M3 enters from CCW side + assertTrue(GeoUtility.checkCrossedLine(mark1, mark2, location1, location2) == 2); + // M1 -> M3 doesn't across + assertFalse(GeoUtility.checkCrossedLine(mark1, mark2, location1, location3) > 0); + // M2 -> M3 enters from CW side + assertTrue(GeoUtility.checkCrossedLine(mark1, mark2, location2, location3) == 1); + // order changes intersect direction + assertTrue(GeoUtility.checkCrossedLine(mark2, mark1, location2, location3) == 2); + assertTrue(GeoUtility.checkCrossedLine(mark1, mark2, location3, location2) == 2); } } \ No newline at end of file From b0e7dddaf38d3aac0242743ebebb4e225af4e1b1 Mon Sep 17 00:00:00 2001 From: William Muir Date: Tue, 8 Aug 2017 15:58:13 +1200 Subject: [PATCH 04/11] Fixed gate passing algorithm boats now must pass through the correct way. This works for start in-race and finish gates Refactored yacht algorithm code for better readability Logging function added or seeing mark roundings occur tags: #story[1124] #pair[hyi25, wmu16] --- src/main/java/seng302/model/Yacht.java | 177 +++++++++++++----- .../java/seng302/utilities/GeoUtility.java | 2 +- 2 files changed, 131 insertions(+), 48 deletions(-) diff --git a/src/main/java/seng302/model/Yacht.java b/src/main/java/seng302/model/Yacht.java index f0adde0a..c87b0b8f 100644 --- a/src/main/java/seng302/model/Yacht.java +++ b/src/main/java/seng302/model/Yacht.java @@ -58,10 +58,12 @@ public class Yacht { private GeoPoint location; private Integer boatStatus; private Double velocity; + //MARK ROUNDING INFO private GeoPoint lastLocation; //For purposes of mark rounding calculations private Boolean hasEnteredRoundingZone; //The distance that the boat must be from the mark to round private Boolean hasPassedFirstLine; //The line extrapolated from the next mark to the current mark + private Boolean hasPassedThroughGate; //CLIENT SIDE private List locationListeners = new ArrayList<>(); @@ -88,6 +90,7 @@ public class Yacht { this.hasEnteredRoundingZone = false; this.hasPassedFirstLine = false; + this.hasPassedThroughGate = false; } /** @@ -129,7 +132,7 @@ public class Yacht { //CHECK FOR MARK ROUNDING //Algorithm wont currently work on last gate and start gate - checkForMarkRounding(); + checkForLegProgression(); // TODO: 3/08/17 wmu16 - Implement line cross check here } @@ -157,70 +160,139 @@ public class Yacht { } + /** + * 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) { + Integer crossedLine = GeoUtility.checkCrossedLine(currentMark.getSubMark(1), + currentMark.getSubMark(2), lastLocation, location); + if (crossedLine > 0) { + CompoundMark nextMark = GameState.getMarkOrder().getNextMark(currentMarkSeqID); + Boolean isClockwiseCross = GeoUtility.isClockwise(currentMark.getSubMark(1), + currentMark.getSubMark(2), + nextMark.getSubMark(1)); + if (crossedLine == 2 && isClockwiseCross || crossedLine == 1 && !isClockwiseCross) { + logMarkRounding(currentMark); + currentMarkSeqID++; + } + } + } + + /** * This algorithm checks for mark rounding. And increments the currentMarSeqID number attribute * of the yacht if so. * The algorithm works by using the last mark, the current mark, the next mark and the change in * boats location, like so: * -Condition 1: - * The boat has entered the mark rounding distance + * The boat has entered the mark rounding distance * -Condition 2: - * The boat has passed the line extending from the last mark to the current mark + * The boat has passed the line extending from the last mark to the current mark * -Condition 3: - * The boat has passed the line extending from the next mark to the current mark + * The boat has passed the line extending from the next mark to the current mark * * A more visual representation of this algorithm can be seen on the Wiki under * 'mark passing algorithm' */ - private void checkForMarkRounding() { - CompoundMark currentMark = GameState.getMarkOrder().getCurrentMark(currentMarkSeqID); + private void checkMarkRounding(CompoundMark currentMark) { + distanceToNextMark = calcDistanceToNextMark(); + CompoundMark nextMark = GameState.getMarkOrder().getNextMark(currentMarkSeqID); + CompoundMark prevMark = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID); - if (GameState.getMarkOrder().isLastMark(currentMarkSeqID) || currentMarkSeqID == 0) { - if (GeoUtility.checkCrossedLine(currentMark.getSubMark(1), - currentMark.getSubMark(2), lastLocation, location) > 0) { - System.out.println( - "(" + currentMarkSeqID + ") Passed gate: " + currentMark.getMarks().get(0) - .getName() - + " ID(" + currentMark.getId() + ")"); - currentMarkSeqID++; + //1 TEST FOR ENTERING THE ROUNDING DISTANCE + if (distanceToNextMark < ROUNDING_DISTANCE) { + hasEnteredRoundingZone = true; + } + + //If the current mark is a gate mark we need to check both its marks for rounding, thus + //we loop + for (Mark thisCurrentMark : currentMark.getMarks()) { + //2 TEST FOR CROSSING NEXT - CURRENT LINE FIRST + if (GeoUtility + .isPointInTriangle(lastLocation, location, nextMark.getMarks().get(0), + thisCurrentMark)) { + hasPassedFirstLine = true; } - } else { - //ALL OTHER MARKS - distanceToNextMark = calcDistanceToNextMark(); -// System.out.println("distanceToNextMark = " + distanceToNextMark); - CompoundMark nextMark = GameState.getMarkOrder().getNextMark(currentMarkSeqID); + //3 TEST FOR CROSSING PREV - CURRENT LINE SECOND + if (GeoUtility + .isPointInTriangle(lastLocation, location, prevMark.getMarks().get(0), + thisCurrentMark)) { + if (hasPassedFirstLine && hasEnteredRoundingZone) { + currentMarkSeqID++; + hasPassedFirstLine = false; + hasEnteredRoundingZone = false; + hasPassedThroughGate = false; + logMarkRounding(currentMark); + break; + } + } + } + } + + + /** + * Checks if a gate line has been crossed and in the correct direction + * + * @param currentMark The current gate + */ + private void checkGateRounding(CompoundMark currentMark) { + Integer crossedLine = GeoUtility.checkCrossedLine(currentMark.getSubMark(1), + currentMark.getSubMark(2), lastLocation, location); + + //We have crossed the line + if (crossedLine > 0) { CompoundMark prevMark = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID); - - //1 TEST FOR ENTERING THE ROUDNING DISTANCE - if (distanceToNextMark < ROUNDING_DISTANCE) { - hasEnteredRoundingZone = true; -// System.out.println("Entered rounding zone!"); + Boolean isClockwiseCross = GeoUtility.isClockwise(currentMark.getSubMark(1), + currentMark.getSubMark(2), + prevMark.getSubMark(1)); + if (crossedLine == 1 && isClockwiseCross || crossedLine == 2 && !isClockwiseCross) { + hasPassedThroughGate = true; } + } - //If the current mark is a gate mark we need to check both its marks for rounding, thus - //we loop - for (Mark thisCurrentMark : currentMark.getMarks()) { + if (hasPassedThroughGate) { + checkMarkRounding(currentMark); + } + } - //2 TEST FOR CROSSING NEXT - CURRENT LINE FIRST - if (GeoUtility.isPointInTriangle(lastLocation, location, nextMark.getMarks().get(0), - thisCurrentMark)) { - hasPassedFirstLine = true; - } - - //3 TEST FOR CROSSING PREV - CURRENT LINE SECOND - if (GeoUtility.isPointInTriangle(lastLocation, location, prevMark.getMarks().get(0), - thisCurrentMark)) { - if (hasPassedFirstLine && hasEnteredRoundingZone) { - currentMarkSeqID++; - hasPassedFirstLine = false; - hasEnteredRoundingZone = false; - System.out.println( - "(" + currentMarkSeqID + ") Passed mark: " + currentMark.getMarks() - .get(0).getName() - + " ID(" + currentMark.getId() + ")"); - break; - } - } + /** + * If we pass the finish gate in the correct direction // TODO: 8/08/17 wmu16 - do something + * + * @param currentMark The current gate + */ + private void checkFinishLineCrossing(CompoundMark currentMark) { + Integer crossedLine = GeoUtility.checkCrossedLine(currentMark.getSubMark(1), + currentMark.getSubMark(2), lastLocation, location); + if (crossedLine > 0) { + CompoundMark previousMark = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID); + Boolean isClockwiseCross = GeoUtility.isClockwise(currentMark.getSubMark(1), + currentMark.getSubMark(2), + previousMark.getSubMark(1)); + if (crossedLine == 1 && isClockwiseCross || crossedLine == 2 && !isClockwiseCross) { + logMarkRounding(currentMark); + // TODO: 8/08/17 wmu16 - Do something! } } } @@ -508,6 +580,17 @@ public class Yacht { } } + private void logMarkRounding(CompoundMark currentMark) { + String typeString = "mark"; + if (currentMark.isGate()) { + typeString = "gate"; + } + System.out.println( + "(" + currentMarkSeqID + ") Passed " + typeString + ": " + currentMark.getMarks().get(0) + .getName() + + " ID(" + currentMark.getId() + ")"); + } + public void addLocationListener (YachtLocationListener listener) { locationListeners.add(listener); } diff --git a/src/main/java/seng302/utilities/GeoUtility.java b/src/main/java/seng302/utilities/GeoUtility.java index ab87c3be..260f5b34 100644 --- a/src/main/java/seng302/utilities/GeoUtility.java +++ b/src/main/java/seng302/utilities/GeoUtility.java @@ -193,7 +193,7 @@ public class GeoUtility { * @param point the point to be tested * @return true if the point is on the RHS of the line */ - private static Boolean isClockwise(GeoPoint v1, GeoPoint v2, GeoPoint point) { + public static Boolean isClockwise(GeoPoint v1, GeoPoint v2, GeoPoint point) { return getBearingDiff(getBearing(v1, v2), getBearing(v1, point)) < 180; } From a545e9dbc32e42562bf4132048820bec7d684759 Mon Sep 17 00:00:00 2001 From: William Muir Date: Tue, 8 Aug 2017 16:14:13 +1200 Subject: [PATCH 05/11] Removing '>' characters from docstrings to fix build Note: This is probably a XML style guid thing and is stupid. Can probably fix this --- src/main/java/seng302/utilities/GeoUtility.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/seng302/utilities/GeoUtility.java b/src/main/java/seng302/utilities/GeoUtility.java index 260f5b34..5d96f33e 100644 --- a/src/main/java/seng302/utilities/GeoUtility.java +++ b/src/main/java/seng302/utilities/GeoUtility.java @@ -133,8 +133,8 @@ public class GeoUtility { * @param lastLocation The last location of the point crossing this line * @param location The current location of the point crossing this line * @return 0 if two line segment doesn't intersect, otherwise 1 if they intersect and - * lastLocation is on RHS of the line segment (mark1 -> mark2) or 2 if lastLocation on LHS of - * the line segment (mark1 -> mark2) + * lastLocation is on RHS of the line segment (mark1 to mark2) or 2 if lastLocation on LHS of + * the line segment (mark1 to mark2) */ public static Integer checkCrossedLine(GeoPoint mark1, GeoPoint mark2, GeoPoint lastLocation, GeoPoint location) { @@ -185,7 +185,7 @@ public class GeoUtility { /** * Check if a geo point ins on the right hand side of the line segment, which - * formed by two geo points v1 -> v2. (Algorithm: point is clockwise to the + * formed by two geo points v1 to v2. (Algorithm: point is clockwise to the * line if the bearing difference is less than 180 deg.) * * @param v1 one end of the line segment @@ -208,7 +208,7 @@ public class GeoUtility { * @return true if the fourth point is inside the triangle */ public static Boolean isPointInTriangle(GeoPoint v1, GeoPoint v2, GeoPoint v3, GeoPoint point) { - // true, if diff of bearing from (v1->v2) to (v1->p) is less than 180 deg + // true, if diff of bearing from (v1 to v2) to (v1 to p) is less than 180 deg boolean isCW = isClockwise(v1, v2, point); if (isClockwise(v2, v3, point) != isCW) { From 249ad9e5c04c00bd796cb39375fa9b21be6ce1b1 Mon Sep 17 00:00:00 2001 From: William Muir Date: Thu, 10 Aug 2017 12:02:19 +1200 Subject: [PATCH 06/11] Fixed Mark rounding Algorithm Algorithm now knows when a player has to round a gate or just pass right through #story[1124] #pair[wmu16, hyi25] --- src/main/java/seng302/model/Yacht.java | 64 +++++++++++++------ .../server_config/xml_templates/race.ftlh | 7 -- src/test/java/seng302/model/YachtTest.java | 2 +- 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/src/main/java/seng302/model/Yacht.java b/src/main/java/seng302/model/Yacht.java index c87b0b8f..95d117fe 100644 --- a/src/main/java/seng302/model/Yacht.java +++ b/src/main/java/seng302/model/Yacht.java @@ -32,6 +32,8 @@ public class Yacht { private Logger logger = LoggerFactory.getLogger(Yacht.class); + + private static final Integer SPEED_MULTIPLIER = 4; private static final Double ROUNDING_DISTANCE = 50d; // TODO: 3/08/17 wmu16 - Look into this value further @@ -46,7 +48,7 @@ public class Yacht { private Long estimateTimeAtFinish; private Integer currentMarkSeqID = 0; private Long markRoundTime; - private Double distanceToNextMark; + private Double distanceToCurrentMark; private Long timeTillNext; private Double heading; private Integer legNumber = 0; @@ -63,7 +65,9 @@ public class Yacht { private GeoPoint lastLocation; //For purposes of mark rounding calculations private Boolean hasEnteredRoundingZone; //The distance that the boat must be from the mark to round private Boolean hasPassedFirstLine; //The line extrapolated from the next mark to the current mark + private Boolean hasPassedSecondLine; private Boolean hasPassedThroughGate; + private Boolean finishedRace; //CLIENT SIDE private List locationListeners = new ArrayList<>(); @@ -90,7 +94,9 @@ public class Yacht { this.hasEnteredRoundingZone = false; this.hasPassedFirstLine = false; + this.hasPassedSecondLine = false; this.hasPassedThroughGate = false; + this.finishedRace = false; } /** @@ -102,7 +108,7 @@ public class Yacht { Double windSpeedKnots = GameState.getWindSpeedKnots(); Double trueWindAngle = Math.abs(GameState.getWindDirection() - heading); Double boatSpeedInKnots = PolarTable.getBoatSpeed(windSpeedKnots, trueWindAngle); - Double maxBoatSpeed = boatSpeedInKnots / 1.943844492 * 1000; + Double maxBoatSpeed = boatSpeedInKnots / 1.943844492 * 1000 * SPEED_MULTIPLIER; if (sailIn && velocity <= maxBoatSpeed && maxBoatSpeed != 0d) { if (velocity < maxBoatSpeed) { @@ -131,8 +137,9 @@ public class Yacht { location = GeoUtility.getGeoCoordinate(location, heading, velocity * secondsElapsed); //CHECK FOR MARK ROUNDING - //Algorithm wont currently work on last gate and start gate - checkForLegProgression(); + if (!finishedRace) { + checkForLegProgression(); + } // TODO: 3/08/17 wmu16 - Implement line cross check here } @@ -145,7 +152,7 @@ public class Yacht { * @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 calcDistanceToNextMark() throws IndexOutOfBoundsException { + public Double calcDistanceToCurrentMark() throws IndexOutOfBoundsException { CompoundMark nextMark = GameState.getMarkOrder().getCurrentMark(currentMarkSeqID); if (nextMark.isGate()) { @@ -194,8 +201,8 @@ public class Yacht { currentMark.getSubMark(2), nextMark.getSubMark(1)); if (crossedLine == 2 && isClockwiseCross || crossedLine == 1 && !isClockwiseCross) { - logMarkRounding(currentMark); currentMarkSeqID++; + logMarkRounding(currentMark); } } } @@ -217,12 +224,12 @@ public class Yacht { * 'mark passing algorithm' */ private void checkMarkRounding(CompoundMark currentMark) { - distanceToNextMark = calcDistanceToNextMark(); + distanceToCurrentMark = calcDistanceToCurrentMark(); CompoundMark nextMark = GameState.getMarkOrder().getNextMark(currentMarkSeqID); CompoundMark prevMark = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID); //1 TEST FOR ENTERING THE ROUNDING DISTANCE - if (distanceToNextMark < ROUNDING_DISTANCE) { + if (distanceToCurrentMark < ROUNDING_DISTANCE) { hasEnteredRoundingZone = true; } @@ -239,16 +246,18 @@ public class Yacht { if (GeoUtility .isPointInTriangle(lastLocation, location, prevMark.getMarks().get(0), thisCurrentMark)) { - if (hasPassedFirstLine && hasEnteredRoundingZone) { - currentMarkSeqID++; - hasPassedFirstLine = false; - hasEnteredRoundingZone = false; - hasPassedThroughGate = false; - logMarkRounding(currentMark); - break; - } + hasPassedSecondLine = true; } } + + if (hasPassedSecondLine && hasPassedFirstLine && hasEnteredRoundingZone) { + currentMarkSeqID++; + hasPassedFirstLine = false; + hasPassedSecondLine = false; + hasEnteredRoundingZone = false; + hasPassedThroughGate = false; + logMarkRounding(currentMark); + } } @@ -272,8 +281,21 @@ public class Yacht { } } + Boolean prevMarkSide = GeoUtility.isClockwise(currentMark.getSubMark(1), + currentMark.getSubMark(2), + GameState.getMarkOrder().getPreviousMark(currentMarkSeqID).getSubMark(1)); + + Boolean nextMarkSide = GeoUtility.isClockwise(currentMark.getSubMark(1), + currentMark.getSubMark(2), + GameState.getMarkOrder().getNextMark(currentMarkSeqID).getSubMark(1)); + if (hasPassedThroughGate) { - checkMarkRounding(currentMark); + if (prevMarkSide == nextMarkSide) { + checkMarkRounding(currentMark); + } else { + currentMarkSeqID++; + logMarkRounding(currentMark); + } } } @@ -291,7 +313,10 @@ public class Yacht { currentMark.getSubMark(2), previousMark.getSubMark(1)); if (crossedLine == 1 && isClockwiseCross || crossedLine == 2 && !isClockwiseCross) { + currentMarkSeqID++; + finishedRace = true; logMarkRounding(currentMark); + System.out.println("YAY YOU FINISHED!"); // TODO: 8/08/17 wmu16 - Do something! } } @@ -376,7 +401,6 @@ public class Yacht { } private void turnTowardsHeading(Double newHeading) { - System.out.println(newHeading); if (heading < 90 && newHeading > 270) { adjustHeading(-TURN_STEP); } else { @@ -566,8 +590,8 @@ public class Yacht { this.velocity = velocity; } - public Double getDistanceToNextMark() { - return distanceToNextMark; + public Double getDistanceToCurrentMark() { + return distanceToCurrentMark; } public void updateLocation(double lat, double lng, double heading, double velocity) { diff --git a/src/main/resources/server_config/xml_templates/race.ftlh b/src/main/resources/server_config/xml_templates/race.ftlh index e38bf0b7..e8421c5c 100644 --- a/src/main/resources/server_config/xml_templates/race.ftlh +++ b/src/main/resources/server_config/xml_templates/race.ftlh @@ -36,13 +36,6 @@ - - - - - - - diff --git a/src/test/java/seng302/model/YachtTest.java b/src/test/java/seng302/model/YachtTest.java index 739c8a28..78192e70 100644 --- a/src/test/java/seng302/model/YachtTest.java +++ b/src/test/java/seng302/model/YachtTest.java @@ -47,7 +47,7 @@ public class YachtTest { // @Test // public void testDistanceToNextMark() { // Double actual, expected; -// actual = yacht.calcDistanceToNextMark(); +// actual = yacht.calcDistanceToCurrentMark(); // expected = 927d; // assertEquals(expected, actual, expected * toleranceRatio); // } From 87f2f1fe637976e9bfa44ec7e2c78a709aca635a Mon Sep 17 00:00:00 2001 From: William Muir Date: Thu, 10 Aug 2017 12:07:47 +1200 Subject: [PATCH 07/11] Fixed Mark rounding Algorithm Algorithm now knows when a player has to round a gate or just pass right through #story[1124] #pair[wmu16, hyi25] --- src/main/resources/server_config/xml_templates/race.ftlh | 7 +++++++ src/test/java/seng302/models/MarkOrderTest.java | 3 +++ 2 files changed, 10 insertions(+) diff --git a/src/main/resources/server_config/xml_templates/race.ftlh b/src/main/resources/server_config/xml_templates/race.ftlh index e8421c5c..e38bf0b7 100644 --- a/src/main/resources/server_config/xml_templates/race.ftlh +++ b/src/main/resources/server_config/xml_templates/race.ftlh @@ -36,6 +36,13 @@ + + + + + + + diff --git a/src/test/java/seng302/models/MarkOrderTest.java b/src/test/java/seng302/models/MarkOrderTest.java index 42531563..d30f999a 100644 --- a/src/test/java/seng302/models/MarkOrderTest.java +++ b/src/test/java/seng302/models/MarkOrderTest.java @@ -42,6 +42,9 @@ public class MarkOrderTest { @Test public void testGetNextMark() { currentSeqID = 4; + for (CompoundMark mark : markOrder.getMarkOrder()) { + System.out.println(mark.getName()); + } CompoundMark nextMark = markOrder.getMarkOrder().get(4 + 1); assertEquals(nextMark, markOrder.getNextMark(currentSeqID)); From abb15f6edf04e7375acb76acd8bb883427da6488 Mon Sep 17 00:00:00 2001 From: William Muir Date: Thu, 10 Aug 2017 12:08:03 +1200 Subject: [PATCH 08/11] Fixed Mark rounding Algorithm Algorithm now knows when a player has to round a gate or just pass right through #story[1124] #pair[wmu16, hyi25] --- src/test/java/seng302/models/MarkOrderTest.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/test/java/seng302/models/MarkOrderTest.java b/src/test/java/seng302/models/MarkOrderTest.java index d30f999a..42531563 100644 --- a/src/test/java/seng302/models/MarkOrderTest.java +++ b/src/test/java/seng302/models/MarkOrderTest.java @@ -42,9 +42,6 @@ public class MarkOrderTest { @Test public void testGetNextMark() { currentSeqID = 4; - for (CompoundMark mark : markOrder.getMarkOrder()) { - System.out.println(mark.getName()); - } CompoundMark nextMark = markOrder.getMarkOrder().get(4 + 1); assertEquals(nextMark, markOrder.getNextMark(currentSeqID)); From 9c79897e013e07e3a59fa0942e0b6954c00ff0f8 Mon Sep 17 00:00:00 2001 From: William Muir Date: Thu, 10 Aug 2017 13:58:32 +1200 Subject: [PATCH 09/11] Tidied code, added MidPoint to CompoundMark class Compound Mark class is now constructed with a list of marks. A mid point is created on its construction for use in Geo Calculations #story[1124] #pair[wmu16, hyi25] --- .../simulator/parsers/CourseParser.java | 15 +-- src/main/java/seng302/model/Yacht.java | 95 ++++++++----------- .../java/seng302/model/mark/CompoundMark.java | 64 ++++--------- .../java/seng302/utilities/GeoUtility.java | 13 +++ .../java/seng302/utilities/XMLParser.java | 4 +- src/test/java/seng302/model/YachtTest.java | 9 +- 6 files changed, 87 insertions(+), 113 deletions(-) diff --git a/src/main/java/seng302/gameServer/server/simulator/parsers/CourseParser.java b/src/main/java/seng302/gameServer/server/simulator/parsers/CourseParser.java index 66def8a1..e4cbf676 100644 --- a/src/main/java/seng302/gameServer/server/simulator/parsers/CourseParser.java +++ b/src/main/java/seng302/gameServer/server/simulator/parsers/CourseParser.java @@ -79,18 +79,19 @@ public class CourseParser extends FileParser { if (node.getNodeType() == Node.ELEMENT_NODE) { Element e = (Element) node; Integer markID = Integer.valueOf(e.getAttribute("CompoundMarkID")); - String name = e.getAttribute("Name"); - CompoundMark cMark = new CompoundMark(markID, name); NodeList marks = e.getElementsByTagName("Mark"); - for (int i = 0; i < marks.getLength(); i++) { + List subMarks = new ArrayList<>(); + for (int i = 0; i < marks.getLength(); i++) { Mark mark = getMark(marks.item(i)); - if (mark != null) - cMark.addSubMarks(mark); + if (mark != null) { + subMarks.add(mark); + } } - return cMark; - } + + return new CompoundMark(markID, name, subMarks); + } System.out.println("Failed to create compound mark."); return null; } diff --git a/src/main/java/seng302/model/Yacht.java b/src/main/java/seng302/model/Yacht.java index 95d117fe..94f351c5 100644 --- a/src/main/java/seng302/model/Yacht.java +++ b/src/main/java/seng302/model/Yacht.java @@ -64,8 +64,7 @@ public class Yacht { //MARK ROUNDING INFO private GeoPoint lastLocation; //For purposes of mark rounding calculations private Boolean hasEnteredRoundingZone; //The distance that the boat must be from the mark to round - private Boolean hasPassedFirstLine; //The line extrapolated from the next mark to the current mark - private Boolean hasPassedSecondLine; + private Boolean hasPassedLine; private Boolean hasPassedThroughGate; private Boolean finishedRace; @@ -93,8 +92,7 @@ public class Yacht { this.velocity = 0d; //in mms-1 this.hasEnteredRoundingZone = false; - this.hasPassedFirstLine = false; - this.hasPassedSecondLine = false; + this.hasPassedLine = false; this.hasPassedThroughGate = false; this.finishedRace = false; } @@ -193,13 +191,13 @@ public class Yacht { * @param currentMark The current gate */ private void checkStartLineCrossing(CompoundMark currentMark) { - Integer crossedLine = GeoUtility.checkCrossedLine(currentMark.getSubMark(1), - currentMark.getSubMark(2), lastLocation, location); + 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) { - CompoundMark nextMark = GameState.getMarkOrder().getNextMark(currentMarkSeqID); - Boolean isClockwiseCross = GeoUtility.isClockwise(currentMark.getSubMark(1), - currentMark.getSubMark(2), - nextMark.getSubMark(1)); + Boolean isClockwiseCross = GeoUtility.isClockwise(mark1, mark2, nextMark.getMidPoint()); if (crossedLine == 2 && isClockwiseCross || crossedLine == 1 && !isClockwiseCross) { currentMarkSeqID++; logMarkRounding(currentMark); @@ -211,49 +209,31 @@ public class Yacht { /** * This algorithm checks for mark rounding. And increments the currentMarSeqID number attribute * of the yacht if so. - * The algorithm works by using the last mark, the current mark, the next mark and the change in - * boats location, like so: - * -Condition 1: - * The boat has entered the mark rounding distance - * -Condition 2: - * The boat has passed the line extending from the last mark to the current mark - * -Condition 3: - * The boat has passed the line extending from the next mark to the current mark - * - * A more visual representation of this algorithm can be seen on the Wiki under + * A visual representation of this algorithm can be seen on the Wiki under * 'mark passing algorithm' */ private void checkMarkRounding(CompoundMark currentMark) { distanceToCurrentMark = calcDistanceToCurrentMark(); - CompoundMark nextMark = GameState.getMarkOrder().getNextMark(currentMarkSeqID); - CompoundMark prevMark = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID); + 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; } - //If the current mark is a gate mark we need to check both its marks for rounding, thus - //we loop + //In case current mark is a gate, loop through all marks just in case for (Mark thisCurrentMark : currentMark.getMarks()) { - //2 TEST FOR CROSSING NEXT - CURRENT LINE FIRST - if (GeoUtility - .isPointInTriangle(lastLocation, location, nextMark.getMarks().get(0), - thisCurrentMark)) { - hasPassedFirstLine = true; - } - //3 TEST FOR CROSSING PREV - CURRENT LINE SECOND - if (GeoUtility - .isPointInTriangle(lastLocation, location, prevMark.getMarks().get(0), - thisCurrentMark)) { - hasPassedSecondLine = true; + if (GeoUtility.isPointInTriangle(lastLocation, location, midPoint, thisCurrentMark)) { + hasPassedLine = true; } } - if (hasPassedSecondLine && hasPassedFirstLine && hasEnteredRoundingZone) { + if (hasPassedLine && hasEnteredRoundingZone) { currentMarkSeqID++; - hasPassedFirstLine = false; - hasPassedSecondLine = false; + hasPassedLine = false; hasEnteredRoundingZone = false; hasPassedThroughGate = false; logMarkRounding(currentMark); @@ -267,29 +247,28 @@ public class Yacht { * @param currentMark The current gate */ private void checkGateRounding(CompoundMark currentMark) { - Integer crossedLine = GeoUtility.checkCrossedLine(currentMark.getSubMark(1), - currentMark.getSubMark(2), lastLocation, location); + 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) { - CompoundMark prevMark = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID); - Boolean isClockwiseCross = GeoUtility.isClockwise(currentMark.getSubMark(1), - currentMark.getSubMark(2), - prevMark.getSubMark(1)); + 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(currentMark.getSubMark(1), - currentMark.getSubMark(2), - GameState.getMarkOrder().getPreviousMark(currentMarkSeqID).getSubMark(1)); - - Boolean nextMarkSide = GeoUtility.isClockwise(currentMark.getSubMark(1), - currentMark.getSubMark(2), - GameState.getMarkOrder().getNextMark(currentMarkSeqID).getSubMark(1)); + 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 { @@ -300,18 +279,18 @@ public class Yacht { } /** - * If we pass the finish gate in the correct direction // TODO: 8/08/17 wmu16 - do something + * If we pass the finish gate in the correct direction * * @param currentMark The current gate */ private void checkFinishLineCrossing(CompoundMark currentMark) { - Integer crossedLine = GeoUtility.checkCrossedLine(currentMark.getSubMark(1), - currentMark.getSubMark(2), lastLocation, location); + 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) { - CompoundMark previousMark = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID); - Boolean isClockwiseCross = GeoUtility.isClockwise(currentMark.getSubMark(1), - currentMark.getSubMark(2), - previousMark.getSubMark(1)); + Boolean isClockwiseCross = GeoUtility.isClockwise(mark1, mark2, prevMark.getMidPoint()); if (crossedLine == 1 && isClockwiseCross || crossedLine == 2 && !isClockwiseCross) { currentMarkSeqID++; finishedRace = true; diff --git a/src/main/java/seng302/model/mark/CompoundMark.java b/src/main/java/seng302/model/mark/CompoundMark.java index af4cd8a8..a4cc8d0c 100644 --- a/src/main/java/seng302/model/mark/CompoundMark.java +++ b/src/main/java/seng302/model/mark/CompoundMark.java @@ -1,8 +1,9 @@ package seng302.model.mark; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import seng302.model.GeoPoint; +import seng302.utilities.GeoUtility; public class CompoundMark { @@ -10,18 +11,17 @@ public class CompoundMark { private String name; private List marks = new ArrayList<>(); + private GeoPoint midPoint; - public CompoundMark(int markID, String name) { - this.compoundMarkId = markID; + public CompoundMark(int markID, String name, List marks) { + this.compoundMarkId = markID; this.name = name; - } - - public void addSubMarks(Mark... marks) { - this.marks.addAll(Arrays.asList(marks)); - } - - public void addSubMarks(List marks) { - this.marks.addAll(marks); + this.marks.addAll(marks); + if (marks.size() > 1) { + this.midPoint = GeoUtility.getDirtyMidPoint(marks.get(0), marks.get(1)); + } else { + this.midPoint = marks.get(0); + } } /** @@ -68,6 +68,16 @@ public class CompoundMark { } } + /** + * NOTE: This is a 'dirty' mid point as it is simply calculated as an xy point would be. + * NO CHECKING FOR LAT / LNG WRAPPING IS DONE IN CREATION OF THIS MIDPOINT + * + * @return GeoPoint of the midpoint of the two marks, or the one mark if there is only one + */ + public GeoPoint getMidPoint() { + return midPoint; + } + /** * Returns whether or not this CompoundMark is a Gate. It is generally cleaner to program to a * specific singleMark or the list of marks. @@ -87,38 +97,6 @@ public class CompoundMark { return marks; } - -// @Override -// public boolean equals(Object other) { -// if (other == null) { -// return false; -// } -// -// if (!(other instanceof Mark)){ -// return false; -// } -// -// Mark otherMark = (Mark) other; -// -// if (otherMark.getLat() != getLat() || otherMark.getLongitude() != getLongitude()) { -// return false; -// } -// -// if (otherMark.getCompoundMarkID() != getCompoundMarkID()){ -// return false; -// } -// -// if (otherMark.getId() != getId()){ -// return false; -// } -// -// if (!otherMark.getName().equals(name)){ -// return false; -// } -// -// return true; -// } - @Override public int hashCode() { int hash = 0; diff --git a/src/main/java/seng302/utilities/GeoUtility.java b/src/main/java/seng302/utilities/GeoUtility.java index 5d96f33e..f6968601 100644 --- a/src/main/java/seng302/utilities/GeoUtility.java +++ b/src/main/java/seng302/utilities/GeoUtility.java @@ -45,6 +45,19 @@ public class GeoUtility { return (Math.toDegrees(getBearingRad(p1, p2)) + 360.0) % 360.0; } + + /** + * WARNING: this function DOES NOT account for wrapping around on lats / longs etc. + * SO BE CAREFUL IN USING THIS FUNCTION + * + * @param p1 GeoPoint 1 + * @param p2 GeoPoint 2 + * @return GeoPoint midPoint + */ + public static GeoPoint getDirtyMidPoint(GeoPoint p1, GeoPoint p2) { + return new GeoPoint((p1.getLat() + p2.getLat()) / 2, (p1.getLng() + p2.getLng()) / 2); + } + /** * Calculates the angle between to angular co-ordinates on a sphere in radians. * diff --git a/src/main/java/seng302/utilities/XMLParser.java b/src/main/java/seng302/utilities/XMLParser.java index c231535d..c4c4348b 100644 --- a/src/main/java/seng302/utilities/XMLParser.java +++ b/src/main/java/seng302/utilities/XMLParser.java @@ -256,9 +256,9 @@ public class XMLParser { if (cMarkNode.getNodeName().equals("CompoundMark")) { cMark = new CompoundMark( XMLParser.getNodeAttributeInt(cMarkNode, "CompoundMarkID"), - XMLParser.getNodeAttributeString(cMarkNode, "Name") + XMLParser.getNodeAttributeString(cMarkNode, "Name"), + createMarks(cMarkNode) ); - cMark.addSubMarks(createMarks(cMarkNode)); allMarks.add(cMark); } } diff --git a/src/test/java/seng302/model/YachtTest.java b/src/test/java/seng302/model/YachtTest.java index 78192e70..bc3b8c80 100644 --- a/src/test/java/seng302/model/YachtTest.java +++ b/src/test/java/seng302/model/YachtTest.java @@ -2,6 +2,8 @@ package seng302.model; import static org.junit.Assert.*; +import java.util.ArrayList; +import java.util.List; import org.junit.Before; import org.junit.Test; import seng302.model.mark.CompoundMark; @@ -33,11 +35,12 @@ public class YachtTest { yacht.setLocation(57.670333, 11.827833); - compoundMark = new CompoundMark(0, "HaomingsMark"); + List subMarks = new ArrayList<>(); Mark subMark1 = new Mark("H", 57.671524, 11.844495, 0); Mark subMark2 = new Mark("H", 57.670822, 11.843392, 0); - compoundMark.addSubMarks(subMark1, subMark2); - + subMarks.add(subMark1); + subMarks.add(subMark2); + compoundMark = new CompoundMark(0, "HaomingsMark", subMarks); } From 1d7b5271308df390bbb05dcb0513182c5fedcf99 Mon Sep 17 00:00:00 2001 From: William Muir Date: Thu, 10 Aug 2017 16:45:30 +1200 Subject: [PATCH 10/11] Tidied code. Added tests #story[1124] #pair[wmu16, hyi25] --- src/test/java/seng302/model/YachtTest.java | 59 ----------------- .../seng302/model/mark/CompoundMarkTest.java | 66 +++++++++++++++++++ .../seng302/utilities/GeoUtilityTest.java | 7 ++ 3 files changed, 73 insertions(+), 59 deletions(-) delete mode 100644 src/test/java/seng302/model/YachtTest.java create mode 100644 src/test/java/seng302/model/mark/CompoundMarkTest.java diff --git a/src/test/java/seng302/model/YachtTest.java b/src/test/java/seng302/model/YachtTest.java deleted file mode 100644 index bc3b8c80..00000000 --- a/src/test/java/seng302/model/YachtTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package seng302.model; - -import static org.junit.Assert.*; - -import java.util.ArrayList; -import java.util.List; -import org.junit.Before; -import org.junit.Test; -import seng302.model.mark.CompoundMark; -import seng302.model.mark.Mark; - -/** - * Use this link to test geo distances - * http://www.csgnetwork.com/gpsdistcalc.html - * Created by wmu16 on 3/08/17. - */ -public class YachtTest { - - private Yacht yacht; - private CompoundMark compoundMark; - private Double toleranceRatio = 0.01; - private GeoPoint p1 = new GeoPoint(57.670333, 11.827833); - private GeoPoint p2 = new GeoPoint(57.671524, 11.844495); - private GeoPoint p3 = new GeoPoint(57.670822, 11.843392); - private GeoPoint p4 = new GeoPoint(25.694829, 98.392049); - - @Before - public void setup() { - yacht = new Yacht("Yacht", - 0, - "0", - "WillIsCool", - "HaomingIsOk", - "NZL"); - - yacht.setLocation(57.670333, 11.827833); - - List subMarks = new ArrayList<>(); - Mark subMark1 = new Mark("H", 57.671524, 11.844495, 0); - Mark subMark2 = new Mark("H", 57.670822, 11.843392, 0); - subMarks.add(subMark1); - subMarks.add(subMark2); - compoundMark = new CompoundMark(0, "HaomingsMark", subMarks); - } - - - //This will no longer work as we cant set the next mark any more as we no longer hold it in - //yacht class as an attribute - -// @Test -// public void testDistanceToNextMark() { -// Double actual, expected; -// actual = yacht.calcDistanceToCurrentMark(); -// expected = 927d; -// assertEquals(expected, actual, expected * toleranceRatio); -// } - - -} \ No newline at end of file diff --git a/src/test/java/seng302/model/mark/CompoundMarkTest.java b/src/test/java/seng302/model/mark/CompoundMarkTest.java new file mode 100644 index 00000000..55ccebf3 --- /dev/null +++ b/src/test/java/seng302/model/mark/CompoundMarkTest.java @@ -0,0 +1,66 @@ +package seng302.model.mark; + +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import seng302.model.GeoPoint; + +/** + * A class to test the compound mark calss + * Created by wmu16 on 10/08/17. + */ +public class CompoundMarkTest { + + private Mark mark1; + private Mark mark2; + private CompoundMark gateMark; + private CompoundMark singleMark; + + private static Double TOLERANCE_RATIO = 0.01; + + + @Before + public void setUp() throws Exception { + mark1 = new Mark("Mark1", 57.670333, 11.842833, 0); + mark2 = new Mark("Mark2", 57.671524, 11.844495, 1); + + List gateMarks = new ArrayList(); + gateMarks.add(mark1); + gateMarks.add(mark2); + + List singleMarks = new ArrayList(); + singleMarks.add(mark1); + + gateMark = new CompoundMark(0, "Fun Mark", gateMarks); + singleMark = new CompoundMark(1, "Awesome Mark", singleMarks); + } + + + @Test + public void getSubMark() throws Exception { + assertEquals(mark1, gateMark.getSubMark(1)); + assertEquals(mark2, gateMark.getSubMark(2)); + + assertEquals(mark1, singleMark.getSubMark(1)); + } + + @Test + public void getMidPoint() throws Exception { + GeoPoint result = gateMark.getMidPoint(); + assertEquals(57.6709285, result.getLat(), result.getLat() * TOLERANCE_RATIO); + assertEquals(11.843664, result.getLng(), result.getLng() * TOLERANCE_RATIO); + + result = singleMark.getMidPoint(); + assertEquals(result, mark1); + } + + @Test + public void isGate() throws Exception { + assertTrue(gateMark.isGate()); + assertFalse(singleMark.isGate()); + } + +} \ No newline at end of file diff --git a/src/test/java/seng302/utilities/GeoUtilityTest.java b/src/test/java/seng302/utilities/GeoUtilityTest.java index 109b1723..ca2af0e9 100644 --- a/src/test/java/seng302/utilities/GeoUtilityTest.java +++ b/src/test/java/seng302/utilities/GeoUtilityTest.java @@ -170,4 +170,11 @@ public class GeoUtilityTest { assertTrue(GeoUtility.checkCrossedLine(mark2, mark1, location2, location3) == 2); assertTrue(GeoUtility.checkCrossedLine(mark1, mark2, location3, location2) == 2); } + + @Test + public void testDirtyMiddlePoint() { + GeoPoint result = GeoUtility.getDirtyMidPoint(p1, p2); + assertEquals(57.6709285, result.getLat(), result.getLat() * toleranceRate); + assertEquals(11.836164, result.getLng(), result.getLng() * toleranceRate); + } } \ No newline at end of file From 0b8ad137b30e3769fc662da5fcbdcb8f24790780 Mon Sep 17 00:00:00 2001 From: William Muir Date: Thu, 10 Aug 2017 17:47:24 +1200 Subject: [PATCH 11/11] Minor fixes for merge #story[1124] #pair[wmu16, hyi25] --- src/main/java/seng302/model/Yacht.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/seng302/model/Yacht.java b/src/main/java/seng302/model/Yacht.java index 94f351c5..e676bd80 100644 --- a/src/main/java/seng302/model/Yacht.java +++ b/src/main/java/seng302/model/Yacht.java @@ -32,8 +32,6 @@ public class Yacht { private Logger logger = LoggerFactory.getLogger(Yacht.class); - - private static final Integer SPEED_MULTIPLIER = 4; private static final Double ROUNDING_DISTANCE = 50d; // TODO: 3/08/17 wmu16 - Look into this value further @@ -106,7 +104,7 @@ public class Yacht { Double windSpeedKnots = GameState.getWindSpeedKnots(); Double trueWindAngle = Math.abs(GameState.getWindDirection() - heading); Double boatSpeedInKnots = PolarTable.getBoatSpeed(windSpeedKnots, trueWindAngle); - Double maxBoatSpeed = boatSpeedInKnots / 1.943844492 * 1000 * SPEED_MULTIPLIER; + Double maxBoatSpeed = boatSpeedInKnots / 1.943844492 * 1000; if (sailIn && velocity <= maxBoatSpeed && maxBoatSpeed != 0d) { if (velocity < maxBoatSpeed) { @@ -295,7 +293,7 @@ public class Yacht { currentMarkSeqID++; finishedRace = true; logMarkRounding(currentMark); - System.out.println("YAY YOU FINISHED!"); + logger.debug(sourceId + " finished"); // TODO: 8/08/17 wmu16 - Do something! } } @@ -588,10 +586,13 @@ public class Yacht { if (currentMark.isGate()) { typeString = "gate"; } - System.out.println( - "(" + currentMarkSeqID + ") Passed " + typeString + ": " + currentMark.getMarks().get(0) - .getName() - + " ID(" + currentMark.getId() + ")"); + logger.debug( + String.format("BoatID %d passed %s %s with id %d. Now on leg %d", + sourceId, + typeString, + currentMark.getMarks().get(0).getName(), + currentMark.getId(), + currentMarkSeqID)); } public void addLocationListener (YachtLocationListener listener) {