mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 06:18:44 +00:00
Merge branch 'Story71_TackAndGybeSmoothly' into 'develop'
Story 71 Tack/Gybe, Story 65 VMG Autopilot # Implementation - Added autopilot functionality to move boat towards a given heading with a single function call. - Added functionality to move boat from one side of the wind to the other (tack/gybe) with a single button press. - Added functionality to move boat to correct VMG from polars with single button press. - Pressing a button a second time will disable the autopilot, as will pressing the Upwind/Downwind keys. # Testing - Fairly simple JUnit testing for a set of beginning headings and expected ends for the autopilot - Unsure how cucumber testing would actually be better than simple junit testing in this case. See merge request !55
This commit is contained in:
@@ -23,5 +23,5 @@ Haoming Yin <hyi25@uclive.ac.nz> <haoming.y@icloud.com>
|
|||||||
Peter Galloway <ptg19@uclive.ac.nz> Peter <ptg19@uclive.ac.nz>
|
Peter Galloway <ptg19@uclive.ac.nz> Peter <ptg19@uclive.ac.nz>
|
||||||
Zhi You Tan <zyt10@uclive.ac.nz> zyt10 <zyt10@uclive.ac.nz>
|
Zhi You Tan <zyt10@uclive.ac.nz> zyt10 <zyt10@uclive.ac.nz>
|
||||||
Zhi You Tan <zyt10@uclive.ac.nz> Ryan Tan <ryan_zhiyou@hotmail.com>
|
Zhi You Tan <zyt10@uclive.ac.nz> Ryan Tan <ryan_zhiyou@hotmail.com>
|
||||||
Alistair McIntyre <ajm412@uclive.ac.nz> alistairjmcintyre <alistairjmcintyre@gmail.com>
|
Alistair McIntyre <ajm412@uclive.ac.nz> <alistairjmcintyre@gmail.com>
|
||||||
Calum <cir27@uclive.ac.nz> cir27 <cir27@uclive.ac.nz>
|
Calum <cir27@uclive.ac.nz> cir27 <cir27@uclive.ac.nz>
|
||||||
@@ -8,10 +8,10 @@ import org.w3c.dom.Document;
|
|||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
import seng302.model.mark.CompoundMark;
|
|
||||||
import seng302.gameServer.server.simulator.Corner;
|
import seng302.gameServer.server.simulator.Corner;
|
||||||
import seng302.model.mark.Mark;
|
|
||||||
import seng302.gameServer.server.simulator.RoundingType;
|
import seng302.gameServer.server.simulator.RoundingType;
|
||||||
|
import seng302.model.mark.CompoundMark;
|
||||||
|
import seng302.model.mark.Mark;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the race xml file to get course details
|
* Parses the race xml file to get course details
|
||||||
@@ -84,17 +84,17 @@ public class CourseParser extends FileParser {
|
|||||||
NodeList marks = e.getElementsByTagName("Mark");
|
NodeList marks = e.getElementsByTagName("Mark");
|
||||||
List<Mark> subMarks = new ArrayList<>();
|
List<Mark> subMarks = new ArrayList<>();
|
||||||
for (int i = 0; i < marks.getLength(); i++) {
|
for (int i = 0; i < marks.getLength(); i++) {
|
||||||
Mark mark = getMark(marks.item(i));
|
Mark mark = getMark(marks.item(i));
|
||||||
if (mark != null) {
|
if (mark != null) {
|
||||||
subMarks.add(mark);
|
subMarks.add(mark);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CompoundMark(markID, name, subMarks);
|
return new CompoundMark(markID, name, subMarks);
|
||||||
}
|
}
|
||||||
System.out.println("Failed to create compound mark.");
|
System.out.println("Failed to create compound mark.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Mark getMark(Node node) {
|
private Mark getMark(Node node) {
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ public class Yacht {
|
|||||||
private GeoPoint location;
|
private GeoPoint location;
|
||||||
private Integer boatStatus;
|
private Integer boatStatus;
|
||||||
private Double velocity;
|
private Double velocity;
|
||||||
|
private Boolean isAuto;
|
||||||
|
private Double autoHeading;
|
||||||
|
|
||||||
//MARK ROUNDING INFO
|
//MARK ROUNDING INFO
|
||||||
private GeoPoint lastLocation; //For purposes of mark rounding calculations
|
private GeoPoint lastLocation; //For purposes of mark rounding calculations
|
||||||
@@ -84,6 +86,7 @@ public class Yacht {
|
|||||||
this.boatName = boatName;
|
this.boatName = boatName;
|
||||||
this.country = country;
|
this.country = country;
|
||||||
this.sailIn = false;
|
this.sailIn = false;
|
||||||
|
this.isAuto = false;
|
||||||
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
|
||||||
@@ -128,6 +131,8 @@ public class Yacht {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runAutoPilot();
|
||||||
|
|
||||||
//UPDATE BOAT LOCATION
|
//UPDATE BOAT LOCATION
|
||||||
lastLocation = location;
|
lastLocation = location;
|
||||||
location = GeoUtility.getGeoCoordinate(location, heading, velocity * secondsElapsed);
|
location = GeoUtility.getGeoCoordinate(location, heading, velocity * secondsElapsed);
|
||||||
@@ -300,15 +305,60 @@ public class Yacht {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts the heading of the boat by a given amount, while recording the boats
|
||||||
|
* last heading.
|
||||||
|
*
|
||||||
|
* @param amount the amount by which to adjust the boat heading.
|
||||||
|
*/
|
||||||
public void adjustHeading(Double amount) {
|
public void adjustHeading(Double amount) {
|
||||||
Double newVal = heading + amount;
|
Double newVal = heading + amount;
|
||||||
lastHeading = heading;
|
lastHeading = heading;
|
||||||
heading = (double) Math.floorMod(newVal.longValue(), 360L);
|
heading = (double) Math.floorMod(newVal.longValue(), 360L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Swaps the boats direction from one side of the wind to the other.
|
||||||
|
*/
|
||||||
public void tackGybe(Double windDirection) {
|
public void tackGybe(Double windDirection) {
|
||||||
Double normalizedHeading = normalizeHeading();
|
if (isAuto) {
|
||||||
adjustHeading(-2 * normalizedHeading);
|
disableAutoPilot();
|
||||||
|
} else {
|
||||||
|
Double normalizedHeading = normalizeHeading();
|
||||||
|
Double newVal = (-2 * normalizedHeading) + heading;
|
||||||
|
Double newHeading = (double) Math.floorMod(newVal.longValue(), 360L);
|
||||||
|
setAutoPilot(newHeading);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables the boats auto pilot feature, which will move the boat towards a given heading.
|
||||||
|
* @param thisHeading The heading to move the boat towards.
|
||||||
|
*/
|
||||||
|
private void setAutoPilot(Double thisHeading) {
|
||||||
|
isAuto = true;
|
||||||
|
autoHeading = thisHeading;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables the auto pilot function.
|
||||||
|
*/
|
||||||
|
public void disableAutoPilot() {
|
||||||
|
isAuto = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the boat towards the given heading when the auto pilot was set. Disables the auto pilot
|
||||||
|
* in the event that the boat is within the range of 1 turn step of its goal.
|
||||||
|
*/
|
||||||
|
public void runAutoPilot() {
|
||||||
|
if (isAuto) {
|
||||||
|
turnTowardsHeading(autoHeading);
|
||||||
|
if (Math.abs(heading - autoHeading)
|
||||||
|
<= TURN_STEP) { //Cancel when within 1 turn step of target.
|
||||||
|
isAuto = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void toggleSailIn() {
|
public void toggleSailIn() {
|
||||||
@@ -316,6 +366,7 @@ public class Yacht {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void turnUpwind() {
|
public void turnUpwind() {
|
||||||
|
disableAutoPilot();
|
||||||
Double normalizedHeading = normalizeHeading();
|
Double normalizedHeading = normalizeHeading();
|
||||||
if (normalizedHeading == 0) {
|
if (normalizedHeading == 0) {
|
||||||
if (lastHeading < 180) {
|
if (lastHeading < 180) {
|
||||||
@@ -337,6 +388,7 @@ public class Yacht {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void turnDownwind() {
|
public void turnDownwind() {
|
||||||
|
disableAutoPilot();
|
||||||
Double normalizedHeading = normalizeHeading();
|
Double normalizedHeading = normalizeHeading();
|
||||||
if (normalizedHeading == 0) {
|
if (normalizedHeading == 0) {
|
||||||
if (lastHeading < 180) {
|
if (lastHeading < 180) {
|
||||||
@@ -357,38 +409,59 @@ public class Yacht {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes the VMG from the polartable for upwind or downwind depending on the boats direction,
|
||||||
|
* and uses this to calculate a heading to move the yacht towards.
|
||||||
|
*/
|
||||||
public void turnToVMG() {
|
public void turnToVMG() {
|
||||||
Double normalizedHeading = normalizeHeading();
|
if (isAuto) {
|
||||||
Double optimalHeading;
|
disableAutoPilot();
|
||||||
HashMap<Double, Double> optimalPolarMap;
|
|
||||||
|
|
||||||
if (normalizedHeading >= 90 && normalizedHeading <= 270) { // Downwind
|
|
||||||
optimalPolarMap = PolarTable.getOptimalDownwindVMG(GameState.getWindSpeedKnots());
|
|
||||||
optimalHeading = optimalPolarMap.keySet().iterator().next();
|
|
||||||
} else {
|
} else {
|
||||||
optimalPolarMap = PolarTable.getOptimalUpwindVMG(GameState.getWindSpeedKnots());
|
Double normalizedHeading = normalizeHeading();
|
||||||
optimalHeading = optimalPolarMap.keySet().iterator().next();
|
Double optimalHeading;
|
||||||
}
|
HashMap<Double, Double> optimalPolarMap;
|
||||||
// Take optimal heading and turn into correct
|
|
||||||
optimalHeading =
|
|
||||||
optimalHeading + (double) Math.floorMod(GameState.getWindDirection().longValue(), 360L);
|
|
||||||
|
|
||||||
turnTowardsHeading(optimalHeading);
|
if (normalizedHeading >= 90 && normalizedHeading <= 270) { // Downwind
|
||||||
|
optimalPolarMap = PolarTable.getOptimalDownwindVMG(GameState.getWindSpeedKnots());
|
||||||
}
|
|
||||||
|
|
||||||
private void turnTowardsHeading(Double newHeading) {
|
|
||||||
if (heading < 90 && newHeading > 270) {
|
|
||||||
adjustHeading(-TURN_STEP);
|
|
||||||
} else {
|
|
||||||
if (heading < newHeading) {
|
|
||||||
adjustHeading(TURN_STEP);
|
|
||||||
} else {
|
} else {
|
||||||
adjustHeading(-TURN_STEP);
|
optimalPolarMap = PolarTable.getOptimalUpwindVMG(GameState.getWindSpeedKnots());
|
||||||
}
|
}
|
||||||
|
optimalHeading = optimalPolarMap.keySet().iterator().next();
|
||||||
|
|
||||||
|
if (normalizedHeading > 180) {
|
||||||
|
optimalHeading = 360 - optimalHeading;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take optimal heading and turn into a boat heading rather than a wind heading.
|
||||||
|
optimalHeading =
|
||||||
|
optimalHeading + GameState.getWindDirection();
|
||||||
|
|
||||||
|
setAutoPilot(optimalHeading);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a given heading and rotates the boat towards that heading.
|
||||||
|
* This does not care about being upwind or downwind, just which direction will reach a given
|
||||||
|
* heading faster.
|
||||||
|
*
|
||||||
|
* @param newHeading The heading to turn the yacht towards.
|
||||||
|
*/
|
||||||
|
private void turnTowardsHeading(Double newHeading) {
|
||||||
|
Double newVal = heading - newHeading;
|
||||||
|
if (Math.floorMod(newVal.longValue(), 360L) > 180) {
|
||||||
|
adjustHeading(TURN_STEP);
|
||||||
|
} else {
|
||||||
|
adjustHeading(-TURN_STEP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a heading normalized for the wind direction. Heading direction into the wind is 0,
|
||||||
|
* directly away is 180.
|
||||||
|
*
|
||||||
|
* @return The normalized heading accounting for wind direction.
|
||||||
|
*/
|
||||||
private Double normalizeHeading() {
|
private Double normalizeHeading() {
|
||||||
Double normalizedHeading = heading - GameState.windDirection;
|
Double normalizedHeading = heading - GameState.windDirection;
|
||||||
normalizedHeading = (double) Math.floorMod(normalizedHeading.longValue(), 360L);
|
normalizedHeading = (double) Math.floorMod(normalizedHeading.longValue(), 360L);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public class CompoundMark {
|
|||||||
|
|
||||||
public CompoundMark(int markID, String name, List<Mark> marks) {
|
public CompoundMark(int markID, String name, List<Mark> marks) {
|
||||||
this.compoundMarkId = markID;
|
this.compoundMarkId = markID;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.marks.addAll(marks);
|
this.marks.addAll(marks);
|
||||||
if (marks.size() > 1) {
|
if (marks.size() > 1) {
|
||||||
this.midPoint = GeoUtility.getDirtyMidPoint(marks.get(0), marks.get(1));
|
this.midPoint = GeoUtility.getDirtyMidPoint(marks.get(0), marks.get(1));
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
package seng302.model.mark;
|
package seng302.model.mark;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
@@ -10,20 +19,11 @@ import seng302.model.stream.xml.parser.RaceXMLData;
|
|||||||
import seng302.utilities.XMLGenerator;
|
import seng302.utilities.XMLGenerator;
|
||||||
import seng302.utilities.XMLParser;
|
import seng302.utilities.XMLParser;
|
||||||
|
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to hold the order of the marks in the race.
|
* Class to hold the order of the marks in the race.
|
||||||
*/
|
*/
|
||||||
public class MarkOrder {
|
public class MarkOrder {
|
||||||
|
|
||||||
private List<CompoundMark> raceMarkOrder;
|
private List<CompoundMark> raceMarkOrder;
|
||||||
private Logger logger = LoggerFactory.getLogger(MarkOrder.class);
|
private Logger logger = LoggerFactory.getLogger(MarkOrder.class);
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ public class MarkOrder {
|
|||||||
* @return An ordered list of marks in the race
|
* @return An ordered list of marks in the race
|
||||||
* OR null if the mark order could not be loaded
|
* OR null if the mark order could not be loaded
|
||||||
*/
|
*/
|
||||||
public List<CompoundMark> getMarkOrder(){
|
public List<CompoundMark> getMarkOrder() {
|
||||||
if (raceMarkOrder == null){
|
if (raceMarkOrder == null){
|
||||||
logger.warn("Race order accessed but not instantiated");
|
logger.warn("Race order accessed but not instantiated");
|
||||||
return null;
|
return null;
|
||||||
@@ -55,10 +55,9 @@ public class MarkOrder {
|
|||||||
/**
|
/**
|
||||||
* @param currentSeqID The seqID of the current mark the boat is heading to
|
* @param currentSeqID The seqID of the current mark the boat is heading to
|
||||||
* @return The mark last passed
|
* @return The mark last passed
|
||||||
* @throws IndexOutOfBoundsException if there is no next mark.
|
* @throws IndexOutOfBoundsException if there is no next mark. Check seqID != 0 first
|
||||||
* Check seqID != 0 first
|
|
||||||
*/
|
*/
|
||||||
public CompoundMark getPreviousMark(Integer currentSeqID) throws IndexOutOfBoundsException{
|
public CompoundMark getPreviousMark(Integer currentSeqID) throws IndexOutOfBoundsException {
|
||||||
return raceMarkOrder.get(currentSeqID - 1);
|
return raceMarkOrder.get(currentSeqID - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,10 +68,10 @@ public class MarkOrder {
|
|||||||
/**
|
/**
|
||||||
* @param currentSeqID The seqID of the current mark the boat is heading to
|
* @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
|
* @return The mark following the mark that the boat is heading to
|
||||||
* @throws IndexOutOfBoundsException if there is no next mark.
|
* @throws IndexOutOfBoundsException if there is no next mark. Check using {@link
|
||||||
* Check using {@link #isLastMark(Integer)}
|
* #isLastMark(Integer)}
|
||||||
*/
|
*/
|
||||||
public CompoundMark getNextMark(Integer currentSeqID) throws IndexOutOfBoundsException{
|
public CompoundMark getNextMark(Integer currentSeqID) throws IndexOutOfBoundsException {
|
||||||
return raceMarkOrder.get(currentSeqID + 1);
|
return raceMarkOrder.get(currentSeqID + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +80,7 @@ public class MarkOrder {
|
|||||||
* @param xml An AC35 RaceXML
|
* @param xml An AC35 RaceXML
|
||||||
* @return An ordered list of marks in the race
|
* @return An ordered list of marks in the race
|
||||||
*/
|
*/
|
||||||
private List<CompoundMark> loadRaceOrderFromXML(String xml){
|
private List<CompoundMark> loadRaceOrderFromXML(String xml) {
|
||||||
|
|
||||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||||
DocumentBuilder db;
|
DocumentBuilder db;
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import javafx.scene.Node;
|
|||||||
import javafx.scene.input.KeyEvent;
|
import javafx.scene.input.KeyEvent;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import seng302.gameServer.MainServerThread;
|
import seng302.gameServer.MainServerThread;
|
||||||
|
import seng302.gameServer.server.messages.BoatActionMessage;
|
||||||
|
import seng302.gameServer.server.messages.BoatActionType;
|
||||||
import seng302.model.RaceState;
|
import seng302.model.RaceState;
|
||||||
import seng302.model.Yacht;
|
import seng302.model.Yacht;
|
||||||
import seng302.model.stream.packets.StreamPacket;
|
import seng302.model.stream.packets.StreamPacket;
|
||||||
@@ -22,8 +24,6 @@ import seng302.model.stream.parser.PositionUpdateData.DeviceType;
|
|||||||
import seng302.model.stream.parser.RaceStatusData;
|
import seng302.model.stream.parser.RaceStatusData;
|
||||||
import seng302.model.stream.xml.parser.RaceXMLData;
|
import seng302.model.stream.xml.parser.RaceXMLData;
|
||||||
import seng302.model.stream.xml.parser.RegattaXMLData;
|
import seng302.model.stream.xml.parser.RegattaXMLData;
|
||||||
import seng302.gameServer.server.messages.BoatActionMessage;
|
|
||||||
import seng302.gameServer.server.messages.BoatActionType;
|
|
||||||
import seng302.utilities.StreamParser;
|
import seng302.utilities.StreamParser;
|
||||||
import seng302.utilities.XMLParser;
|
import seng302.utilities.XMLParser;
|
||||||
import seng302.visualiser.controllers.LobbyController;
|
import seng302.visualiser.controllers.LobbyController;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package seng302.model.mark;
|
package seng302.model.mark;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
package seng302.models;
|
package seng302.models;
|
||||||
|
|
||||||
|
import static junit.framework.TestCase.assertEquals;
|
||||||
|
import static junit.framework.TestCase.assertFalse;
|
||||||
|
import static junit.framework.TestCase.assertTrue;
|
||||||
|
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import seng302.model.mark.CompoundMark;
|
import seng302.model.mark.CompoundMark;
|
||||||
import seng302.model.mark.Mark;
|
|
||||||
import seng302.model.mark.MarkOrder;
|
import seng302.model.mark.MarkOrder;
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertEquals;
|
|
||||||
import static junit.framework.TestCase.assertFalse;
|
|
||||||
import static junit.framework.TestCase.assertTrue;
|
|
||||||
|
|
||||||
public class MarkOrderTest {
|
public class MarkOrderTest {
|
||||||
private static MarkOrder markOrder;
|
private static MarkOrder markOrder;
|
||||||
private static Integer currentSeqID;
|
private static Integer currentSeqID;
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package seng302.models;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import seng302.gameServer.GameState;
|
||||||
|
import seng302.model.PolarTable;
|
||||||
|
import seng302.model.Yacht;
|
||||||
|
|
||||||
|
|
||||||
|
public class YachtTest {
|
||||||
|
|
||||||
|
private static Yacht y1;
|
||||||
|
//Yacht y2;
|
||||||
|
private static Double windDirection = 180d;
|
||||||
|
private static Double windSpeed = 20d;
|
||||||
|
private static GameState gs;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setUp() {
|
||||||
|
y1 = new Yacht("Yacht", 101, "Y1", "Y1", "Yacht 1", "C1");
|
||||||
|
gs = new GameState("localhost");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void tackGybeTest() {
|
||||||
|
HashMap<Double, Double> values = new HashMap<>();
|
||||||
|
values.put(280.0, 80.0);
|
||||||
|
values.put(270.0, 90.0);
|
||||||
|
values.put(359.0, 1.0);
|
||||||
|
values.put(180.0, 180.0);
|
||||||
|
values.put(75.0, 285.0);
|
||||||
|
|
||||||
|
for (Double begin : values.keySet()) {
|
||||||
|
y1.setHeading(begin);
|
||||||
|
y1.tackGybe(windDirection);
|
||||||
|
for (int i = 0; i < 50; i++) {
|
||||||
|
y1.runAutoPilot();
|
||||||
|
}
|
||||||
|
assertEquals(values.get(begin), y1.getHeading(), 5.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void vmgTest() {
|
||||||
|
|
||||||
|
PolarTable.parsePolarFile(getClass().getResourceAsStream("/config/acc_polars.csv"));
|
||||||
|
Double upwind = PolarTable.getOptimalUpwindVMG(windSpeed).keySet().iterator().next();
|
||||||
|
Double downwind = PolarTable.getOptimalDownwindVMG(windSpeed).keySet().iterator().next();
|
||||||
|
|
||||||
|
HashMap<Double, Double> values = new HashMap<>();
|
||||||
|
|
||||||
|
upwind = (double) Math.floorMod(upwind.longValue() + windDirection.longValue(), 360L);
|
||||||
|
Double upwindRight = upwind;
|
||||||
|
Double upwindLeft = 360 - upwindRight;
|
||||||
|
downwind = (double) Math.floorMod(downwind.longValue() + windDirection.longValue(), 360L);
|
||||||
|
Double downwindRight = downwind;
|
||||||
|
Double downwindLeft = 360 - downwindRight;
|
||||||
|
|
||||||
|
values.put(190d, upwindRight);
|
||||||
|
values.put(170d, upwindLeft);
|
||||||
|
values.put(10d, downwindLeft);
|
||||||
|
values.put(350d, downwindRight);
|
||||||
|
|
||||||
|
for (Double begin : values.keySet()) {
|
||||||
|
System.out.println(begin);
|
||||||
|
y1.setHeading(begin);
|
||||||
|
y1.turnToVMG();
|
||||||
|
for (int i = 0; i < 50; i++) {
|
||||||
|
y1.runAutoPilot();
|
||||||
|
System.out.println(y1.getHeading());
|
||||||
|
}
|
||||||
|
y1.disableAutoPilot();
|
||||||
|
assertEquals(values.get(begin), y1.getHeading(), 5.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void tearDown() {
|
||||||
|
y1 = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user