Track feature #21

Merged
fassband merged 9 commits from track-feature into main 2022-03-11 18:41:11 +01:00
6 changed files with 425 additions and 23 deletions

View File

@ -29,6 +29,7 @@ dependencies {
// beryx uses SLF4J. To remove warning, we add the implementation "no operation"
implementation 'org.slf4j:slf4j-nop:2.+'
implementation 'junit:junit:4.13.1'
// Use JUnit Jupiter API for testing.
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'

View File

@ -127,6 +127,8 @@ public class Game implements GameSpecification {
throw new UnsupportedOperationException();
}
/**
* Does indicate if a car would have a crash with a WALL space or another car at the given position.
* @param carIndex The zero-based carIndex number
@ -135,7 +137,8 @@ public class Game implements GameSpecification {
*/
@Override
public boolean willCarCrash(int carIndex, PositionVector position) {
// TODO: implementation
throw new UnsupportedOperationException();
return true;
}
}

View File

@ -0,0 +1,9 @@
package ch.zhaw.pm2.racetrack;
public class PositionVectorNotValid extends Throwable {
public PositionVectorNotValid(String message) {
super(message);
}
public PositionVectorNotValid() {}
}

View File

@ -1,9 +1,13 @@
package ch.zhaw.pm2.racetrack;
import ch.zhaw.pm2.racetrack.given.ConfigSpecification;
import ch.zhaw.pm2.racetrack.given.TrackSpecification;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* This class represents the racetrack board.
@ -54,8 +58,9 @@ import java.io.FileNotFoundException;
public class Track implements TrackSpecification {
public static final char CRASH_INDICATOR = 'X';
// TODO: Add necessary variables
private List<String> track;
private List<Car> cars;
private final List<PositionVector> finishLine;
/**
* Initialize a Track from the given track file.
@ -64,9 +69,178 @@ public class Track implements TrackSpecification {
* @throws FileNotFoundException if the given track file could not be found
* @throws InvalidTrackFormatException if the track file contains invalid data (no tracklines, ...)
*/
public Track(File trackFile) throws FileNotFoundException, InvalidTrackFormatException {
// TODO: implementation
throw new UnsupportedOperationException();
public Track(File trackFile) throws FileNotFoundException, InvalidTrackFormatException, PositionVectorNotValid {
track = new ArrayList<>();
cars = new ArrayList<>();
finishLine = new ArrayList<>();
readFile(trackFile);
findFinish();
addCars();
}
/**
* This method reads the File and saves it to the track ArrayList Line by Line
*
* @param trackFile the File where the track has been documented
* @throws FileNotFoundException if the FilePath is invalid.
*/
private void readFile(File trackFile) throws FileNotFoundException {
Scanner scanner = new Scanner(trackFile);
while (scanner.hasNextLine()) {
track.add(scanner.nextLine());
}
}
private void addCars() throws InvalidTrackFormatException {
ConfigSpecification.SpaceType[] spaceTypes = ConfigSpecification.SpaceType.values();
List<Character> allSpaceTypesAsChar = new ArrayList<>();
List<Character> usedSymbolForCar = new ArrayList<>();
for (ConfigSpecification.SpaceType spaceType : spaceTypes) {
allSpaceTypesAsChar.add(spaceType.getValue());
}
for (int j = 0; j < track.size(); j++) {
String line = track.get(j);
for (int i = 0; i < line.length(); i++) {
char possibleCarChar = line.charAt(i);
if (!allSpaceTypesAsChar.contains(possibleCarChar)) {
if (usedSymbolForCar.contains(possibleCarChar)) {
throw new InvalidTrackFormatException();
}
usedSymbolForCar.add(possibleCarChar);
cars.add(new Car(possibleCarChar, new PositionVector(i, j)));
}
}
}
}
private void findFinish() throws InvalidTrackFormatException {
for (int i = 0; i < track.size(); i++) {
String line = track.get(i);
for (int j = 0; j < line.length(); j++) {
if (line.charAt(j) == ConfigSpecification.SpaceType.FINISH_LEFT.getValue() ||
line.charAt(j) == ConfigSpecification.SpaceType.FINISH_RIGHT.getValue() ||
line.charAt(j) == ConfigSpecification.SpaceType.FINISH_DOWN.getValue() ||
line.charAt(j) == ConfigSpecification.SpaceType.FINISH_UP.getValue()) {
finishLine.add(new PositionVector(j, i));
}
}
}
if (finishLine.size() == 0) {
throw new InvalidTrackFormatException();
}
ConfigSpecification.SpaceType finishTyp = getSpaceType(finishLine.get(0));
for (PositionVector positionVector : finishLine) {
if (getSpaceType(positionVector) != finishTyp) {
throw new InvalidTrackFormatException();
}
}
}
private PositionVector findChar(char symbol) {
PositionVector vector = null;
for (int i = 0; i < track.size(); i++) {
String line = track.get(i);
for (int j = 0; j < line.length(); j++) {
if (line.charAt(j) == symbol) {
vector = new PositionVector(j, i);
}
}
}
return vector;
}
private void drawCharOnTrackIndicator(PositionVector positionVector, char symbol) {
String line = track.get(positionVector.getY());
line = line.substring(0, positionVector.getX()) + symbol + line.substring(positionVector.getX() + 1);
track.remove(positionVector.getY());
track.add(positionVector.getY(), line);
}
private void isPositionVectorOnTrack(PositionVector positionVector) throws PositionVectorNotValid {
try{
track.get(positionVector.getY()).charAt(positionVector.getX());
}catch (IndexOutOfBoundsException e) {
throw new PositionVectorNotValid();
}
}
/**
* @return all Cars
*/
public List<Car> getCars() {
return cars;
}
/**
* @return finishLine
*/
public List<PositionVector> getFinishLine() {
return finishLine;
}
/**
* @return the track
*/
public List<String> getTrack() {
return track;
}
/**
* This Method will update the Car on the track
* and will make the Car move to the next position
*
* @param carIndex representing the current Car
*/
public void moveCar(int carIndex) {
makeCarMoveInTrack(carIndex);
//Change position of car
getCar(carIndex).move();
}
/**
* This class does change the Position of the car only in the track.
*
* @param carIndex of the current car
*/
private void makeCarMoveInTrack(int carIndex) {
PositionVector positionVector = findChar(getCarId(carIndex));
//Removes the Car at Current Pos
drawCharOnTrackIndicator(positionVector, ConfigSpecification.SpaceType.TRACK.getValue());
//Adds Car at new Position
positionVector = cars.get(carIndex).nextPosition();
drawCharOnTrackIndicator(positionVector, cars.get(carIndex).getID());
}
/**
* This Method will check if the Car could crash at the specific position
*
* @param positionVector the position to check if the car could crash
* @return true if car would crash. Else false.
*/
public boolean willCrashAtPosition(int carIndex, PositionVector positionVector) throws PositionVectorNotValid {
isPositionVectorOnTrack(positionVector);
char charAtPosition = track.get(positionVector.getY()).charAt(positionVector.getX());
if (getCarId(carIndex) == charAtPosition) return false;
return (charAtPosition == ConfigSpecification.SpaceType.WALL.value);
}
/**
* This Method will make the Car Crash. In Track and in the Car Object
*
* @param carIndex representing current Car
* @param positionVector where the Crash did happen
*/
public void carDoesCrash(int carIndex, PositionVector positionVector) throws PositionVectorNotValid{
isPositionVectorOnTrack(positionVector);
Car car = cars.get(carIndex);
car.crash();
car.setPosition(positionVector);
drawCharOnTrackIndicator(new PositionVector(positionVector.getX(), positionVector.getY()), CRASH_INDICATOR);
}
/**
@ -78,8 +252,16 @@ public class Track implements TrackSpecification {
*/
@Override
public Config.SpaceType getSpaceType(PositionVector position) {
// TODO: implementation
throw new UnsupportedOperationException();
//isPositionVectorOnTrack(position); Should be used but we are not allowed to change method head. We don't use function anyway
char charAtPosition = track.get(position.getY()).charAt(position.getX());
ConfigSpecification.SpaceType[] spaceTypes = ConfigSpecification.SpaceType.values();
for (ConfigSpecification.SpaceType spaceType : spaceTypes) {
if (spaceType.getValue() == charAtPosition) {
return spaceType;
}
}
return null;
}
/**
@ -89,8 +271,7 @@ public class Track implements TrackSpecification {
*/
@Override
public int getCarCount() {
// TODO: implementation
throw new UnsupportedOperationException();
return cars.size();
}
/**
@ -101,8 +282,7 @@ public class Track implements TrackSpecification {
*/
@Override
public Car getCar(int carIndex) {
// TODO: implementation
throw new UnsupportedOperationException();
return cars.get(carIndex);
}
/**
@ -113,20 +293,19 @@ public class Track implements TrackSpecification {
*/
@Override
public char getCarId(int carIndex) {
// TODO: implementation
throw new UnsupportedOperationException();
return cars.get(carIndex).getID();
}
/**
* Get the position of the specified car.
* Returns Null if carIndex not valid
*
* @param carIndex The zero-based carIndex number
* @return A PositionVector containing the car's current position
*/
@Override
public PositionVector getCarPos(int carIndex) {
// TODO: implementation
throw new UnsupportedOperationException();
return findChar(cars.get(carIndex).getID());
}
/**
@ -137,8 +316,7 @@ public class Track implements TrackSpecification {
*/
@Override
public PositionVector getCarVelocity(int carIndex) {
// TODO: implementation
throw new UnsupportedOperationException();
return cars.get(carIndex).getVelocity();
}
/**
@ -152,8 +330,15 @@ public class Track implements TrackSpecification {
*/
@Override
public char getCharAtPosition(int y, int x, Config.SpaceType currentSpace) {
// TODO: implementation
throw new UnsupportedOperationException();
char charAtPos = track.get(y).charAt(x);
PositionVector positionVector = new PositionVector(x, y);
for (Car car : cars) {
if (charAtPos == car.getID()) {
return charAtPos;
}
}
if (positionVector.equals(findChar(CRASH_INDICATOR))) return CRASH_INDICATOR;
return currentSpace.getValue();
}
/**
@ -163,7 +348,8 @@ public class Track implements TrackSpecification {
*/
@Override
public String toString() {
// TODO: implementation
throw new UnsupportedOperationException();
StringBuilder str = new StringBuilder();
for (String line : track) str.append(line).append("\n");
return str.toString();
}
}

View File

@ -0,0 +1,26 @@
###############################################################
################# #############
############### ###########
############### #########
############### ################## #########
############### #################### #########
############## ##################### #########
############ ###################### ##########
######### ###################### ############
######### ###################### ##############
######### ##################### ################
######### ################# ##################
######### ################ ##################
########## ################## ###############
########### #################### #############
########### ####################### ##########
########## ########################## #########
######### ############################ ########
######## ############################# ########
####### ############################## ########
###### ############################# ########
###### ############################ #########
###### > a ###########
###### > a ##############
######## > b #################
###############################################################

View File

@ -0,0 +1,177 @@
package ch.zhaw.pm2.racetrack;
import ch.zhaw.pm2.racetrack.given.ConfigSpecification;
import org.junit.jupiter.api.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
public class TrackTest {
Track trackObj;
@Nested
@DisplayName("Positiv Test Cases")
class positivClass {
@BeforeEach
void setup() {
File file = new File(".\\tracks\\challenge.txt");
try {
trackObj = new Track(file);
} catch (Exception | PositionVectorNotValid e) {
System.err.println("Error in Test compareTrack" + e.getMessage());
}
}
@Test
@DisplayName("Create correct amount of Car instance")
void checkCars() {
Assertions.assertEquals(2, trackObj.getCarCount());
}
@Test
@DisplayName("Create Car instance with correct Symbols / Id")
void checkCarId() {
Assertions.assertEquals('a', trackObj.getCarId(0));
Assertions.assertEquals('b', trackObj.getCarId(1));
}
@Test
@DisplayName("Check getSpaceTyp")
void getSpaceTyp() {
Assertions.assertEquals(ConfigSpecification.SpaceType.FINISH_RIGHT, trackObj.getSpaceType(new PositionVector(22, 24)));
}
@Test
@DisplayName("Find FinishLine")
void findFinish() {
List<PositionVector> expected = new ArrayList<>();
expected.add(new PositionVector(22, 22));
expected.add(new PositionVector(22, 23));
expected.add(new PositionVector(22, 24));
Assertions.assertEquals(expected, trackObj.getFinishLine());
}
@Test
@DisplayName("Converts Trackfile correctly to List<String>")
void checkTrack() {
Track trackObj;
try {
trackObj = new Track(new File(".\\tracks\\oval-anticlock-right.txt"));
List<String> track = new ArrayList<>();
track.add("##################################################");
track.add("##################################################");
track.add("############## #############");
track.add("########## ##########");
track.add("####### #######");
track.add("###### ################# ######");
track.add("##### ################### #####");
track.add("##### ################### #####");
track.add("###### ################# ######");
track.add("####### > a #######");
track.add("########## > ##########");
track.add("############## > b ##############");
track.add("##################################################");
track.add("##################################################");
Assertions.assertLinesMatch(track, trackObj.getTrack());
} catch (FileNotFoundException | InvalidTrackFormatException | PositionVectorNotValid e) {
e.printStackTrace();
}
}
@Test
@DisplayName("Make Car move down on track")
void makeCarMoveDown() {
PositionVector beforeMove = trackObj.getCarPos(0);
trackObj.getCar(0).accelerate(PositionVector.Direction.DOWN);
trackObj.moveCar(0);
PositionVector afterMove = trackObj.getCarPos(0);
Assertions.assertEquals(beforeMove.getY() + 1, afterMove.getY());
Assertions.assertEquals(beforeMove.getX(), afterMove.getX());
}
@Test
@DisplayName("Make Car move with (0,0) acceleration on track")
void makeCarStay() {
PositionVector beforeMove = trackObj.getCarPos(0);
trackObj.moveCar(0);
PositionVector afterMove = trackObj.getCarPos(0);
Assertions.assertEquals(beforeMove.getY(), afterMove.getY());
Assertions.assertEquals(beforeMove.getX(), afterMove.getX());
}
@Test
@DisplayName("Will Car Crash")
void willCarCrash() {
try {
//Car will Crash
Assertions.assertTrue(trackObj.willCrashAtPosition(0, new PositionVector(25, 21)));
//Car will not Crash and is on track
Assertions.assertFalse(trackObj.willCrashAtPosition(0, new PositionVector(7, 22)));
//Car will not Crash and is on finishLine
Assertions.assertFalse(trackObj.willCrashAtPosition(0, trackObj.getFinishLine().get(0)));
} catch (PositionVectorNotValid positionVectorNotValid) {
positionVectorNotValid.printStackTrace();
Assertions.fail("Test should not throw error");
}
}
@Test
@DisplayName("Make Car Crash")
void makeCarCrash() {
try {
trackObj.carDoesCrash(0, new PositionVector(6, 22));
} catch (PositionVectorNotValid positionVectorNotValid) {
positionVectorNotValid.printStackTrace();
Assertions.fail("Test should not throw exception");
}
Assertions.assertEquals(Track.CRASH_INDICATOR, trackObj.getTrack().get(22).charAt(6));
Assertions.assertTrue(trackObj.getCar(0).isCrashed());
}
}
@Nested
@DisplayName("Negative TestCase")
class negativeClass {
File file;
@BeforeEach
void setup() {
file = new File(".\\tracks\\challenge.txt");
try {
trackObj = new Track(file);
} catch (Exception | PositionVectorNotValid e) {
System.err.println("Error in Test compareTrack" + e.getMessage());
}
}
@Test
@DisplayName("Throw error if File not found")
void canReadFile() {
file = new File(".\\tracks\\NotExisting.txt");
Assertions.assertThrows(FileNotFoundException.class, () -> new Track(file));
}
@Test
@DisplayName("Throw error if File is invalid")
void invalidTrackFile() {
File testfile = new File(".\\src\\test\\InvalidTracks\\sameCar.txt");
Assertions.assertThrows(InvalidTrackFormatException.class, () -> new Track(testfile));
}
@Test
@DisplayName("Invalid Position Vector used")
void invalidPositionVector() {
Assertions.assertThrows(PositionVectorNotValid.class, () -> trackObj.willCrashAtPosition(0, new PositionVector(100, 200)));
Assertions.assertThrows(PositionVectorNotValid.class, () -> trackObj.carDoesCrash(1,new PositionVector(200,100)));
}
}
}