diff --git a/Klassendiagramm.drawio b/Klassendiagramm.drawio
new file mode 100644
index 0000000..afde28c
--- /dev/null
+++ b/Klassendiagramm.drawio
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/README.md b/README.md
index 9156ddf..e3e5c8a 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,39 @@
# team02-AngryNerds-projekt1-racetrack
PM2 Team 02 Projekt 1 Racetrack
+Racetrack is a pen and paper game that dates back to the early 1960s in this version of the game, the game is digitalized and the math behind it is done automatically rather than calculated by hand and the winner gets informed automatically as well.
+
+The aim of the game is to finish the race faster than your opponent or win by being the only survivor in case the other cars crash.
+
+In order to not crash you have to keep in mind the acceleration and other players car to get to the finish line safely.
+
+# Initialization:
+#### The game can be initialized by the terminal command:
+ ./gradlew run
+You will then be prompted to select a track file from the selection by entering the corresponding number.
+
+#### For each car that is taking part in the race a strategy has to be chosen there are the following options:
+
++ Do not move Strategy
+> This Strategy sets the car stationary, and it won't make any moves during the game staying at the startpoint indefinitely.
++ User Move Strategy
+> The player is prompted for each move to make a choice the different choices you are able to take are as following:
+> > 1=down-left
2=down
3=down-right
4=left
5=no acceleration
6=right
7=up-left
8=up
9=up-right
it is also possible to leave the game when it is your turn by entering 10
++ Move List Strategy
+> For this strategy a predefined list of moves have to be given, the list may contain all allowed moves like mentioned in User Move Strategy
++ Path Follow Move Strategy
+> A list of point has to be given to the follow move strategy, the strategy will then calculate the route to cross each point in the given order.
++ Path Finder Strategy
+> The pathfinder Strategy Calculates a route itself and follows it direction fully automatically.
+
+The shown Track can be interpreted as following:
+'#' is a Wall
+'>,<,^,v' are finish line components
+And every other character represents a car.
+
+### Determining a winner
+The winner gets determined automatically.
The car that first passes the finish line (doing a complete round) is given the win, if all car except one crash the surviving car will be crowned as the winner.
The game will inform you of this, and you will have the option to quit the game or play another match.
+
+## Branching Model
+We choose a simple branching model where all starting features got a branch and where merged into the main branch, some branches who needed unfinished code to be completed where taken from the game branch but merged into the main at the end as well.
Since there was just one end product we abstained from using a development branch and merges where done straight into main.
+
+## Class Diagramm
diff --git a/build.gradle b/build.gradle
index 669126f..9b73dff 100644
--- a/build.gradle
+++ b/build.gradle
@@ -44,7 +44,7 @@ version = '2022.1'
application {
// Define the main class for the application.
- mainClass = 'ch.zhaw.pm2.racetrack.ConsoleApp'
+ mainClass = 'ch.zhaw.pm2.racetrack.Main'
}
run {
diff --git a/follower/challenge_points.txt b/follower/challenge_points.txt
new file mode 100644
index 0000000..d794046
--- /dev/null
+++ b/follower/challenge_points.txt
@@ -0,0 +1,19 @@
+(X:40, Y:22)
+(X:43, Y:22)
+(X:46, Y:21)
+(X:48, Y:19)
+(X:48, Y:17)
+(X:46, Y:15)
+(X:41, Y:13)
+(X:41, Y:10)
+(X:46, Y:9)
+(X:49, Y:4)
+(X:40, Y:2)
+(X:30, Y:2)
+(X:21, Y:3)
+(X:16, Y:7)
+(X:13, Y:10)
+(X:14, Y:14)
+(X:11, Y:19)
+(X:13, Y:22)
+(X:24, Y:22)
diff --git a/src/main/java/ch/zhaw/pm2/racetrack/Game.java b/src/main/java/ch/zhaw/pm2/racetrack/Game.java
index 810ad78..265cd15 100644
--- a/src/main/java/ch/zhaw/pm2/racetrack/Game.java
+++ b/src/main/java/ch/zhaw/pm2/racetrack/Game.java
@@ -18,7 +18,7 @@ import static ch.zhaw.pm2.racetrack.PositionVector.Direction;
public class Game implements GameSpecification {
public static final int NO_WINNER = -1;
private Track track;
- int currentCarIndex;
+ private int currentCarIndex;
UserInterface userInterface;
@@ -27,7 +27,7 @@ public class Game implements GameSpecification {
}
- public boolean initPhase() throws InvalidTrackFormatException, FileNotFoundException {
+ public boolean initPhase() throws InvalidTrackFormatException {
File folder = new File("tracks");
File[] listOfFiles = folder.listFiles();
if (listOfFiles.length > 0) {
@@ -36,42 +36,46 @@ public class Game implements GameSpecification {
tracks.add(file.getName());
}
File selectedTrack = listOfFiles[userInterface.selectOption("Select Track file", tracks)];
- try {
- track = new Track(selectedTrack);
- } catch (FileNotFoundException | PositionVectorNotValid e) {
- e.printStackTrace();
- }
+ selectTrack(selectedTrack);
List moveStrategies = new ArrayList<>();
moveStrategies.add("Do not move Strategy");
moveStrategies.add("User Move Strategy");
moveStrategies.add("Move List Strategy");
moveStrategies.add("Path Follow Move Strategy");
for (int i = 0; i < track.getCarCount(); i++) {
- while(track.getCar(i).getMoveStrategy() == null) {
+ Car car = track.getCar(i);
+ MoveStrategy moveStrategy = null;
+ while (moveStrategy == null) {
+ String filePath;
int moveStrategie = userInterface.selectOption(
"Select Strategy for Car " + i + " (" + track.getCarId(i) + ")", moveStrategies);
switch (moveStrategie + 1) {
case 1:
- track.getCar(i).setMoveStrategy(new DoNotMoveStrategy());
+ moveStrategy = new DoNotMoveStrategy();
break;
case 2:
- track.getCar(i).setMoveStrategy(new UserMoveStrategy(userInterface, i, track.getCarId(i)));
+ moveStrategy = new UserMoveStrategy(userInterface, i, track.getCarId(i));
break;
case 3:
- String path = ".\\moves\\" + selectedTrack.getName().split("\\.")[0] + "-car-" + track.getCar(i).getID() + ".txt";
+ filePath = ".\\moves\\" + selectedTrack.getName().split("\\.")[0] + "-car-" + track.getCar(i).getID() + ".txt";
try {
- MoveStrategy moveStrategy = new MoveListStrategy(path);
- track.getCar(i).setMoveStrategy(moveStrategy);
+ moveStrategy = new MoveListStrategy(filePath);
} catch (FileNotFoundException e) {
- userInterface.printInformation("There is no MoveList implemented. Choose another Strategy!");
+ userInterface.printInformation("There is no Move-List implemented. Choose another Strategy!");
}
//TODO: Backslash kompatibel für Linux
break;
case 4:
- track.getCar(i).setMoveStrategy(new PathFollowerMoveStrategy()); //TODO: add Arguments
+ filePath = ".\\follower\\" + selectedTrack.getName().split("\\.")[0] + "_points.txt";
+ try {
+ moveStrategy = new PathFollowerMoveStrategy(filePath, track.getCarPos(i));
+ } catch (FileNotFoundException e) {
+ userInterface.printInformation("There is no Point-List implemented. Choose another Strategy!");
+ }
break;
}
}
+ selectMoveStrategy(car, moveStrategy);
}
return true;
} else {
@@ -80,6 +84,31 @@ public class Game implements GameSpecification {
}
}
+ /**
+ * The functionality was taken out of init to automate testing
+ *
+ * @param selectedTrack
+ */
+ Track selectTrack(File selectedTrack) {
+ try {
+ track = new Track(selectedTrack);
+ return track;
+ } catch (FileNotFoundException | PositionVectorNotValid | InvalidTrackFormatException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * The functionality was taken out of init to automate testing
+ *
+ * @param car to set the MoveStrategy
+ * @param strategy The movestrategy to set
+ */
+ void selectMoveStrategy(Car car, MoveStrategy strategy) {
+ car.setMoveStrategy(strategy);
+ }
+
/**
* Return the index of the current active car.
* Car indexes are zero-based, so the first car is 0, and the last car is getCarCount() - 1.
@@ -115,6 +144,7 @@ public class Game implements GameSpecification {
/**
* Get the velocity of the specified car.
+ *
* @param carIndex The zero-based carIndex number
* @return A PositionVector containing the car's current velocity
*/
@@ -192,20 +222,20 @@ public class Game implements GameSpecification {
}
public String gamePhase() throws PositionVectorNotValid {
- while (CarsMoving() && getWinner() == NO_WINNER) {
+ while (carsMoving() && getWinner() == NO_WINNER) {
userInterface.printTrack(track);
- Direction direction = null;
- direction= track.getCar(currentCarIndex).getMoveStrategy().nextMove();
- if(direction == null) {
+ Direction direction;
+ direction = track.getCar(currentCarIndex).getMoveStrategy().nextMove();
+ if (direction == null) {
track.getCar(currentCarIndex).setMoveStrategy(new DoNotMoveStrategy());
- direction= track.getCar(currentCarIndex).getMoveStrategy().nextMove();
+ direction = track.getCar(currentCarIndex).getMoveStrategy().nextMove();
}
doCarTurn(direction);
switchToNextActiveCar();
}
userInterface.printTrack(track);
int indexWinner = getWinner();
- if(indexWinner == NO_WINNER){
+ if (indexWinner == NO_WINNER) {
return null;
}
return String.valueOf(track.getCar(indexWinner).getID());
@@ -241,69 +271,15 @@ public class Game implements GameSpecification {
*/
@Override
public List calculatePath(PositionVector startPosition, PositionVector endPosition) {
- ArrayList pathList = new ArrayList<>();
- // Use Bresenham's algorithm to determine positions.
- int x = startPosition.getX();
- int y = startPosition.getY();
-
- // Relative Distance (x & y axis) between end- and starting position
- int diffX = endPosition.getX() - startPosition.getX();
- int diffY = endPosition.getY() - startPosition.getY();
-
- // Absolute distance (x & y axis) between end- and starting position
- int distX = Math.abs(diffX);
- int distY = Math.abs(diffY);
-
- // Direction of vector on x & y axis (-1: to left/down, 0: none, +1 : to right/up)
- int dirX = Integer.signum(diffX);
- int dirY = Integer.signum(diffY);
-
- // Determine which axis is the fast direction and set parallel/diagonal step values
- int parallelStepX, parallelStepY;
- int diagonalStepX, diagonalStepY;
- int distanceSlowAxis, distanceFastAxis;
- if (distX > distY) {
- // x axis is the 'fast' direction
- parallelStepX = dirX;
- parallelStepY = 0; // parallel step only moves in x direction
- diagonalStepX = dirX;
- diagonalStepY = dirY; // diagonal step moves in both directions
- distanceSlowAxis = distY;
- distanceFastAxis = distX;
- } else {
- // y axis is the 'fast' direction
- parallelStepX = 0;
- parallelStepY = dirY; // parallel step only moves in y direction
- diagonalStepX = dirX;
- diagonalStepY = dirY; // diagonal step moves in both directions
- distanceSlowAxis = distX;
- distanceFastAxis = distY;
- }
-
- int error = distanceFastAxis / 2;
- for (int step = 0; step < distanceFastAxis; step++) {
- error -= distanceSlowAxis;
- if (error < 0) {
- error += distanceFastAxis; // correct error value to be positive again
- // step into slow direction; diagonal step
- x += diagonalStepX;
- y += diagonalStepY;
- } else {
- // step into fast direction; parallel step
- x += parallelStepX;
- y += parallelStepY;
- }
-
- pathList.add(new PositionVector(x, y));
- }
- return pathList;
+ return track.calculatePointsOnPath(startPosition, endPosition);
}
+
+
private void calculateWinner(PositionVector start, PositionVector finish, int carIndex) {
List path = calculatePath(start, finish);
for (PositionVector point : path) {
- if (track.getSpaceType(point) != null)
- {
+ if (track.getSpaceType(point) != null) {
switch (track.getSpaceType(point)) {
case FINISH_UP:
if (start.getY() < finish.getY()) {
@@ -333,15 +309,16 @@ public class Game implements GameSpecification {
track.getCar(carIndex).increaseWinPoints();
}
break;
- }
-
}
+
+ }
}
}
/**
* 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
* @param position A PositionVector of the possible crash position
* @return A boolean indicator if the car would crash with a WALL or another car.
@@ -353,17 +330,17 @@ public class Game implements GameSpecification {
public boolean onlyOneCarLeft() {
int carsLeft = 0;
- for(int carIndex = 0; carIndex < track.getCarCount(); carIndex ++) {
- if(! track.getCar(carIndex).isCrashed()) {
+ for (int carIndex = 0; carIndex < track.getCarCount(); carIndex++) {
+ if (!track.getCar(carIndex).isCrashed()) {
carsLeft++;
}
}
return !(carsLeft > 1);
}
- public boolean CarsMoving() {
- for(int carIndex = 0; carIndex < track.getCarCount(); carIndex ++) {
- if(! (track.getCar(carIndex).isCrashed() || track.getCar(carIndex).getMoveStrategy().getClass() == DoNotMoveStrategy.class)) {
+ public boolean carsMoving() {
+ for (int carIndex = 0; carIndex < track.getCarCount(); carIndex++) {
+ if (!(track.getCar(carIndex).isCrashed() || track.getCar(carIndex).getMoveStrategy().getClass() == DoNotMoveStrategy.class)) {
return true;
}
}
diff --git a/src/main/java/ch/zhaw/pm2/racetrack/InvalidFileFormatException.java b/src/main/java/ch/zhaw/pm2/racetrack/InvalidFileFormatException.java
index e7d0c07..6655e69 100644
--- a/src/main/java/ch/zhaw/pm2/racetrack/InvalidFileFormatException.java
+++ b/src/main/java/ch/zhaw/pm2/racetrack/InvalidFileFormatException.java
@@ -1,5 +1,10 @@
package ch.zhaw.pm2.racetrack;
+/**
+ * Class for Exception when invalid Fileformat is used.
+ */
public class InvalidFileFormatException extends Exception {
- // TODO: implementation
+ public InvalidFileFormatException(String errorMessage) {
+ super(errorMessage);
+ }
}
diff --git a/src/main/java/ch/zhaw/pm2/racetrack/InvalidTrackFormatException.java b/src/main/java/ch/zhaw/pm2/racetrack/InvalidTrackFormatException.java
index 25de664..fa24c25 100644
--- a/src/main/java/ch/zhaw/pm2/racetrack/InvalidTrackFormatException.java
+++ b/src/main/java/ch/zhaw/pm2/racetrack/InvalidTrackFormatException.java
@@ -1,5 +1,13 @@
package ch.zhaw.pm2.racetrack;
+/**
+ * Class for Exception when invalid track format is used.
+ */
public class InvalidTrackFormatException extends Exception {
- // TODO: implementation
+ public InvalidTrackFormatException(String errorMessage) {
+ super(errorMessage);
+ }
+ public InvalidTrackFormatException() {
+ super();
+ }
}
diff --git a/src/main/java/ch/zhaw/pm2/racetrack/Main.java b/src/main/java/ch/zhaw/pm2/racetrack/Main.java
index 4013616..aec65bd 100644
--- a/src/main/java/ch/zhaw/pm2/racetrack/Main.java
+++ b/src/main/java/ch/zhaw/pm2/racetrack/Main.java
@@ -6,7 +6,7 @@ import java.util.List;
public class Main {
- public static void main(String[] args) throws InvalidTrackFormatException, FileNotFoundException, PositionVectorNotValid {
+ public static void main(String[] args) throws InvalidTrackFormatException, PositionVectorNotValid {
UserInterface userInterface = new UserInterface("Hello and Welcome to Racetrack by Team02-\"AngryNerds\"");
while (true) {
Game game = new Game(userInterface);
diff --git a/src/main/java/ch/zhaw/pm2/racetrack/Track.java b/src/main/java/ch/zhaw/pm2/racetrack/Track.java
index c83839e..e0a6ff4 100644
--- a/src/main/java/ch/zhaw/pm2/racetrack/Track.java
+++ b/src/main/java/ch/zhaw/pm2/racetrack/Track.java
@@ -355,6 +355,65 @@ public class Track implements TrackSpecification {
return currentSpace.getValue();
}
+ public ArrayList calculatePointsOnPath(PositionVector startPosition, PositionVector endPosition) {
+ ArrayList pathList = new ArrayList<>();
+ // Use Bresenham's algorithm to determine positions.
+ int x = startPosition.getX();
+ int y = startPosition.getY();
+
+ // Relative Distance (x & y axis) between end- and starting position
+ int diffX = endPosition.getX() - startPosition.getX();
+ int diffY = endPosition.getY() - startPosition.getY();
+
+ // Absolute distance (x & y axis) between end- and starting position
+ int distX = Math.abs(diffX);
+ int distY = Math.abs(diffY);
+
+ // Direction of vector on x & y axis (-1: to left/down, 0: none, +1 : to right/up)
+ int dirX = Integer.signum(diffX);
+ int dirY = Integer.signum(diffY);
+
+ // Determine which axis is the fast direction and set parallel/diagonal step values
+ int parallelStepX, parallelStepY;
+ int diagonalStepX, diagonalStepY;
+ int distanceSlowAxis, distanceFastAxis;
+ if (distX > distY) {
+ // x axis is the 'fast' direction
+ parallelStepX = dirX;
+ parallelStepY = 0; // parallel step only moves in x direction
+ diagonalStepX = dirX;
+ diagonalStepY = dirY; // diagonal step moves in both directions
+ distanceSlowAxis = distY;
+ distanceFastAxis = distX;
+ } else {
+ // y axis is the 'fast' direction
+ parallelStepX = 0;
+ parallelStepY = dirY; // parallel step only moves in y direction
+ diagonalStepX = dirX;
+ diagonalStepY = dirY; // diagonal step moves in both directions
+ distanceSlowAxis = distX;
+ distanceFastAxis = distY;
+ }
+
+ int error = distanceFastAxis / 2;
+ for (int step = 0; step < distanceFastAxis; step++) {
+ error -= distanceSlowAxis;
+ if (error < 0) {
+ error += distanceFastAxis; // correct error value to be positive again
+ // step into slow direction; diagonal step
+ x += diagonalStepX;
+ y += diagonalStepY;
+ } else {
+ // step into fast direction; parallel step
+ x += parallelStepX;
+ y += parallelStepY;
+ }
+
+ pathList.add(new PositionVector(x, y));
+ }
+ return pathList;
+ }
+
/**
* Return a String representation of the track, including the car locations.
*
diff --git a/src/main/java/ch/zhaw/pm2/racetrack/strategy/MoveListStrategy.java b/src/main/java/ch/zhaw/pm2/racetrack/strategy/MoveListStrategy.java
index 7d1e68e..3de3f23 100644
--- a/src/main/java/ch/zhaw/pm2/racetrack/strategy/MoveListStrategy.java
+++ b/src/main/java/ch/zhaw/pm2/racetrack/strategy/MoveListStrategy.java
@@ -25,9 +25,9 @@ public class MoveListStrategy implements MoveStrategy {
Direction[] directions = Direction.values();
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
- for (Direction dir : directions) {
- if (dir.toString().equals(line)) {
- moveList.add(dir);
+ for (Direction direction : directions) {
+ if (direction.toString().equals(line)) {
+ moveList.add(direction);
break;
}
}
diff --git a/src/main/java/ch/zhaw/pm2/racetrack/strategy/PathFollowerMoveStrategy.java b/src/main/java/ch/zhaw/pm2/racetrack/strategy/PathFollowerMoveStrategy.java
index 23ad51f..807ddc8 100644
--- a/src/main/java/ch/zhaw/pm2/racetrack/strategy/PathFollowerMoveStrategy.java
+++ b/src/main/java/ch/zhaw/pm2/racetrack/strategy/PathFollowerMoveStrategy.java
@@ -1,15 +1,126 @@
package ch.zhaw.pm2.racetrack.strategy;
+import ch.zhaw.pm2.racetrack.PositionVector;
import ch.zhaw.pm2.racetrack.PositionVector.Direction;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Scanner;
+
/**
* The PathFollowerMoveStrategy class determines the next move based on a file containing points on a path.
*/
public class PathFollowerMoveStrategy implements MoveStrategy {
+ /**
+ * The current Position of the car.
+ */
+ private PositionVector currentPosition;
+ /**
+ * The current Velocity of the car.
+ */
+ private PositionVector currentVelocity;
+ /**
+ * List of all points on the path.
+ */
+ private ArrayList pointList;
+ /**
+ * The index of the next point on the path.
+ */
+ private int pointer;
+
+ /**
+ * Constructor to create a new PathFollowerMoveStrategy for a car.
+ * @param path The location where the file is saved
+ * @param startPosition The start position of the car
+ * @throws FileNotFoundException If the file with the given path does not exist.
+ */
+ public PathFollowerMoveStrategy(String path, PositionVector startPosition) throws FileNotFoundException {
+ pointList = new ArrayList<>();
+ pointer = 0;
+ readFile(new File(path));
+ currentPosition = startPosition;
+ currentVelocity = new PositionVector(0, 0);
+ }
+
+ /**
+ * Method to read the given File and add the points to the pointList
+ * @param trackFile the File Object which should be read
+ * @throws FileNotFoundException If the file with the given path does not exist.
+ */
+ public void readFile(File trackFile) throws FileNotFoundException {
+ Scanner scanner = new Scanner(new FileInputStream(trackFile), "UTF-8");
+ while (scanner.hasNextLine()) {
+ String line = scanner.nextLine();
+ String[] coordinates = line.split("(\\(X:|, Y:|\\))");
+ pointList.add(new PositionVector(Integer.parseInt(coordinates[1]), Integer.parseInt(coordinates[2])));
+ }
+ }
+
+ /**
+ * Method to select the direction for the next move.
+ * @return The direction for the next move. null if there are no points left in the list.
+ */
@Override
public Direction nextMove() {
- // TODO: implementation
- throw new UnsupportedOperationException();
+ // if no more points in the list --> return null
+ if (pointer >= pointList.size()) {
+ return null;
+ }
+
+ // increase pointer variable if the next point is reached.
+ if (pointList.get(pointer).equals(currentPosition)) {
+ pointer ++;
+ }
+
+ // calculate Vector from current Position to next Point
+ PositionVector movementVector = new PositionVector(pointList.get(pointer).getX() - currentPosition.getX(), pointList.get(pointer).getY() - currentPosition.getY());
+
+ // select acceleration for X
+ int accelerationX;
+ if((movementVector.getX() == 0 && currentVelocity.getX() > 0) || //reduce velocity to 0 if the destination coordinate is reached
+ (movementVector.getX() > 0 && movementVector.getX()/2.0 <= currentVelocity.getX()) || //increase velocity
+ (movementVector.getX() < 0 && movementVector.getX()/2.0 < currentVelocity.getX())){ //reduce velocity
+ accelerationX = -1;
+ } else if((movementVector.getX() == 0 && currentVelocity.getX() < 0) || //reduce velocity to 0 if the destination coordinate is reached
+ (movementVector.getX() > 0 && movementVector.getX()/2.0 > currentVelocity.getX()) || //increase velocity
+ (movementVector.getX() < 0 && movementVector.getX()/2.0 >= currentVelocity.getX())) { //reduce velocity
+ accelerationX = 1;
+ }
+ else { //no acceleration
+ accelerationX = 0;
+ }
+
+ // select acceleration for Y
+ int accelerationY;
+ if((movementVector.getY() == 0 && currentVelocity.getY() > 0) || //reduce velocity to 0 if the destination coordinate is reached
+ (movementVector.getY() > 0 && movementVector.getY()/2.0 <= currentVelocity.getY()) || //increase velocity
+ (movementVector.getY() < 0 && movementVector.getY()/2.0 < currentVelocity.getY())){ //reduce velocity
+ accelerationY = -1;
+ } else if((movementVector.getY() == 0 && currentVelocity.getY() < 0) || //reduce velocity to 0 if the destination coordinate is reached
+ (movementVector.getY() > 0 && movementVector.getY()/2.0 > currentVelocity.getY()) || //increase velocity
+ (movementVector.getY() < 0 && movementVector.getY()/2.0 >= currentVelocity.getY())) { //reduce velocity
+ accelerationY = 1;
+ }
+ else { //no acceleration
+ accelerationY = 0;
+ }
+
+ //update current Velocity and current Position with the selected acceleration
+ currentVelocity = new PositionVector(currentVelocity.getX() + accelerationX, currentVelocity.getY() + accelerationY);
+ currentPosition = new PositionVector(currentPosition.getX() + currentVelocity.getX(), currentPosition.getY() + currentVelocity.getY());
+
+ //Find Direction for acceleration
+ PositionVector acceleration = new PositionVector(accelerationX, accelerationY);
+ Direction[] directions = Direction.values();
+ for (Direction direction : directions) {
+ if (direction.vector.equals(acceleration)) {
+ return direction;
+ }
+ }
+ return null;
+
}
}
diff --git a/src/test/java/ch/zhaw/pm2/racetrack/CarTest.java b/src/test/java/ch/zhaw/pm2/racetrack/CarTest.java
new file mode 100644
index 0000000..ebac56c
--- /dev/null
+++ b/src/test/java/ch/zhaw/pm2/racetrack/CarTest.java
@@ -0,0 +1,228 @@
+
+package ch.zhaw.pm2.racetrack;
+
+import ch.zhaw.pm2.racetrack.strategy.*;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Tests for Class Car
+ */
+class CarTest {
+
+ Car car;
+
+ // Default coordinates for tests
+ int DEFAULT_X = 10;
+ int DEFAULT_Y = 10;
+ char DEFAULT_ID = 'f';
+
+ /**
+ * Create a new Car Object and set Position to a defined Default Position
+ */
+ @BeforeEach
+ void setUp() {
+ car = new Car(DEFAULT_ID, new PositionVector(DEFAULT_X, DEFAULT_Y));
+ }
+
+ @Test
+ void getID() {
+ assertEquals(DEFAULT_ID, car.getID());
+ }
+
+ /**
+ * Method to check nextPosition with coordinates as int
+ *
+ * @param x the expected value for x coordinate
+ * @param y the expected value for y coordinate
+ */
+ void checkNextPosition(int x, int y) {
+ assertEquals(new PositionVector(x, y), car.nextPosition());
+ }
+
+ /**
+ * - checks if the position of the car can be set and saved correctly with valid positions.
+ * - checks if an exception is throwed and position keeps unchanged if invalid coordinates are entered.
+ */
+ @Test
+ void setPosition() {
+ checkNextPosition(DEFAULT_X, DEFAULT_Y);
+
+ // List of valid Positions
+ List validPositions = new ArrayList<>();
+ validPositions.add(new PositionVector(20, 20));
+ validPositions.add(new PositionVector(0, 0));
+ validPositions.add(new PositionVector(20, 0));
+ validPositions.add(new PositionVector(0, 20));
+
+ for (PositionVector positionVector : validPositions) {
+ car.setPosition(positionVector);
+ assertEquals(positionVector, car.nextPosition());
+ }
+
+ // List of invalid positions.
+ List invalidPositions = new ArrayList<>();
+ invalidPositions.add(new PositionVector(0, -20));
+ invalidPositions.add(new PositionVector(-20, 0));
+ invalidPositions.add(new PositionVector(-20, -20));
+
+ for (PositionVector positionVector : invalidPositions) {
+ boolean exception = false;
+ setUp();
+ try {
+ car.setPosition(positionVector);
+ } catch (IllegalArgumentException e) {
+ exception = true;
+ }
+ assertTrue(exception);
+
+ // position should keep unchanged
+ checkNextPosition(DEFAULT_X, DEFAULT_Y);
+ }
+ }
+
+ void checkVelocity(int x, int y) {
+ assertEquals(new PositionVector(x, y), car.getVelocity());
+ }
+
+
+ /**
+ * Checks if the methods accelerate, move and getVelocity are working correctly with acceleration in all directions.
+ * Checks also if velocity is calculated correctly if method accelerate is called a second time.
+ */
+ @Test
+ void movement() {
+ // add all possible directions in a List
+ List directions = Arrays.asList(PositionVector.Direction.values());
+
+ //position shouldn't be changed because velocity should be 0.
+ car.move();
+ checkNextPosition(DEFAULT_X, DEFAULT_Y);
+
+
+
+ for (PositionVector.Direction direction1 : directions) {
+ for (PositionVector.Direction direction2 : directions) {
+
+ //create a new instance of Car with default coordinates and velocity 0.
+ setUp();
+
+ //variables to save the actual expected result of method nextPosition
+ int expectedNextPosX = DEFAULT_X;
+ int expectedNextPosY = DEFAULT_Y;
+
+ //variables to save the acutal expected result of method getVelocity
+ int expectedVelocityX = 0;
+ int expectedVelocityY = 0;
+
+ car.accelerate(direction1);
+ expectedVelocityX += direction1.vector.getX();
+ expectedVelocityY += direction1.vector.getY();
+ expectedNextPosX += direction1.vector.getX();
+ expectedNextPosY += direction1.vector.getY();
+ checkVelocity(expectedVelocityX, expectedVelocityY);
+ checkNextPosition(expectedNextPosX, expectedNextPosY);
+
+ car.move();
+ expectedNextPosX += direction1.vector.getX();
+ expectedNextPosY += direction1.vector.getY();
+ checkVelocity(expectedVelocityX, expectedVelocityY);
+ checkNextPosition(expectedNextPosX, expectedNextPosY);
+
+
+ car.accelerate(direction2);
+ expectedVelocityX += direction2.vector.getX();
+ expectedVelocityY += direction2.vector.getY();
+ expectedNextPosX += direction2.vector.getX();
+ expectedNextPosY += direction2.vector.getY();
+ checkVelocity(expectedVelocityX, expectedVelocityY);
+ checkNextPosition(expectedNextPosX, expectedNextPosY);
+
+ car.move();
+ checkVelocity(expectedVelocityX, expectedVelocityY);
+ expectedNextPosX += (direction1.vector.getX() + direction2.vector.getX());
+ expectedNextPosY += (direction1.vector.getY() + direction2.vector.getY());
+ checkNextPosition(expectedNextPosX, expectedNextPosY);
+ }
+ }
+ }
+
+
+ /**
+ * test for methods crash and isCrashed. checks if state crashed is set and returned correctly.
+ */
+ @Test
+ void crash() {
+ assertFalse(car.isCrashed());
+ car.crash();
+ assertTrue(car.isCrashed());
+ }
+
+ /**
+ * test for methods setMoveStrategy. Checks if the MoveStrategy Object is saved and returned correctly
+ * with all Types of MoveStrategy.
+ */
+ @Test
+ void MoveStrategy() {
+ MoveStrategy moveStrategy;
+
+ moveStrategy = new DoNotMoveStrategy();
+ car.setMoveStrategy(moveStrategy);
+ assertEquals(moveStrategy, car.getMoveStrategy());
+
+ try {
+ moveStrategy = new MoveListStrategy(".\\moves\\challenge-car-a.txt");
+ } catch (FileNotFoundException e) {
+ Assertions.fail();
+ }
+ car.setMoveStrategy(moveStrategy);
+ assertEquals(moveStrategy, car.getMoveStrategy());
+
+ try {
+ moveStrategy = new PathFollowerMoveStrategy(".\\follower\\challenge_points.txt", new PositionVector(0, 0));
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ car.setMoveStrategy(moveStrategy);
+ assertEquals(moveStrategy, car.getMoveStrategy());
+
+ moveStrategy = new UserMoveStrategy(new UserInterface("Hello"),0,'a');
+ car.setMoveStrategy(moveStrategy);
+ assertEquals(moveStrategy, car.getMoveStrategy());
+ }
+
+ /**
+ * Test for get WinPoints
+ */
+ @Test
+ void getWinPoints() {
+ assertEquals(0,car.getWinPoints());
+ }
+
+ /**
+ * Test for increase WinPoints
+ */
+ @Test
+ void increaseWinPoints() {
+ car.increaseWinPoints();
+ assertEquals(1,car.getWinPoints());
+ }
+
+ /**
+ * Test for deduct WinPoints
+ */
+ @Test
+ void deductWinPoints() {
+ car.deductWinPoints();
+ assertEquals(-1,car.getWinPoints());
+ }
+
+}
diff --git a/src/test/java/ch/zhaw/pm2/racetrack/GameTest.java b/src/test/java/ch/zhaw/pm2/racetrack/GameTest.java
new file mode 100644
index 0000000..0c1dea3
--- /dev/null
+++ b/src/test/java/ch/zhaw/pm2/racetrack/GameTest.java
@@ -0,0 +1,233 @@
+package ch.zhaw.pm2.racetrack;
+
+import ch.zhaw.pm2.racetrack.strategy.UserMoveStrategy;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Assertions;
+import java.io.File;
+import java.util.List;
+import static ch.zhaw.pm2.racetrack.Game.NO_WINNER;
+import static ch.zhaw.pm2.racetrack.PositionVector.Direction.*;
+
+
+/**
+ * Test for Class Game
+ */
+class GameTest {
+ private UserInterface userInterface;
+ private Game game;
+ private Track track;
+
+ private String TRACK_FILE_PATH = ".\\tracks\\challenge.txt";
+ private int CAR_INDEX_ONE = 0;
+ private int CAR_INDEX_TWO = 1;
+
+ /**
+ * This nested Class tests if the game gets initiatet correctly
+ */
+ @Nested
+ @DisplayName("Test correct Setup")
+ class Setup {
+
+ @BeforeEach
+ void setup() {
+ userInterface = new UserInterface("Test");
+ game = new Game(userInterface);
+ track = game.selectTrack(new File(TRACK_FILE_PATH));
+ game.selectMoveStrategy(track.getCar(CAR_INDEX_ONE), new UserMoveStrategy(new UserInterface("Testing"), CAR_INDEX_ONE, track.getCarId(CAR_INDEX_ONE)));
+ game.selectMoveStrategy(track.getCar(CAR_INDEX_TWO), new UserMoveStrategy(new UserInterface("Testing"), CAR_INDEX_TWO, track.getCarId(CAR_INDEX_TWO)));
+
+ }
+
+ @Test
+ void getCurrentCarIndex() {
+ Assertions.assertEquals(CAR_INDEX_ONE, game.getCurrentCarIndex());
+ game.switchToNextActiveCar();
+ Assertions.assertEquals(CAR_INDEX_TWO, game.getCurrentCarIndex());
+ }
+
+ @Test
+ void getCarId() {
+ Assertions.assertEquals('a', game.getCarId(0));
+ Assertions.assertEquals('b', game.getCarId(1));
+ }
+
+ @Test
+ void getCarPosition() {
+ Assertions.assertEquals(new PositionVector(24, 22), game.getCarPosition(0));
+ Assertions.assertEquals(new PositionVector(24, 24), game.getCarPosition(1));
+ }
+
+ @Test
+ void getCarVelocity() {
+ Assertions.assertEquals(new PositionVector(0, 0), game.getCarVelocity(0));
+ Assertions.assertEquals(new PositionVector(0, 0), game.getCarVelocity(1));
+ }
+
+ @Test
+ void getWinner() {
+ Assertions.assertEquals(NO_WINNER, game.getWinner());
+ }
+
+ @Test
+ void onlyOneCarLeft() {
+ Assertions.assertFalse(game.onlyOneCarLeft());
+ }
+
+ @Test
+ void carsMoving() {
+ Assertions.assertTrue(game.carsMoving());
+ }
+ }
+
+ /**
+ * This nested Class makes basic manipulation after Game init.
+ */
+ @Nested
+ @DisplayName("Basic manipulation")
+ class Manipulation {
+
+ @BeforeEach
+ void setup() {
+ userInterface = new UserInterface("Test");
+ game = new Game(userInterface);
+ track = game.selectTrack(new File(TRACK_FILE_PATH));
+ game.selectMoveStrategy(track.getCar(CAR_INDEX_ONE), new UserMoveStrategy(new UserInterface("Testing"), CAR_INDEX_ONE, track.getCarId(CAR_INDEX_ONE)));
+ game.selectMoveStrategy(track.getCar(CAR_INDEX_TWO), new UserMoveStrategy(new UserInterface("Testing"), CAR_INDEX_TWO, track.getCarId(CAR_INDEX_TWO)));
+
+ }
+
+ @Test
+ void carTurnCorrect() {
+ try {
+ game.doCarTurn(RIGHT);
+ Assertions.assertEquals(new PositionVector(1, 0), game.getCarVelocity(0));
+ } catch (PositionVectorNotValid positionVectorNotValid) {
+ positionVectorNotValid.printStackTrace();
+ }
+ }
+
+ @Test
+ void carCrash() {
+ try {
+ game.doCarTurn(PositionVector.Direction.UP);
+ Assertions.assertTrue(game.onlyOneCarLeft());
+ } catch (PositionVectorNotValid positionVectorNotValid) {
+ positionVectorNotValid.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * This nested Class tests a Playtrough. And implements a UserInterface interagtion to pretend a real player
+ */
+ @Nested
+ @DisplayName("Playtrough")
+ class Play {
+ private Game game;
+
+ @Test
+ void winner() {
+ game = new Game(new interFace("Test",new Integer[]{0,2,1},new PositionVector.Direction[]{RIGHT,
+ RIGHT,
+ RIGHT,
+ NONE,
+ NONE,
+ NONE,
+ NONE,
+ UP,
+ LEFT,
+ LEFT,
+ LEFT,
+ LEFT,
+ UP_LEFT,
+ NONE,
+ RIGHT,
+ RIGHT,
+ RIGHT,
+ NONE,
+ LEFT,
+ DOWN_LEFT,
+ DOWN_LEFT,
+ LEFT,
+ LEFT,
+ NONE,
+ RIGHT,
+ NONE,
+ DOWN,
+ DOWN,
+ RIGHT,
+ NONE,
+ RIGHT,
+ DOWN,
+ NONE,
+ UP_RIGHT,
+ RIGHT,
+ UP_RIGHT,
+ UP_RIGHT,
+ RIGHT,
+ RIGHT}));
+ try {
+ game.initPhase();
+ Assertions.assertEquals("a",game.gamePhase());
+ } catch (InvalidTrackFormatException | PositionVectorNotValid e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ void crashA() {
+ game = new Game(new interFace("Test",new Integer[]{0,2,2},new PositionVector.Direction[]{UP}));
+ try {
+ game.initPhase();
+ Assertions.assertEquals("b",game.gamePhase());
+ } catch (InvalidTrackFormatException | PositionVectorNotValid e) {
+ e.printStackTrace();
+ }
+ }
+
+ }
+
+ private class interFace extends UserInterface {
+
+ private final PositionVector.Direction[] directions;
+ private final Integer[] instructions;
+ private int pointerDir,pointerInstruction;
+
+
+ public interFace(String welcometxt, Integer[] instructions, PositionVector.Direction[] directions) {
+ super(welcometxt);
+ pointerDir = -1;
+ pointerInstruction = -1;
+ this.instructions = instructions;
+ this.directions = directions;
+ }
+
+ @Override
+ public int selectOption(String text, List options) {
+ pointerInstruction++;
+ return instructions[pointerInstruction];
+
+ }
+
+ public void printInformation(String text) {
+ }
+
+ public void printTrack(Track track) {
+ }
+
+ public void quit(String text) {
+ }
+
+ public PositionVector.Direction selectDirection(int playingCarIndex, char playingCarID) {
+ pointerDir += 1;
+ if(pointerDir < directions.length) {
+ return directions[pointerDir];
+ }
+ return NONE;
+ }
+
+ }
+}