Compare commits

..

72 Commits

Author SHA1 Message Date
Marcela Ruiz 552b350c30 Feedback P1 Ruiz
Feedback P1 Ruiz
2022-04-08 11:55:57 +02:00
Leonardo Brandenberger 3e50a96f41 Merge remote-tracking branch 'origin/main' 2022-03-25 23:54:18 +01:00
Leonardo Brandenberger e68ab597d5 small adjustment in javadoc GameTest, MoveListStrategyTest, TrackTest
Author added in all of them
2022-03-25 23:54:10 +01:00
romanschenk37 9a278eb2db Javadoc in GameTest.java 2022-03-25 23:53:03 +01:00
Leonardo Brandenberger 31c76273c9 Merge remote-tracking branch 'origin/main' 2022-03-25 23:48:21 +01:00
Leonardo Brandenberger a5b12760ba added small improvements in GameTest 2022-03-25 23:48:07 +01:00
Leonardo Brandenberger 4a955c2978 added small improvements in GameTest 2022-03-25 23:47:48 +01:00
Andrin Fassbind 3ed26fb909 javadoc 2022-03-25 23:46:06 +01:00
Andrin Fassbind 143b5da14c Merge remote-tracking branch 'origin/main' 2022-03-25 23:45:03 +01:00
Andrin Fassbind 591bbc423d javadoc 2022-03-25 23:44:56 +01:00
romanschenk37 b4664cd854 Javadoc in MoveListStrategyTest 2022-03-25 23:44:06 +01:00
romanschenk37 1addc2a6d5 Merge remote-tracking branch 'origin/main' 2022-03-25 23:40:55 +01:00
romanschenk37 45cffafb6b code cleanup 2022-03-25 23:40:41 +01:00
Leonardo Brandenberger 9d8e699c02 Merge remote-tracking branch 'origin/main' 2022-03-25 23:38:58 +01:00
Leonardo Brandenberger cec225ea10 checked and added javadocs where needed in CarTest 2022-03-25 23:38:49 +01:00
romanschenk37 087ead234e Merge remote-tracking branch 'origin/main' 2022-03-25 23:38:35 +01:00
romanschenk37 52d0ace051 code cleanup 2022-03-25 23:38:26 +01:00
Leonardo Brandenberger c041006efb fixed small bug in README.md 2022-03-25 23:27:28 +01:00
Leonardo Brandenberger b68b567804 Merge remote-tracking branch 'origin/main' 2022-03-25 23:22:50 +01:00
Leonardo Brandenberger 94fe5c1e53 fixed small bug in README.md 2022-03-25 23:22:40 +01:00
Leonardo Brandenberger 093992ec99 fixed small bug in README.md 2022-03-25 23:21:20 +01:00
Andrin Fassbind 5701eaae43 Merge remote-tracking branch 'origin/main' 2022-03-25 23:19:21 +01:00
Andrin Fassbind 01391075bd javadoc 2022-03-25 23:19:09 +01:00
Leonardo Brandenberger 3b76abf760 Merge remote-tracking branch 'origin/main' 2022-03-25 23:16:21 +01:00
Leonardo Brandenberger e4e08859f7 finished README.md 2022-03-25 23:16:11 +01:00
romanschenk37 ee6b348611 Merge remote-tracking branch 'origin/main' 2022-03-25 23:13:53 +01:00
Andrin Fassbind be240d6721 codecleanup 2022-03-25 23:06:23 +01:00
romanschenk37 be2fb1fb5c code cleanup 2022-03-25 23:06:20 +01:00
Leonardo Brandenberger c41b56b47e Merge remote-tracking branch 'origin/main' 2022-03-25 22:58:02 +01:00
Leonardo Brandenberger d0da883795 fixed typos in several classes 2022-03-25 22:57:48 +01:00
romanschenk37 acd01b42eb code cleanup 2022-03-25 22:42:51 +01:00
Leonardo Brandenberger efe1e1f304 Merge remote-tracking branch 'origin/main' 2022-03-25 22:34:36 +01:00
Leonardo Brandenberger bb0e52b8bd added javadocs in InvalidFileFormatException, InvalidTrackFormatException, Track, UserInterface and Main. Enhanced Game methods 2022-03-25 22:34:26 +01:00
Andrin Fassbind 6c72354278 codecleanup 2022-03-25 22:33:22 +01:00
romanschenk37 2f6b2d2e41 Merge remote-tracking branch 'origin/main' 2022-03-25 22:15:30 +01:00
romanschenk37 cf9301d4b8 javadocs of Methods to create Movestrategy 2022-03-25 22:15:19 +01:00
Andrin Fassbind 8233758ca8 Klassendiagramm.svg updated 2022-03-25 22:11:42 +01:00
romanschenk37 976b153edb Merge remote-tracking branch 'origin/main' 2022-03-25 22:04:50 +01:00
romanschenk37 a77d02b440 refactoring Game.java 2022-03-25 22:04:38 +01:00
Leonardo Brandenberger 6560de8708 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	src/main/java/ch/zhaw/pm2/racetrack/InvalidFileFormatException.java
2022-03-25 22:04:18 +01:00
Andrin Fassbind d08ebc0187 code cleanup 2022-03-25 21:58:23 +01:00
Andrin Fassbind 77652705dc code cleanup 2022-03-25 21:55:19 +01:00
Leonardo Brandenberger 77922b71b8 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	src/main/java/ch/zhaw/pm2/racetrack/Game.java
#	src/main/java/ch/zhaw/pm2/racetrack/InvalidFileFormatException.java
2022-03-25 21:53:11 +01:00
Leonardo Brandenberger 73fd298fe5 added java docs 2022-03-25 21:48:40 +01:00
Andrin Fassbind 00cb917842 Merge remote-tracking branch 'origin/main' 2022-03-25 21:48:25 +01:00
Andrin Fassbind 65d7ea6e7e code cleanup 2022-03-25 21:48:12 +01:00
romanschenk37 a002362b62 Code Cleanup 2022-03-25 21:26:56 +01:00
romanschenk37 c72ae904e7 Merge remote-tracking branch 'origin/main' 2022-03-25 21:14:29 +01:00
romanschenk37 70a2179cce Code Cleanup 2022-03-25 21:14:18 +01:00
Leonardo Brandenberger 64996f4b93 added comments when exception is thrown in Track 2022-03-25 21:09:27 +01:00
romanschenk37 e2c6b5cb49 Merge remote-tracking branch 'origin/main' 2022-03-25 20:57:19 +01:00
romanschenk37 29b9daadd4 Code Cleanup 2022-03-25 20:57:07 +01:00
Leonardo Brandenberger 5c52f06956 Merge remote-tracking branch 'origin/main' 2022-03-25 20:51:50 +01:00
Leonardo Brandenberger 00debf7fa5 improved java docs 2022-03-25 20:51:27 +01:00
Andrin Fassbind a9366de7bc code cleanup 2022-03-25 20:28:47 +01:00
Andrin Fassbind a2560ce5d5 Klassendiagramm.svg 2022-03-25 20:22:37 +01:00
Andrin Fassbind 48675b0ca5 codecleanup MoveListStrategy 2022-03-25 20:17:30 +01:00
Andrin Fassbind bb41595609 updated Klassendiagramm.drawio and README 2022-03-25 20:08:11 +01:00
romanschenk37 d1444a366f Code Cleanup 2022-03-25 19:16:17 +01:00
romanschenk37 3761377b61 Code Cleanup 2022-03-25 19:14:01 +01:00
romanschenk37 53b2dabc59 Merge remote-tracking branch 'origin/main' 2022-03-25 16:31:47 +01:00
romanschenk37 52f9eb7629 fixing function quit in usermovestrategy 2022-03-25 16:31:37 +01:00
Andrin Fassbind 4a401f54c4 updated Klassendiagramm.drawio 2022-03-25 16:23:56 +01:00
Andrin Fassbind c43a08cef9 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	src/main/java/ch/zhaw/pm2/racetrack/strategy/PathFinderMoveStrategy.java
2022-03-25 11:08:48 +01:00
Andrin Fassbind 8581b7ee64 - updated javadoc
- added playscenario to GameTest
2022-03-25 11:06:08 +01:00
romanschenk37 6c4f18a0d1 change in Readme.md 2022-03-25 11:05:29 +01:00
romanschenk37 daa0cc322a removed Todo 2022-03-25 11:00:30 +01:00
romanschenk37 09ab684fdd Merge remote-tracking branch 'origin/main' 2022-03-25 10:52:43 +01:00
romanschenk37 ff41150e44 Javadoc in PathFinderMoveStrategy.java 2022-03-25 10:52:32 +01:00
Leonardo Brandenberger d038132055 Merge remote-tracking branch 'origin/main' 2022-03-25 09:27:09 +01:00
Leonardo Brandenberger 38728f0921 finished Java 2022-03-25 09:27:00 +01:00
romanschenk37 483d800484 Merge pull request #31 from PM2-IT21bWIN-ruiz-mach-krea/Strategy
Strategy
2022-03-25 09:24:21 +01:00
23 changed files with 628 additions and 329 deletions

BIN
FeedbackP1_ruiz.md.pdf Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

1
Klassendiagramm.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 115 KiB

View File

@ -1,20 +1,22 @@
# team02-AngryNerds-projekt1-racetrack
PM2 Team 02 Projekt 1 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. 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. 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. In order to not crash you have to keep in mind the acceleration and other player's car to get to the finish line safely.
# Testing
#### All test can be run by the terminal command:
gradlew test
# Initialization: # Initialization:
#### The game can be initialized by the terminal command: #### The game can be initialized by the terminal command:
./gradlew run gradlew run
You will then be prompted to select a track file from the selection by entering the corresponding number. 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: #### 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 + 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. > This Strategy sets the car stationary, and it won't make any moves during the game staying at the start point indefinitely.
+ User Move Strategy + 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: > 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 <br> 2=down<br> 3=down-right<br> 4=left<br> 5=no acceleration<br> 6=right <br> 7=up-left<br> 8=up<br> 9=up-right<br> it is also possible to leave the game when it is your turn by entering 10 > > 1=down-left <br> 2=down<br> 3=down-right<br> 4=left<br> 5=no acceleration<br> 6=right <br> 7=up-left<br> 8=up<br> 9=up-right<br> it is also possible to leave the game when it is your turn by entering 10
@ -38,7 +40,12 @@ And every other character represents a car.
The winner gets determined automatically. <br> 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.<br>The game will inform you of this, and you will have the option to quit the game or play another match. The winner gets determined automatically. <br> 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.<br>The game will inform you of this, and you will have the option to quit the game or play another match.
## Branching Model ## 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.<br> Since there was just one end product we abstained from using a development branch and merges where done straight into main branch. 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.<br> Since there was just one end product we abstained from using a development branch and merges where done straight into main branch.<br>Commits which contain only documentation and doesn't change any functionality are committed directly into the Main branch.
## Class Diagramm ## Class Diagramm
![Classdiagramm of this program](/Klassendiagramm.drawio) To create the best possible insight, all methods that are in the classes that we edited are shown completely. Including the private methods, since they can clearly be distinguished from the public ones through their prefix. <br>
However, the classes that were already given, i.e. not editable, are also in the class diagram for clarity, but the methods had been omitted since they can be found in the documentation, and the interfaces are just there for grading test purposes. <br><br>
![Classdiagram of this program](./Klassendiagramm.svg)
## GitHub Project
Our GitHub Project can be found here: [GitHubProject](https://github.zhaw.ch/PM2-IT21bWIN-ruiz-mach-krea/team02-AngryNerds-projekt1-racetrack/projects)

View File

@ -1,3 +1,7 @@
(X:28, Y:22)
(X:31, Y:22)
(X:34, Y:22)
(X:37, Y:22)
(X:40, Y:22) (X:40, Y:22)
(X:43, Y:22) (X:43, Y:22)
(X:46, Y:21) (X:46, Y:21)

View File

@ -35,6 +35,3 @@ UP_RIGHT
UP_RIGHT UP_RIGHT
RIGHT RIGHT
RIGHT RIGHT

View File

@ -62,14 +62,25 @@ public class Car implements CarSpecification {
return id; return id;
} }
/**
* Increases the winpoints by one
*/
public void increaseWinPoints() { public void increaseWinPoints() {
winPoints++; winPoints++;
} }
/**
* Decreases the winpoints by one
*/
public void deductWinPoints() { public void deductWinPoints() {
winPoints--; winPoints--;
} }
/**
* Returns the current value of winpoints.
*
* @return winpoints of the car
*/
public int getWinPoints() { public int getWinPoints() {
return winPoints; return winPoints;
} }
@ -86,7 +97,7 @@ public class Car implements CarSpecification {
/** /**
* Set this Car position directly, regardless of current position and velocity. * Set this Car position directly, regardless of current position and velocity.
* This should only be used by the game controller in rare cases to set the crash or winning position. * This should only be used by the game controller in rare cases to set the crash or winning position.
* The next position is normaly automatically calculated and set in the {@link #move()} method. * The next position is normally automatically calculated and set in the {@link #move()} method.
* *
* @param position The new position to set the car directly to. * @param position The new position to set the car directly to.
* @throws IllegalArgumentException if invalid PositionVector is given. * @throws IllegalArgumentException if invalid PositionVector is given.
@ -140,7 +151,7 @@ public class Car implements CarSpecification {
} }
/** /**
* Mark this Car as being crashed. * Marks the car as crashed
*/ */
@Override @Override
public void crash() { public void crash() {
@ -148,7 +159,7 @@ public class Car implements CarSpecification {
} }
/** /**
* Returns whether this Car has been marked as crashed. * Returns whether this Car has been marked as crashed or not
* *
* @return Returns true if crash() has been called on this Car, false otherwise. * @return Returns true if crash() has been called on this Car, false otherwise.
*/ */
@ -158,20 +169,20 @@ public class Car implements CarSpecification {
} }
/** /**
* Set move strategy * Returns the current move Strategy
*
* @return the current move Strategy
*/
public MoveStrategy getMoveStrategy() {
return this.moveStrategy;
}
/**
* Set the move Strategy of the car.
* *
* @param moveStrategy Strategy to be implemented * @param moveStrategy Strategy to be implemented
*/ */
public void setMoveStrategy(MoveStrategy moveStrategy) { public void setMoveStrategy(MoveStrategy moveStrategy) {
this.moveStrategy = moveStrategy; this.moveStrategy = moveStrategy;
} }
/**
* Get current move strategy
*
* @return MoveStrategy
*/
public MoveStrategy getMoveStrategy() {
return this.moveStrategy;
}
} }

View File

@ -6,6 +6,7 @@ import ch.zhaw.pm2.racetrack.strategy.*;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import static ch.zhaw.pm2.racetrack.PositionVector.Direction; import static ch.zhaw.pm2.racetrack.PositionVector.Direction;
@ -19,34 +20,29 @@ public class Game implements GameSpecification {
public static final int NO_WINNER = -1; public static final int NO_WINNER = -1;
private Track track; private Track track;
private int currentCarIndex; private int currentCarIndex;
private final Config config;
private final UserInterface userInterface;
UserInterface userInterface; public Game(UserInterface userInterface, Config config) {
public Game(UserInterface userInterface) {
this.userInterface = userInterface; this.userInterface = userInterface;
this.config = config;
} }
/** /**
* This method will initialize the game. Therefore it interacts with the user via UserInterface * This method will initialize the game. Therefore, it interacts with the user via UserInterface
*
* @return true if the initialization is completed. Returns false if there is an error. * @return true if the initialization is completed. Returns false if there is an error.
*/ */
public boolean initPhase() { public boolean initPhase() {
File folder = new File("tracks"); if (config.getTrackDirectory().listFiles().length > 0) {
File[] listOfFiles = folder.listFiles(); File selectedTrack = config.getTrackDirectory().listFiles()[userInterface.selectOption("Select Track file", Arrays.asList(config.getTrackDirectory().list()))];
if (listOfFiles.length > 0) {
List<String> tracks = new ArrayList<>();
for (File file : listOfFiles) {
tracks.add(file.getName());
}
File selectedTrack = listOfFiles[userInterface.selectOption("Select Track file", tracks)];
try { try {
selectTrack(selectedTrack); selectTrack(selectedTrack);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
userInterface.printInformation("There is an unexpected Error with the trackfile Path. Add trackfiles only to tracks path. Exit the Game and Fix the Problem"); userInterface.printInformation("There is an unexpected Error with the track file Path. Add track files only to tracks path. Exit the Game and Fix the Problem");
return false; return false;
} catch (InvalidTrackFormatException e) { } catch (InvalidTrackFormatException e) {
userInterface.printInformation("There is an unexpected Error with the trackfile. Format does not match specifications! Exit the Game and Fix the Problem"); userInterface.printInformation("There is an unexpected Error with the track file. Format does not match specifications! Exit the Game and Fix the Problem");
return false; return false;
} }
List<String> moveStrategies = new ArrayList<>(); List<String> moveStrategies = new ArrayList<>();
@ -55,60 +51,102 @@ public class Game implements GameSpecification {
moveStrategies.add("Move List Strategy"); moveStrategies.add("Move List Strategy");
moveStrategies.add("Path Follow Move Strategy"); moveStrategies.add("Path Follow Move Strategy");
moveStrategies.add("Path Finder Move Strategy"); moveStrategies.add("Path Finder Move Strategy");
for (int i = 0; i < track.getCarCount(); i++) { for (int carIndex = 0; carIndex < track.getCarCount(); carIndex++) {
Car car = track.getCar(i); Car car = track.getCar(carIndex);
MoveStrategy moveStrategy = null; MoveStrategy moveStrategy = null;
while (moveStrategy == null) { while (moveStrategy == null) {
String filePath;
int moveStrategie = userInterface.selectOption( int moveStrategie = userInterface.selectOption(
"Select Strategy for Car " + i + " (" + track.getCarId(i) + ")", moveStrategies); "Select Strategy for Car " + carIndex + " (" + track.getCarId(carIndex) + ")", moveStrategies);
switch (moveStrategie) { switch (moveStrategie) {
case 0: case 0 -> moveStrategy = new DoNotMoveStrategy();
moveStrategy = new DoNotMoveStrategy(); case 1 -> moveStrategy = new UserMoveStrategy(userInterface, carIndex, track.getCarId(carIndex));
break; case 2 -> moveStrategy = getMoveListStrategy(selectedTrack, carIndex);
case 1: case 3 -> moveStrategy = getPathFollowerMoveStrategy(selectedTrack, carIndex);
moveStrategy = new UserMoveStrategy(userInterface, i, track.getCarId(i)); case 4 -> moveStrategy = new PathFinderMoveStrategy(track, carIndex);
break;
case 2:
filePath = ".\\moves\\" + selectedTrack.getName().split("\\.")[0] + "-car-" + track.getCar(i).getID() + ".txt";
try {
moveStrategy = new MoveListStrategy(filePath);
} catch (FileNotFoundException e) {
userInterface.printInformation("There is no Move-List implemented. Choose another Strategy!");
}
break;
case 3:
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;
case 4:
moveStrategy = new PathFinderMoveStrategy(track, i);
break;
} }
} }
selectMoveStrategy(car, moveStrategy); selectMoveStrategy(car, moveStrategy);
} }
return true; return true;
} else { } else {
userInterface.printInformation("No Trackfile found!"); userInterface.printInformation("No track file found!");
return false; return false;
} }
} }
/** /**
* The functionality was taken out of init to automate testing * creates a PathFollowerMoveStrategy for a specific track and a specific car
* @param selectedTrack * @param selectedTrack the selected Track
* @param carIndex the Index of the car
* @return the created Movestrategy, null if File not found or invalid
*/ */
Track selectTrack(File selectedTrack) throws InvalidTrackFormatException,FileNotFoundException { private MoveStrategy getPathFollowerMoveStrategy(File selectedTrack, int carIndex) {
File selectedFile = null;
MoveStrategy moveStrategy = null;
for (File file : config.getFollowerDirectory().listFiles()) {
if (file.toString().equals(config.getFollowerDirectory().toString() + "\\" + selectedTrack.getName().split("\\.")[0] + "_points.txt")) {
selectedFile = file;
}
}
if (selectedFile != null) {
try {
moveStrategy = new PathFollowerMoveStrategy(selectedFile, track.getCarPos(carIndex));
} catch (FileNotFoundException e) {
e.printStackTrace();
userInterface.printInformation("There is no Point-List implemented. Choose another Strategy!");
} catch (InvalidFileFormatException e) {
e.printStackTrace();
userInterface.printInformation("Invalid Point-List format. Change Strategy and clean Point-List");
}
} else {
userInterface.printInformation("There is no Point-List implemented. Choose another Strategy!");
}
return moveStrategy;
}
/**
* creates a MoveListStrategy for a specific track and a specific car
* @param selectedTrack the selected Track
* @param carIndex the Index of the car
* @return the created Movestrategy, null if File not found or invalid
*/
private MoveStrategy getMoveListStrategy(File selectedTrack, int carIndex) {
File selectedFile = null;
MoveStrategy moveStrategy = null;
for (File file : config.getMoveDirectory().listFiles()) {
if (file.toString().equals(config.getMoveDirectory().toString() + "\\" + selectedTrack.getName().split("\\.")[0] + "-car-" + track.getCar(carIndex).getID() + ".txt")) {
selectedFile = file;
}
}
if (selectedFile != null) {
try {
moveStrategy = new MoveListStrategy(selectedFile);
} catch (FileNotFoundException e) {
userInterface.printInformation("There is no Move-List implemented. Choose another Strategy!");
e.printStackTrace();
} catch (InvalidFileFormatException e) {
userInterface.printInformation("Invalid Data in Move-List. Choose another Strategy and clean the File");
e.printStackTrace();
}
} else {
userInterface.printInformation("There is no Move-List implemented. Choose another Strategy!");
}
return moveStrategy;
}
/**
* Selects the desired track and returns it.
* The functionality was taken out of init to automate testing.
*
* @param selectedTrack the Track which was selected by user
*/
Track selectTrack(File selectedTrack) throws InvalidTrackFormatException, FileNotFoundException {
track = new Track(selectedTrack); track = new Track(selectedTrack);
return track; return track;
} }
/** /**
* Sets a desired move strategy for a desired car.
* The functionality was taken out of init to automate testing * The functionality was taken out of init to automate testing
* *
* @param car to set the MoveStrategy * @param car to set the MoveStrategy
@ -130,7 +168,7 @@ public class Game implements GameSpecification {
} }
/** /**
* Get the id of the specified car. * Gets the id of the specified car.
* *
* @param carIndex The zero-based carIndex number * @param carIndex The zero-based carIndex number
* @return A char containing the id of the car * @return A char containing the id of the car
@ -203,7 +241,7 @@ public class Game implements GameSpecification {
* <p>The calling method must check the winner state and decide how to go on. If the winner is different * <p>The calling method must check the winner state and decide how to go on. If the winner is different
* than {@link Game#NO_WINNER}, or the current car is already marked as crashed the method returns immediately.</p> * than {@link Game#NO_WINNER}, or the current car is already marked as crashed the method returns immediately.</p>
* *
* @param acceleration A Direction containing the current cars acceleration vector (-1,0,1) in x and y direction * @param acceleration A Direction containing the current car's acceleration vector (-1,0,1) in x and y direction
* for this turn * for this turn
*/ */
@Override @Override
@ -222,9 +260,9 @@ public class Game implements GameSpecification {
} }
} }
int newWinPoints = track.calculateNewWinPoints(track.getCarPos(currentCarIndex), track.getCar(currentCarIndex).nextPosition()); int newWinPoints = track.calculateNewWinPoints(track.getCarPos(currentCarIndex), track.getCar(currentCarIndex).nextPosition());
if(newWinPoints == 1){ if (newWinPoints == 1) {
track.getCar(currentCarIndex).increaseWinPoints(); track.getCar(currentCarIndex).increaseWinPoints();
}else if(newWinPoints == -1){ } else if (newWinPoints == -1) {
track.getCar(currentCarIndex).deductWinPoints(); track.getCar(currentCarIndex).deductWinPoints();
} }
if (crashPosition != null) { if (crashPosition != null) {
@ -235,22 +273,31 @@ public class Game implements GameSpecification {
} }
/** /**
* This method implements the gameflow in a while loop. If there is a winner. The method will return its carid. * This method is in charge of changing the players and checking if a winner is found if there is no winner null will be returned.
* If a winner is found the game will return the char of the winning player.
* *
* @return the ID of the winning car return null if there is no winner. * @return the ID of the winning car return null if there is no winner.
*/ */
public String gamePhase() { public String gamePhase() {
while (carsMoving() && getWinner() == NO_WINNER) { boolean quit = false;
while (carsMoving() && getWinner() == NO_WINNER && !quit) {
userInterface.printInformation("Player " + track.getCar(currentCarIndex).getID() + "'s turn!");
userInterface.printTrack(track); userInterface.printTrack(track);
Direction direction; Direction direction;
direction = track.getCar(currentCarIndex).getMoveStrategy().nextMove(); direction = track.getCar(currentCarIndex).getMoveStrategy().nextMove();
if (direction == null) { if (direction == null) {
if (track.getCar(currentCarIndex).getMoveStrategy() instanceof UserMoveStrategy) {
quit = true;
direction = Direction.NONE;
} else {
track.getCar(currentCarIndex).setMoveStrategy(new DoNotMoveStrategy()); track.getCar(currentCarIndex).setMoveStrategy(new DoNotMoveStrategy());
direction = track.getCar(currentCarIndex).getMoveStrategy().nextMove(); direction = track.getCar(currentCarIndex).getMoveStrategy().nextMove();
} }
}
doCarTurn(direction); doCarTurn(direction);
switchToNextActiveCar(); switchToNextActiveCar();
} }
userInterface.printInformation("The game has finished!");
userInterface.printTrack(track); userInterface.printTrack(track);
int indexWinner = getWinner(); int indexWinner = getWinner();
if (indexWinner == NO_WINNER) { if (indexWinner == NO_WINNER) {
@ -271,7 +318,6 @@ public class Game implements GameSpecification {
currentCarIndex++; currentCarIndex++;
} }
} while (track.getCar(currentCarIndex).isCrashed()); } while (track.getCar(currentCarIndex).isCrashed());
// TODO: evtl andere Kapselung
} }
/** /**
@ -292,51 +338,6 @@ public class Game implements GameSpecification {
return track.calculatePointsOnPath(startPosition, endPosition); return track.calculatePointsOnPath(startPosition, endPosition);
} }
/**
* This method will check if a car is passing the finishline.
* If the car is passing the finishline in the wrong direction, the car will lose a winpoint.
* If the car is passing the finishline in the correct direction, the car will gain a winpoint.
* @param start the startposition of the car
* @param finish the expected finishpositon of the car after the move
*/
private void calculateWinner(PositionVector start, PositionVector finish, int carIndex) {
List<PositionVector> path = calculatePath(start, finish);
for (PositionVector point : path) {
switch (track.getSpaceType(point)) {
case FINISH_UP:
if (start.getY() < finish.getY()) {
track.getCar(carIndex).increaseWinPoints();
} else if (start.getY() > finish.getY()) {
track.getCar(carIndex).deductWinPoints();
}
break;
case FINISH_DOWN:
if (start.getY() > finish.getY()) {
track.getCar(carIndex).increaseWinPoints();
} else if (start.getY() < finish.getY()) {
track.getCar(carIndex).deductWinPoints();
}
break;
case FINISH_RIGHT:
if (start.getX() < finish.getX()) {
track.getCar(carIndex).increaseWinPoints();
} else if (start.getX() > finish.getX()) {
track.getCar(carIndex).deductWinPoints();
}
break;
case FINISH_LEFT:
if (start.getX() > finish.getX()) {
track.getCar(carIndex).increaseWinPoints();
} else if (start.getX() < finish.getX()) {
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. * Does indicate if a car would have a crash with a WALL space or another car at the given position.
* *
@ -349,6 +350,11 @@ public class Game implements GameSpecification {
return track.willCrashAtPosition(carIndex, position); return track.willCrashAtPosition(carIndex, position);
} }
/**
* Checks if there is just one player left in the game being able to make moves.
*
* @return true if there is just one car left false there are more than one car left in game
*/
public boolean onlyOneCarLeft() { public boolean onlyOneCarLeft() {
int carsLeft = 0; int carsLeft = 0;
for (int carIndex = 0; carIndex < track.getCarCount(); carIndex++) { for (int carIndex = 0; carIndex < track.getCarCount(); carIndex++) {
@ -359,6 +365,11 @@ public class Game implements GameSpecification {
return !(carsLeft > 1); return !(carsLeft > 1);
} }
/**
* Checks if all cars have implemented the do not move strategy, so the game can determine it will end in a draw.
*
* @return true if all players have implemented do not move false otherwise
*/
public boolean carsMoving() { public boolean carsMoving() {
for (int carIndex = 0; carIndex < track.getCarCount(); carIndex++) { for (int carIndex = 0; carIndex < track.getCarCount(); carIndex++) {
if (!(track.getCar(carIndex).isCrashed() || track.getCar(carIndex).getMoveStrategy().getClass() == DoNotMoveStrategy.class)) { if (!(track.getCar(carIndex).isCrashed() || track.getCar(carIndex).getMoveStrategy().getClass() == DoNotMoveStrategy.class)) {

View File

@ -1,10 +1,18 @@
package ch.zhaw.pm2.racetrack; package ch.zhaw.pm2.racetrack;
/** /**
* Class for Exception when invalid Fileformat is used. * Class for Exception when an invalid file format is used.
*/ */
public class InvalidFileFormatException extends Exception { public class InvalidFileFormatException extends Exception {
public InvalidFileFormatException(){super();} public InvalidFileFormatException() {
super();
}
/**
* Constructor that is used when an error message is given with the exception.
*
* @param errorMessage is the message to be displayed
*/
public InvalidFileFormatException(String errorMessage) { public InvalidFileFormatException(String errorMessage) {
super(errorMessage); super(errorMessage);
} }

View File

@ -4,10 +4,17 @@ package ch.zhaw.pm2.racetrack;
* Class for Exception when invalid track format is used. * Class for Exception when invalid track format is used.
*/ */
public class InvalidTrackFormatException extends Exception { public class InvalidTrackFormatException extends Exception {
public InvalidTrackFormatException(String errorMessage) {
super(errorMessage);
}
public InvalidTrackFormatException() { public InvalidTrackFormatException() {
super(); super();
} }
/**
* Constructor that is used when an error message is given with the exception.
*
* @param errorMessage is the message to be displayed
*/
public InvalidTrackFormatException(String errorMessage) {
super(errorMessage);
}
} }

View File

@ -3,12 +3,24 @@ package ch.zhaw.pm2.racetrack;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/**
* Class containing the main method
*
* @author Roman Schenk
*/
public class Main { public class Main {
/**
* Method creating the game and initializing the Configs and Userinterface.
* Starts and initializes a new game until the user decides to stop the game.
* In charge of closing the user interface when user decides to stop the game
*
* @param args no arguments needed
*/
public static void main(String[] args) { public static void main(String[] args) {
UserInterface userInterface = new UserInterface("Hello and Welcome to Racetrack by Team02-\"AngryNerds\""); UserInterface userInterface = new UserInterface("Hello and Welcome to Racetrack by Team02-\"AngryNerds\"");
Config config = new Config();
while (true) { while (true) {
Game game = new Game(userInterface); Game game = new Game(userInterface, config);
String winner; String winner;
if (game.initPhase()) { if (game.initPhase()) {
winner = game.gamePhase(); winner = game.gamePhase();

View File

@ -7,8 +7,8 @@ package ch.zhaw.pm2.racetrack;
* Created by mach 21.01.2020 * Created by mach 21.01.2020
*/ */
public final class PositionVector { public final class PositionVector {
private final int x; // horizontal component (position / velocity) private int x; // horizontal component (position / velocity)
private final int y; // vertical component (position / velocity) private int y; // vertical component (position / velocity)
/** /**
* Enum representing a direction on the track grid. * Enum representing a direction on the track grid.

View File

@ -45,9 +45,9 @@ import java.util.Scanner;
* There are 1 to {@link Config#MAX_CARS} allowed. </li> * There are 1 to {@link Config#MAX_CARS} allowed. </li>
* </ul> * </ul>
* *
* <p>All lines must have the same length, used to initialize the grid width). * <p>All lines must have the same length, used to initialize the grid width.
* Beginning empty lines are skipped. * Beginning empty lines are skipped.
* The the tracks ends with the first empty line or the file end.<br> * The tracks ends with the first empty line or the file end.<br>
* An {@link InvalidTrackFormatException} is thrown, if * An {@link InvalidTrackFormatException} is thrown, if
* <ul> * <ul>
* <li>not all track lines have the same length</li> * <li>not all track lines have the same length</li>
@ -55,7 +55,7 @@ import java.util.Scanner;
* <li>the file contains more than {@link Config#MAX_CARS} cars</li> * <li>the file contains more than {@link Config#MAX_CARS} cars</li>
* </ul> * </ul>
* *
* <p>The Track can return a String representing the current state of the race (including car positons)</p> * <p>The Track can return a String representing the current state of the race (including car positions)</p>
*/ */
public class Track implements TrackSpecification { public class Track implements TrackSpecification {
@ -67,10 +67,11 @@ public class Track implements TrackSpecification {
/** /**
* Initializes the Track from the given track File including the cars. * Initializes the Track from the given track File including the cars.
* Throws an corresponding error if one of the conditions are not met to build a track. * Throws a corresponding error if one of the conditions are not met to build a track.
*
* @param trackFile Reference to a file containing the track data * @param trackFile Reference to a file containing the track data
* @throws FileNotFoundException if the given track file could not be found * @throws FileNotFoundException if the given track file could not be found
* @throws InvalidTrackFormatException if the track file contains invalid data (no tracklines, ...) * @throws InvalidTrackFormatException if the track file contains invalid data (no track lines, ...)
*/ */
public Track(File trackFile) throws FileNotFoundException, InvalidTrackFormatException { public Track(File trackFile) throws FileNotFoundException, InvalidTrackFormatException {
track = new ArrayList<>(); track = new ArrayList<>();
@ -95,7 +96,7 @@ public class Track implements TrackSpecification {
} }
/** /**
* Goes through the track ArrayList and determines the locations of each cars and initializes them at the location. * Goes through the track ArrayList and determines the locations of each car and initializes them at the location.
* *
* @throws InvalidTrackFormatException is thrown if a car is found more than once inside the track. * @throws InvalidTrackFormatException is thrown if a car is found more than once inside the track.
*/ */
@ -114,16 +115,14 @@ public class Track implements TrackSpecification {
char possibleCarChar = line.charAt(xPosition); char possibleCarChar = line.charAt(xPosition);
if (!allSpaceTypesAsChar.contains(possibleCarChar)) { if (!allSpaceTypesAsChar.contains(possibleCarChar)) {
if (usedSymbolForCar.contains(possibleCarChar)) { if (usedSymbolForCar.contains(possibleCarChar)) {
throw new InvalidTrackFormatException(); throw new InvalidTrackFormatException("More than one car on track!");
} }
usedSymbolForCar.add(possibleCarChar); usedSymbolForCar.add(possibleCarChar);
cars.add(new Car(possibleCarChar, new PositionVector(xPosition, yPosition))); cars.add(new Car(possibleCarChar, new PositionVector(xPosition, yPosition)));
} }
} }
} }
} }
//TODO: THIS
/** /**
* Determines the finish line and saves it in a list, throws an Exception if none is found. * Determines the finish line and saves it in a list, throws an Exception if none is found.
@ -143,12 +142,12 @@ public class Track implements TrackSpecification {
} }
} }
if (finishLine.size() == 0) { if (finishLine.size() == 0) {
throw new InvalidTrackFormatException(); throw new InvalidTrackFormatException("No finish line found!");
} }
finishTyp = getSpaceType(finishLine.get(0)); finishTyp = getSpaceType(finishLine.get(0));
for (PositionVector positionVector : finishLine) { for (PositionVector positionVector : finishLine) {
if (getSpaceType(positionVector) != finishTyp) { if (getSpaceType(positionVector) != finishTyp) {
throw new InvalidTrackFormatException(); throw new InvalidTrackFormatException("Inconsistent finish line found!");
} }
} }
} }
@ -186,7 +185,7 @@ public class Track implements TrackSpecification {
} }
/** /**
* Method that returns the finishline as a List * Method that returns the finish line as a List
* *
* @return finishLine List * @return finishLine List
*/ */
@ -225,7 +224,7 @@ public class Track implements TrackSpecification {
//Removes the Car at Current Pos //Removes the Car at Current Pos
drawCharOnTrackIndicator(carPositionVector, ConfigSpecification.SpaceType.TRACK.getValue()); drawCharOnTrackIndicator(carPositionVector, ConfigSpecification.SpaceType.TRACK.getValue());
//Redraw finishline if Car was on finish-line Position //Redraw finish line if Car was on finish-line Position
for (PositionVector finishLinePositionVector : finishLine) { for (PositionVector finishLinePositionVector : finishLine) {
if (finishLinePositionVector.equals(carPositionVector)) { if (finishLinePositionVector.equals(carPositionVector)) {
drawCharOnTrackIndicator(carPositionVector, finishTyp.getValue()); drawCharOnTrackIndicator(carPositionVector, finishTyp.getValue());
@ -348,7 +347,7 @@ public class Track implements TrackSpecification {
* If there is a crashed car at the position, {@link #CRASH_INDICATOR} is returned. * If there is a crashed car at the position, {@link #CRASH_INDICATOR} is returned.
* *
* @param y position Y-value * @param y position Y-value
* @param x position X-vlaue * @param x position X-value
* @param currentSpace char to return if no car is at position (x,y) * @param currentSpace char to return if no car is at position (x,y)
* @return character representing position (x,y) on the track * @return character representing position (x,y) on the track
*/ */
@ -378,11 +377,11 @@ public class Track implements TrackSpecification {
int x = startPosition.getX(); int x = startPosition.getX();
int y = startPosition.getY(); int y = startPosition.getY();
// Relative Distance (x & y axis) between end- and starting position // Relative Distance (x & y-axis) between end- and starting position
int diffX = endPosition.getX() - startPosition.getX(); int diffX = endPosition.getX() - startPosition.getX();
int diffY = endPosition.getY() - startPosition.getY(); int diffY = endPosition.getY() - startPosition.getY();
// Absolute distance (x & y axis) between end- and starting position // Absolute distance (x & y-axis) between end- and starting position
int distX = Math.abs(diffX); int distX = Math.abs(diffX);
int distY = Math.abs(diffY); int distY = Math.abs(diffY);
@ -395,7 +394,7 @@ public class Track implements TrackSpecification {
int diagonalStepX, diagonalStepY; int diagonalStepX, diagonalStepY;
int distanceSlowAxis, distanceFastAxis; int distanceSlowAxis, distanceFastAxis;
if (distX > distY) { if (distX > distY) {
// x axis is the 'fast' direction // x-axis is the 'fast' direction
parallelStepX = dirX; parallelStepX = dirX;
parallelStepY = 0; // parallel step only moves in x direction parallelStepY = 0; // parallel step only moves in x direction
diagonalStepX = dirX; diagonalStepX = dirX;
@ -403,7 +402,7 @@ public class Track implements TrackSpecification {
distanceSlowAxis = distY; distanceSlowAxis = distY;
distanceFastAxis = distX; distanceFastAxis = distX;
} else { } else {
// y axis is the 'fast' direction // y-axis is the 'fast' direction
parallelStepX = 0; parallelStepX = 0;
parallelStepY = dirY; // parallel step only moves in y direction parallelStepY = dirY; // parallel step only moves in y direction
diagonalStepX = dirX; diagonalStepX = dirX;
@ -432,12 +431,13 @@ public class Track implements TrackSpecification {
} }
/** /**
* This method will check if a car is passing the finishline. * This method will check if a car is passing the finish line.
* If the car is passing the finishline in the wrong direction, the car will lose a winpoint. * If the car is passing the finish line in the wrong direction, the car will lose a winpoint.
* If the car is passing the finishline in the correct direction, the car will gain a winpoint. * If the car is passing the finish line in the correct direction, the car will gain a winpoint.
* @param start the startposition of the car *
* @param finish the expected finishpositon of the car after the move * @param start the start position of the car
* @return Number of new Winpoints for the current player. * @param finish the expected finish position of the car after the move
* @return Number of new winpoints for the current player.
*/ */
public int calculateNewWinPoints(PositionVector start, PositionVector finish) { public int calculateNewWinPoints(PositionVector start, PositionVector finish) {
List<PositionVector> path = calculatePointsOnPath(start, finish); List<PositionVector> path = calculatePointsOnPath(start, finish);
@ -472,8 +472,6 @@ public class Track implements TrackSpecification {
} }
break; break;
} }
} }
return 0; return 0;
} }

View File

@ -8,7 +8,7 @@ import java.util.List;
/** /**
* Class representing the Userinterface. * Class representing the Userinterface.
* Used to get inputs from users via textio. * Used to get inputs from users via text io.
* *
* @author Roman Schenk * @author Roman Schenk
*/ */

View File

@ -5,10 +5,15 @@ import ch.zhaw.pm2.racetrack.PositionVector;
import static ch.zhaw.pm2.racetrack.PositionVector.Direction; import static ch.zhaw.pm2.racetrack.PositionVector.Direction;
/** /**
* Do not accelerate in any direction. * This Class represents the DoNotMoveStrategy.
*/ */
public class DoNotMoveStrategy implements MoveStrategy { public class DoNotMoveStrategy implements MoveStrategy {
/**
* This method will be used to return the next Direction for the car.
*
* @return a NONE Direction
*/
@Override @Override
public Direction nextMove() { public Direction nextMove() {
return PositionVector.Direction.NONE; return PositionVector.Direction.NONE;

View File

@ -1,5 +1,6 @@
package ch.zhaw.pm2.racetrack.strategy; package ch.zhaw.pm2.racetrack.strategy;
import ch.zhaw.pm2.racetrack.InvalidFileFormatException;
import ch.zhaw.pm2.racetrack.PositionVector.Direction; import ch.zhaw.pm2.racetrack.PositionVector.Direction;
import java.io.File; import java.io.File;
@ -10,31 +11,52 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Scanner; import java.util.Scanner;
/**
* This Class represent the MoveListStrategy. The Directions returned by the
* nextMove() are written down in a List.
*/
public class MoveListStrategy implements MoveStrategy { public class MoveListStrategy implements MoveStrategy {
private final List<Direction> moveList; private final List<Direction> moveList;
private int pointer; private int pointer;
public MoveListStrategy(String path) throws FileNotFoundException{ public MoveListStrategy(File moveListFile) throws FileNotFoundException, InvalidFileFormatException {
moveList = new ArrayList<>(); moveList = new ArrayList<>();
pointer = -1; pointer = -1;
readFile(new File(path)); readFile(moveListFile);
} }
private void readFile(File trackFile) throws FileNotFoundException { /**
* This Method will read in a File and checks line by line if the file contains a valid direction.
* If so the direction will be added to the moveList.
*
* @param trackFile the file to read in the directions
* @throws FileNotFoundException if the file does not exist.
*/
private void readFile(File trackFile) throws FileNotFoundException, InvalidFileFormatException {
Scanner scanner = new Scanner(new FileInputStream(trackFile), StandardCharsets.UTF_8); Scanner scanner = new Scanner(new FileInputStream(trackFile), StandardCharsets.UTF_8);
Direction[] directions = Direction.values(); Direction[] directions = Direction.values();
while (scanner.hasNextLine()) { while (scanner.hasNextLine()) {
boolean validLine = false;
String line = scanner.nextLine(); String line = scanner.nextLine();
for (Direction direction : directions) { for (Direction direction : directions) {
if (direction.toString().equals(line)) { if (direction.toString().equals(line)) {
moveList.add(direction); moveList.add(direction);
validLine = true;
break; break;
} }
} }
if (!(validLine || line.equals(""))) {
throw new InvalidFileFormatException("The File contains invalid data!");
}
} }
} }
/**
* This method will be used to return the next Direction for the car.
*
* @return the next direction from the list. Returns null if the list is empty.
*/
@Override @Override
public Direction nextMove() { public Direction nextMove() {
pointer += 1; pointer += 1;

View File

@ -4,45 +4,67 @@ import ch.zhaw.pm2.racetrack.PositionVector;
import ch.zhaw.pm2.racetrack.Track; import ch.zhaw.pm2.racetrack.Track;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
public class PathFinderMoveStrategy implements MoveStrategy{ /**
private Track track; * Strategy which calculates the path automatically
private int carIndex; */
public class PathFinderMoveStrategy implements MoveStrategy {
private final Track track;
private final int carIndex;
private List<PositionVector.Direction> moveList; private List<PositionVector.Direction> moveList;
// the index of the next move in moveList
private int pointer; private int pointer;
private List<PositionVector.Direction> allDirections; // all Directions which can be used for acceleration
private final PositionVector.Direction[] allDirections;
// List of all States (combination of Position and Velocity) which are already reached with a calculated move.
private List<State> calculatedStates; private List<State> calculatedStates;
/**
* Constructor which initialises the needed variables and creates the MoveList
*
* @param track track instance of the game
* @param carIndex index of the car which owns this moveStrategy
*/
public PathFinderMoveStrategy(Track track, int carIndex) { public PathFinderMoveStrategy(Track track, int carIndex) {
this.track = track; this.track = track;
this.carIndex = carIndex; this.carIndex = carIndex;
allDirections = Arrays.asList(PositionVector.Direction.values()); allDirections = PositionVector.Direction.values();
createMoveList(); createMoveList();
} }
private void createMoveList(){ /**
pointer = -1; * Method to create a working moveList
*/
private void createMoveList() {
// if Movelist is recreated next move will be the first move in moveList
pointer = 0;
calculatedStates = new ArrayList<>(); calculatedStates = new ArrayList<>();
PossibleMove finishedMove = null; PossibleMove finishedMove = null;
List<PossibleMove> possibleMoves= new ArrayList<>(); List<PossibleMove> possibleMoves = new ArrayList<>();
for(PositionVector.Direction direction : allDirections){
// create a PossibleMove object for each direction which doesn't end with a crash.
for (PositionVector.Direction direction : allDirections) {
PossibleMove newMove = new PossibleMove(null, direction); PossibleMove newMove = new PossibleMove(null, direction);
if(! newMove.crashed()){ if (!newMove.crashed()) {
possibleMoves.add(newMove); possibleMoves.add(newMove);
} }
} }
while(finishedMove == null){
// while no PossibleMove crosses the finish line
// every PossibleMove will be accelerated in each direction to find a Move which finishes.
while (finishedMove == null) {
List<PossibleMove> newMoves = new ArrayList<>(); List<PossibleMove> newMoves = new ArrayList<>();
for(PossibleMove previousMove : possibleMoves){ for (PossibleMove previousMove : possibleMoves) {
for(PositionVector.Direction direction : allDirections){ for (PositionVector.Direction direction : allDirections) {
PossibleMove newMove = new PossibleMove(previousMove, direction); PossibleMove newMove = new PossibleMove(previousMove, direction);
State newState = new State(newMove.endPosition, newMove.endVelocity); State newState = new State(newMove.endPosition, newMove.endVelocity);
if(! (newMove.crashed() || alreadyCalculated(newState) || finishedMove != null)){ //only use the new created Possible Move if it doesn't crash, end State isn't in List of calculatedStates
if(newMove.finished()){ // and if there is no move found yet which is finished.
if (!(newMove.crashed() || alreadyCalculated(newState) || finishedMove != null)) {
if (newMove.finished()) {
finishedMove = newMove; finishedMove = newMove;
} else { } else {
calculatedStates.add(newState); calculatedStates.add(newState);
@ -55,112 +77,144 @@ public class PathFinderMoveStrategy implements MoveStrategy{
} }
// if a finished Move is found save its directions as moveList
moveList = finishedMove.directions; moveList = finishedMove.directions;
} }
private boolean alreadyCalculated(State state){ /**
for(State calculatedState: calculatedStates){ * Method to check if a State is already in List calculatedStates
if(state.equals(calculatedState)){ *
* @param state the State which should be checked
* @return true if it is in List, false if it isn't in List
*/
private boolean alreadyCalculated(State state) {
for (State calculatedState : calculatedStates) {
if (state.equals(calculatedState)) {
return true; return true;
} }
} }
return false; return false;
} }
public class State{ /**
PositionVector position; * Combination of position and velocity
PositionVector velocity; */
public static class State {
final PositionVector position;
final PositionVector velocity;
public State(PositionVector position, PositionVector velocity){ /**
* Constructor of State
*
* @param position the PositionVector object with coordinates of the Position
* @param velocity the PositionVector object with coordinates of the Velocity
*/
public State(PositionVector position, PositionVector velocity) {
this.position = position; this.position = position;
this.velocity = velocity; this.velocity = velocity;
} }
public boolean equals(State compareState){ /**
if(compareState.position.equals(position) && compareState.velocity.equals(velocity)){ * Checks if a state has the same Position and the same Velocity
return true; *
} else{ * @param compareState the State object to compare
return false; * @return true if it is equal, false if it is not equal
} */
public boolean equals(State compareState) {
return compareState.position.equals(position) && compareState.velocity.equals(velocity);
} }
} }
/**
* PossibleMove represents a move which can be done by the player.
*/
public class PossibleMove { public class PossibleMove {
List<PositionVector.Direction> directions; // List of all directions used for the previous moves and the direction of the current move (the highest Index).
PositionVector startPosition; final List<PositionVector.Direction> directions;
PositionVector endPosition; // Position of the car bevor the move is executed
PositionVector endVelocity; final PositionVector startPosition;
// Position of the car after the move is executed
final PositionVector endPosition;
// Velocity of the car after the move is executed
final PositionVector endVelocity;
/**
public PossibleMove(PossibleMove previousMove, PositionVector.Direction nextDirection){ * Constructor of PossibleMove
*
* @param previousMove The move which must be executed bevor this move can be executed
* @param nextDirection The direction of the move
*/
public PossibleMove(PossibleMove previousMove, PositionVector.Direction nextDirection) {
// Velocity of the car bevor the move is executed
PositionVector startVelocity; PositionVector startVelocity;
directions = new ArrayList<>(); directions = new ArrayList<>();
if(previousMove != null){ // check if there was a previousMove.
directions.addAll(previousMove.directions); if (previousMove != null) {
startPosition = previousMove.endPosition; directions.addAll(previousMove.directions); //copy the LIst of Directions from the previousMove
startVelocity = previousMove.endVelocity; startPosition = previousMove.endPosition; //use the endPosition from previousMove as startPosition
} startVelocity = previousMove.endVelocity; //use the endVelocity from previousMove as startVelocity
else { } else { //if there was no previousMove
startPosition = track.getCarPos(carIndex); startPosition = track.getCarPos(carIndex); //use the current Position of the car from track as startPosition
startVelocity = track.getCar(carIndex).getVelocity(); startVelocity = track.getCar(carIndex).getVelocity(); //use the current Velocity of the car from track as startVelocity
} }
directions.add(nextDirection); directions.add(nextDirection);
endVelocity = new PositionVector(startVelocity.getX() + nextDirection.vector.getX(), startVelocity.getY() + nextDirection.vector.getY()); endVelocity = new PositionVector(startVelocity.getX() + nextDirection.vector.getX(), startVelocity.getY() + nextDirection.vector.getY());
endPosition = new PositionVector(startPosition.getX() + endVelocity.getX(), startPosition.getY() + endVelocity.getY()); endPosition = new PositionVector(startPosition.getX() + endVelocity.getX(), startPosition.getY() + endVelocity.getY());
} }
public boolean finished(){ /**
if(track.calculateNewWinPoints(startPosition, endPosition) == 1){ * check if the finish line is crossed (in correct direction) if this move is executed
return true; *
} * @return true if finish line will be crossed
else{ */
return false; public boolean finished() {
} return track.calculateNewWinPoints(startPosition, endPosition) == 1;
} }
/**
* checks if the car will crash or finishline will be crossed in wrong direction if this move is executed
*
* @return true if car will crash
*/
public boolean crashed() { public boolean crashed() {
List<PositionVector> points = track.calculatePointsOnPath(startPosition, endPosition); List<PositionVector> points = track.calculatePointsOnPath(startPosition, endPosition);
for(PositionVector point : points) { for (PositionVector point : points) {
if (track.willCrashAtPosition(carIndex, point)){ if (track.willCrashAtPosition(carIndex, point)) {
return true; return true;
} }
} }
if(track.calculateNewWinPoints(startPosition, endPosition) == -1){ return track.calculateNewWinPoints(startPosition, endPosition) == -1;
return true;
}
return false;
} }
} }
/**
* Checks if the next Move in moveList will crash. If no crash next move in moveList will be executed.
* If crash the moveList will be recreated.
*
* @return the direction of acceleration which should be executed.
*/
@Override @Override
public PositionVector.Direction nextMove() { //TODO check for crash and recreate movelist if crash public PositionVector.Direction nextMove() {
pointer += 1;
if (pointer < moveList.size()) { if (pointer < moveList.size()) {
PositionVector.Direction direction = moveList.get(pointer); PositionVector.Direction direction = moveList.get(pointer);
PositionVector currentVelocity = track.getCarVelocity(carIndex); PositionVector currentVelocity = track.getCarVelocity(carIndex);
PositionVector newVelocity = new PositionVector(currentVelocity.getX() + direction.vector.getX(), currentVelocity.getY() + direction.vector.getY()); PositionVector newVelocity = new PositionVector(currentVelocity.getX() + direction.vector.getX(), currentVelocity.getY() + direction.vector.getY());
PositionVector currentPosition = track.getCarPos(carIndex); PositionVector currentPosition = track.getCarPos(carIndex);
PositionVector newPosition = new PositionVector(currentPosition.getX() + newVelocity.getX(), currentPosition.getY() + newVelocity.getY()); PositionVector newPosition = new PositionVector(currentPosition.getX() + newVelocity.getX(), currentPosition.getY() + newVelocity.getY());
System.out.println("currentVelocity:" + currentVelocity.getX()+ ","+ currentVelocity.getY()); for (PositionVector point : track.calculatePointsOnPath(currentPosition, newPosition)) {
System.out.println("newVelocity:" + newVelocity.getX()+ ","+ newVelocity.getY()); if (track.willCrashAtPosition(carIndex, point)) {
for(PositionVector point : track.calculatePointsOnPath(currentPosition, newPosition)){
if(track.willCrashAtPosition(carIndex, point)){
createMoveList(); createMoveList();
pointer = 0; pointer = 0;
direction = moveList.get(pointer);
break; break;
} }
} }
pointer += 1;
return moveList.get(pointer); return direction;
} }
return null; return null;
} }

View File

@ -1,5 +1,6 @@
package ch.zhaw.pm2.racetrack.strategy; package ch.zhaw.pm2.racetrack.strategy;
import ch.zhaw.pm2.racetrack.InvalidFileFormatException;
import ch.zhaw.pm2.racetrack.PositionVector; import ch.zhaw.pm2.racetrack.PositionVector;
import ch.zhaw.pm2.racetrack.PositionVector.Direction; import ch.zhaw.pm2.racetrack.PositionVector.Direction;
@ -34,34 +35,41 @@ public class PathFollowerMoveStrategy implements MoveStrategy {
/** /**
* Constructor to create a new PathFollowerMoveStrategy for a car. * Constructor to create a new PathFollowerMoveStrategy for a car.
* @param path The location where the file is saved *
* @param trackFile The location where the file is saved
* @param startPosition The start position of the car * @param startPosition The start position of the car
* @throws FileNotFoundException If the file with the given path does not exist. * @throws FileNotFoundException If the file with the given path does not exist.
*/ */
public PathFollowerMoveStrategy(String path, PositionVector startPosition) throws FileNotFoundException { public PathFollowerMoveStrategy(File trackFile, PositionVector startPosition) throws FileNotFoundException, InvalidFileFormatException {
pointList = new ArrayList<>(); pointList = new ArrayList<>();
pointer = 0; pointer = 0;
readFile(new File(path)); readFile(trackFile);
currentPosition = startPosition; currentPosition = startPosition;
currentVelocity = new PositionVector(0, 0); currentVelocity = new PositionVector(0, 0);
} }
/** /**
* Method to read the given File and add the points to the pointList * Method to read the given File and add the points to the pointList
*
* @param trackFile the File Object which should be read * @param trackFile the File Object which should be read
* @throws FileNotFoundException If the file with the given path does not exist. * @throws FileNotFoundException If the file with the given path does not exist.
*/ */
public void readFile(File trackFile) throws FileNotFoundException { public void readFile(File trackFile) throws FileNotFoundException, InvalidFileFormatException {
Scanner scanner = new Scanner(new FileInputStream(trackFile), StandardCharsets.UTF_8); Scanner scanner = new Scanner(new FileInputStream(trackFile), StandardCharsets.UTF_8);
while (scanner.hasNextLine()) { while (scanner.hasNextLine()) {
String line = scanner.nextLine(); String line = scanner.nextLine();
String[] coordinates = line.split("(\\(X:|, Y:|\\))"); String[] coordinates = line.split("(\\(X:|, Y:|\\))");
try {
pointList.add(new PositionVector(Integer.parseInt(coordinates[1]), Integer.parseInt(coordinates[2]))); pointList.add(new PositionVector(Integer.parseInt(coordinates[1]), Integer.parseInt(coordinates[2])));
} catch (NumberFormatException e) {
throw new InvalidFileFormatException("Invalid File Format");
}
} }
} }
/** /**
* Method to select the direction for the next move. * 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. * @return The direction for the next move. null if there are no points left in the list.
*/ */
@Override @Override
@ -73,7 +81,7 @@ public class PathFollowerMoveStrategy implements MoveStrategy {
// increase pointer variable if the next point is reached. // increase pointer variable if the next point is reached.
if (pointList.get(pointer).equals(currentPosition)) { if (pointList.get(pointer).equals(currentPosition)) {
pointer ++; pointer++;
} }
// calculate Vector from current Position to next Point // calculate Vector from current Position to next Point
@ -81,31 +89,29 @@ public class PathFollowerMoveStrategy implements MoveStrategy {
// select acceleration for X // select acceleration for X
int accelerationX; int accelerationX;
if((movementVector.getX() == 0 && currentVelocity.getX() > 0) || //reduce velocity to 0 if the destination coordinate is reached 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()) || //increase velocity
(movementVector.getX() < 0 && movementVector.getX()/2.0 < currentVelocity.getX())){ //reduce velocity (movementVector.getX() < 0 && movementVector.getX() / 2.0 < currentVelocity.getX())) { //reduce velocity
accelerationX = -1; accelerationX = -1;
} else if((movementVector.getX() == 0 && currentVelocity.getX() < 0) || //reduce velocity to 0 if the destination coordinate is reached } 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()) || //increase velocity
(movementVector.getX() < 0 && movementVector.getX()/2.0 >= currentVelocity.getX())) { //reduce velocity (movementVector.getX() < 0 && movementVector.getX() / 2.0 >= currentVelocity.getX())) { //reduce velocity
accelerationX = 1; accelerationX = 1;
} } else { //no acceleration
else { //no acceleration
accelerationX = 0; accelerationX = 0;
} }
// select acceleration for Y // select acceleration for Y
int accelerationY; int accelerationY;
if((movementVector.getY() == 0 && currentVelocity.getY() > 0) || //reduce velocity to 0 if the destination coordinate is reached 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()) || //increase velocity
(movementVector.getY() < 0 && movementVector.getY()/2.0 < currentVelocity.getY())){ //reduce velocity (movementVector.getY() < 0 && movementVector.getY() / 2.0 < currentVelocity.getY())) { //reduce velocity
accelerationY = -1; accelerationY = -1;
} else if((movementVector.getY() == 0 && currentVelocity.getY() < 0) || //reduce velocity to 0 if the destination coordinate is reached } 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()) || //increase velocity
(movementVector.getY() < 0 && movementVector.getY()/2.0 >= currentVelocity.getY())) { //reduce velocity (movementVector.getY() < 0 && movementVector.getY() / 2.0 >= currentVelocity.getY())) { //reduce velocity
accelerationY = 1; accelerationY = 1;
} } else { //no acceleration
else { //no acceleration
accelerationY = 0; accelerationY = 0;
} }

View File

@ -5,6 +5,7 @@ import ch.zhaw.pm2.racetrack.UserInterface;
/** /**
* Let the user decide the next move. * Let the user decide the next move.
* Therefore, it uses the UserInterface class to ask for a direction.
*/ */
public class UserMoveStrategy implements MoveStrategy { public class UserMoveStrategy implements MoveStrategy {
private final UserInterface userInterface; private final UserInterface userInterface;
@ -17,6 +18,11 @@ public class UserMoveStrategy implements MoveStrategy {
this.carID = carID; this.carID = carID;
} }
/**
* Uses the interface to determine which move the user takes.
*
* @return the next taken move as Direction
*/
@Override @Override
public Direction nextMove() { public Direction nextMove() {
return userInterface.selectDirection(carIndex, carID); return userInterface.selectDirection(carIndex, carID);

View File

@ -6,33 +6,38 @@ import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
/** /**
* Tests for Class Car * Class that contains all test for the Class Car
*
* @author Roman Schenk
*/ */
class CarTest { class CarTest {
Car car; Car car;
// Default coordinates for tests // Default coordinates for tests
int DEFAULT_X = 10; final int DEFAULT_X = 10;
int DEFAULT_Y = 10; final int DEFAULT_Y = 10;
char DEFAULT_ID = 'f'; final char DEFAULT_ID = 'f';
/** /**
* Create a new Car Object and set Position to a defined Default Position * Creates a new Car Object and set Position to a defined Default Position
*/ */
@BeforeEach @BeforeEach
void setUp() { void setUp() {
car = new Car(DEFAULT_ID, new PositionVector(DEFAULT_X, DEFAULT_Y)); car = new Car(DEFAULT_ID, new PositionVector(DEFAULT_X, DEFAULT_Y));
} }
/**
* Checks if the method getID returns the correct value
*/
@Test @Test
void getID() { void getID() {
assertEquals(DEFAULT_ID, car.getID()); assertEquals(DEFAULT_ID, car.getID());
@ -49,8 +54,8 @@ class CarTest {
} }
/** /**
* - checks if the position of the car can be set and saved correctly with valid positions. * 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. * Checks if an exception is thrown and position kept unchanged if invalid coordinates are entered.
*/ */
@Test @Test
void setPosition() { void setPosition() {
@ -108,7 +113,6 @@ class CarTest {
checkNextPosition(DEFAULT_X, DEFAULT_Y); checkNextPosition(DEFAULT_X, DEFAULT_Y);
for (PositionVector.Direction direction1 : directions) { for (PositionVector.Direction direction1 : directions) {
for (PositionVector.Direction direction2 : directions) { for (PositionVector.Direction direction2 : directions) {
@ -119,7 +123,7 @@ class CarTest {
int expectedNextPosX = DEFAULT_X; int expectedNextPosX = DEFAULT_X;
int expectedNextPosY = DEFAULT_Y; int expectedNextPosY = DEFAULT_Y;
//variables to save the acutal expected result of method getVelocity //variables to save the actual expected result of method getVelocity
int expectedVelocityX = 0; int expectedVelocityX = 0;
int expectedVelocityY = 0; int expectedVelocityY = 0;
@ -157,7 +161,8 @@ class CarTest {
/** /**
* test for methods crash and isCrashed. checks if state crashed is set and returned correctly. * Test for methods crash and isCrashed.
* Checks if state crashed is set and returned correctly.
*/ */
@Test @Test
void crash() { void crash() {
@ -167,8 +172,8 @@ class CarTest {
} }
/** /**
* test for methods setMoveStrategy. Checks if the MoveStrategy Object is saved and returned correctly * Test for methods setMoveStrategy.
* with all Types of MoveStrategy. * Checks if the MoveStrategy Object is saved and returned correctly with all Types of MoveStrategy.
*/ */
@Test @Test
void MoveStrategy() { void MoveStrategy() {
@ -179,50 +184,50 @@ class CarTest {
assertEquals(moveStrategy, car.getMoveStrategy()); assertEquals(moveStrategy, car.getMoveStrategy());
try { try {
moveStrategy = new MoveListStrategy(".\\moves\\challenge-car-a.txt"); moveStrategy = new MoveListStrategy(new File(".\\moves\\challenge-car-a.txt"));
} catch (FileNotFoundException e) { } catch (FileNotFoundException | InvalidFileFormatException e) {
Assertions.fail(); Assertions.fail();
} }
car.setMoveStrategy(moveStrategy); car.setMoveStrategy(moveStrategy);
assertEquals(moveStrategy, car.getMoveStrategy()); assertEquals(moveStrategy, car.getMoveStrategy());
try { try {
moveStrategy = new PathFollowerMoveStrategy(".\\follower\\challenge_points.txt", new PositionVector(0, 0)); moveStrategy = new PathFollowerMoveStrategy(new File(".\\follower\\challenge_points.txt"), new PositionVector(0, 0));
} catch (FileNotFoundException e) { } catch (FileNotFoundException | InvalidFileFormatException e) {
e.printStackTrace(); e.printStackTrace();
} }
car.setMoveStrategy(moveStrategy); car.setMoveStrategy(moveStrategy);
assertEquals(moveStrategy, car.getMoveStrategy()); assertEquals(moveStrategy, car.getMoveStrategy());
moveStrategy = new UserMoveStrategy(new UserInterface("Hello"),0,'a'); moveStrategy = new UserMoveStrategy(new UserInterface("Hello"), 0, 'a');
car.setMoveStrategy(moveStrategy); car.setMoveStrategy(moveStrategy);
assertEquals(moveStrategy, car.getMoveStrategy()); assertEquals(moveStrategy, car.getMoveStrategy());
} }
/** /**
* Test for get WinPoints * Tests if getWinPoints returns the correct value.
*/ */
@Test @Test
void getWinPoints() { void getWinPoints() {
assertEquals(0,car.getWinPoints()); assertEquals(0, car.getWinPoints());
} }
/** /**
* Test for increase WinPoints * Tests if winpoints increase functions properly.
*/ */
@Test @Test
void increaseWinPoints() { void increaseWinPoints() {
car.increaseWinPoints(); car.increaseWinPoints();
assertEquals(1,car.getWinPoints()); assertEquals(1, car.getWinPoints());
} }
/** /**
* Test for deduct WinPoints * Tests if the deductions of winpoints functions properly.
*/ */
@Test @Test
void deductWinPoints() { void deductWinPoints() {
car.deductWinPoints(); car.deductWinPoints();
assertEquals(-1,car.getWinPoints()); assertEquals(-1, car.getWinPoints());
} }
} }

View File

@ -1,11 +1,8 @@
package ch.zhaw.pm2.racetrack; package ch.zhaw.pm2.racetrack;
import ch.zhaw.pm2.racetrack.strategy.UserMoveStrategy; import ch.zhaw.pm2.racetrack.strategy.UserMoveStrategy;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.*;
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.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.util.List; import java.util.List;
@ -14,7 +11,9 @@ import static ch.zhaw.pm2.racetrack.PositionVector.Direction.*;
/** /**
* Test for Class Game * Test for Class Game. The Class is split up in nested classes.
*
* @author Andrin Fassbind
*/ */
class GameTest { class GameTest {
private UserInterface userInterface; private UserInterface userInterface;
@ -26,16 +25,20 @@ class GameTest {
private final int CAR_INDEX_TWO = 1; private final int CAR_INDEX_TWO = 1;
/** /**
* This nested Class tests if the game gets initiatet correctly * This nested Class tests if the game gets initiated correctly.
*/ */
@Nested @Nested
@DisplayName("Test correct Setup") @DisplayName("Test correct Setup")
class Setup { class Setup {
/**
* Sets up getting the Config, Game and Userinterface for the following tests.
*/
@BeforeEach @BeforeEach
void setup() { void setup() {
userInterface = new UserInterface("Test"); userInterface = new UserInterface("Test");
game = new Game(userInterface); Config config = new Config();
game = new Game(userInterface, config);
try { try {
track = game.selectTrack(new File(TRACK_FILE_PATH)); track = game.selectTrack(new File(TRACK_FILE_PATH));
} catch (InvalidTrackFormatException | FileNotFoundException e) { } catch (InvalidTrackFormatException | FileNotFoundException e) {
@ -47,6 +50,9 @@ class GameTest {
} }
/**
* Tests if car index is correct
*/
@Test @Test
void getCurrentCarIndex() { void getCurrentCarIndex() {
Assertions.assertEquals(CAR_INDEX_ONE, game.getCurrentCarIndex()); Assertions.assertEquals(CAR_INDEX_ONE, game.getCurrentCarIndex());
@ -54,34 +60,52 @@ class GameTest {
Assertions.assertEquals(CAR_INDEX_TWO, game.getCurrentCarIndex()); Assertions.assertEquals(CAR_INDEX_TWO, game.getCurrentCarIndex());
} }
/**
* Checks if CarId matches char given in track file
*/
@Test @Test
void getCarId() { void getCarId() {
Assertions.assertEquals('a', game.getCarId(0)); Assertions.assertEquals('a', game.getCarId(0));
Assertions.assertEquals('b', game.getCarId(1)); Assertions.assertEquals('b', game.getCarId(1));
} }
/**
* Checks initial car position
*/
@Test @Test
void getCarPosition() { void getCarPosition() {
Assertions.assertEquals(new PositionVector(24, 22), game.getCarPosition(0)); Assertions.assertEquals(new PositionVector(24, 22), game.getCarPosition(0));
Assertions.assertEquals(new PositionVector(24, 24), game.getCarPosition(1)); Assertions.assertEquals(new PositionVector(24, 24), game.getCarPosition(1));
} }
/**
* Checks if initial car velocity is 0,0
*/
@Test @Test
void getCarVelocity() { void getCarVelocity() {
Assertions.assertEquals(new PositionVector(0, 0), game.getCarVelocity(0)); Assertions.assertEquals(new PositionVector(0, 0), game.getCarVelocity(0));
Assertions.assertEquals(new PositionVector(0, 0), game.getCarVelocity(1)); Assertions.assertEquals(new PositionVector(0, 0), game.getCarVelocity(1));
} }
/**
* Checks initial winner
*/
@Test @Test
void getWinner() { void getWinner() {
Assertions.assertEquals(NO_WINNER, game.getWinner()); Assertions.assertEquals(NO_WINNER, game.getWinner());
} }
/**
* Checks correct initial state
*/
@Test @Test
void onlyOneCarLeft() { void onlyOneCarLeft() {
Assertions.assertFalse(game.onlyOneCarLeft()); Assertions.assertFalse(game.onlyOneCarLeft());
} }
/**
* Checks if cars are able to move
*/
@Test @Test
void carsMoving() { void carsMoving() {
Assertions.assertTrue(game.carsMoving()); Assertions.assertTrue(game.carsMoving());
@ -98,7 +122,8 @@ class GameTest {
@BeforeEach @BeforeEach
void setup() { void setup() {
userInterface = new UserInterface("Test"); userInterface = new UserInterface("Test");
game = new Game(userInterface); Config config = new Config();
game = new Game(userInterface, config);
try { try {
track = game.selectTrack(new File(TRACK_FILE_PATH)); track = game.selectTrack(new File(TRACK_FILE_PATH));
} catch (InvalidTrackFormatException | FileNotFoundException e) { } catch (InvalidTrackFormatException | FileNotFoundException e) {
@ -110,12 +135,18 @@ class GameTest {
} }
/**
* Checks if car does change velocity specified by direction input
*/
@Test @Test
void carTurnCorrect() { void carTurnCorrect() {
game.doCarTurn(RIGHT); game.doCarTurn(RIGHT);
Assertions.assertEquals(new PositionVector(1, 0), game.getCarVelocity(0)); Assertions.assertEquals(new PositionVector(1, 0), game.getCarVelocity(0));
} }
/**
* Checks if car crash is set if a car crashes.
*/
@Test @Test
void carCrash() { void carCrash() {
game.doCarTurn(PositionVector.Direction.UP); game.doCarTurn(PositionVector.Direction.UP);
@ -124,16 +155,20 @@ class GameTest {
} }
/** /**
* This nested Class tests a Playtrough. And implements a UserInterface interagtion to pretend a real player * This nested Class tests a play trough and implements a userInterface which pretends to be a real player.
* At the end of every Test the user interface stays open for 10 more sec to visualize the game.
*/ */
@Nested @Nested
@DisplayName("Playtrough") @DisplayName("Playtrough")
class Play { class Play {
private Game game; private Game game;
/**
* This method will start a game instance on the challenge track. Car a uses MoveListStrategy. Car b uses DoNotMoveStrategy
*/
@Test @Test
void winner() { void winner() {
game = new Game(new interFace("Test",new Integer[]{0,2,0},new PositionVector.Direction[]{RIGHT, game = new Game(new Interface("Test",new Integer[]{0,2,0},new PositionVector.Direction[]{RIGHT,
RIGHT, RIGHT,
RIGHT, RIGHT,
NONE, NONE,
@ -171,35 +206,75 @@ class GameTest {
UP_RIGHT, UP_RIGHT,
UP_RIGHT, UP_RIGHT,
RIGHT, RIGHT,
RIGHT})); RIGHT}), new Config());
game.initPhase(); game.initPhase();
Assertions.assertEquals("a",game.gamePhase()); Assertions.assertEquals("a",game.gamePhase());
} }
/**
* Will start a game instance where car a does crash.
*/
@Test @Test
void crashA() { void crashA() {
game = new Game(new interFace("Test",new Integer[]{0,1,0},new PositionVector.Direction[]{UP})); game = new Game(new Interface("Test",new Integer[]{0,1,0},new PositionVector.Direction[]{UP}), new Config());
game.initPhase(); game.initPhase();
Assertions.assertEquals("b",game.gamePhase()); Assertions.assertEquals("b",game.gamePhase());
} }
/**
* Checks that a car cant win before finishing a complete round around the track.
*/
@Test
void passFinishLineInWrongDirection() {
game = new Game(new Interface("Test",new Integer[]{1,0,1},new PositionVector.Direction[]{LEFT,NONE,NONE,RIGHT,RIGHT}), new Config());
game.initPhase();
Assertions.assertEquals("a",game.gamePhase());
} }
private class interFace extends UserInterface { /**
* Does wait 10 seconds before closing the textio.
*/
@AfterEach
void cleanUp() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private PositionVector.Direction[] directions; }
private Integer[] instructions;
/**
* This Class is used to communicate with the UserInterface. It overrides crucial methods and returns an instruction based on the instructions' data field.
* To implement the right instructions the user has to be aware of the game sequence.
*/
private class Interface extends UserInterface {
private final PositionVector.Direction[] directions;
private final Integer[] instructions;
private int pointerDir,pointerInstruction; private int pointerDir,pointerInstruction;
/**
public interFace(String welcometxt, Integer[] instructions, PositionVector.Direction[] directions) { * Constructor to create a new Interface Object
super(welcometxt); * @param welcometext The first Text which will be printed in the UserInterface Window
* @param instructions list of instructions to simulate userinput
* @param directions list of Directions to simulate userinput
*/
public Interface(String welcometext, Integer[] instructions, PositionVector.Direction[] directions) {
super(welcometext);
pointerDir = -1; pointerDir = -1;
pointerInstruction = -1; pointerInstruction = -1;
this.instructions = instructions; this.instructions = instructions;
this.directions = directions; this.directions = directions;
} }
/**
* Overwriting Method for testing without userinteraction
* @param text Text which is printed before the options are printed. Example: "Select Track file:"
* @param options List with the options which can be selected.
* @return the selected Option
*/
@Override @Override
public int selectOption(String text, List<String> options) { public int selectOption(String text, List<String> options) {
pointerInstruction++; pointerInstruction++;
@ -207,15 +282,29 @@ class GameTest {
} }
/**
* Overwriting Method for testing without userinteraction
* @param text The Text which should be printed.
*/
@Override
public void printInformation(String text) { public void printInformation(String text) {
} }
public void printTrack(Track track) { /**
} * Overwriting Method for testing without userinteraction
* @param text Output Text
*/
@Override
public void quit(String text) { public void quit(String text) {
} }
/**
* Overwriting Method for testing without userinteraction
* @param playingCarIndex the index of the player
* @param playingCarID the ID of the player
* @return the direction to accelerate
*/
@Override
public PositionVector.Direction selectDirection(int playingCarIndex, char playingCarID) { public PositionVector.Direction selectDirection(int playingCarIndex, char playingCarID) {
pointerDir += 1; pointerDir += 1;
if(pointerDir < directions.length) { if(pointerDir < directions.length) {

View File

@ -4,26 +4,38 @@ import ch.zhaw.pm2.racetrack.strategy.MoveListStrategy;
import ch.zhaw.pm2.racetrack.strategy.MoveStrategy; import ch.zhaw.pm2.racetrack.strategy.MoveStrategy;
import org.junit.jupiter.api.*; import org.junit.jupiter.api.*;
import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
public class MoveStrategyTest { /**
* This Class tests the MoveStrategy.
*
* @author Andrin Fassbind
*/
public class MoveListStrategyTest {
private MoveStrategy moveList; private MoveStrategy moveList;
@Nested @Nested
@DisplayName("MoveListStrategy") @DisplayName("MoveListStrategy")
class MoveList { class MoveList {
/**
* Creates a new MoveListStrategy
*/
@BeforeEach @BeforeEach
void setup() { void setup() {
try { try {
moveList = new MoveListStrategy(".\\moves\\challenge-car-a.txt"); moveList = new MoveListStrategy(new File(".\\moves\\challenge-car-a.txt"));
}catch (FileNotFoundException e) { }catch (FileNotFoundException | InvalidFileFormatException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
/**
* Checks if the directions are returned correct by method nextMove
*/
@Test @Test
void checkMove() { void checkMove() {
Assertions.assertEquals(PositionVector.Direction.RIGHT,moveList.nextMove()); Assertions.assertEquals(PositionVector.Direction.RIGHT,moveList.nextMove());

View File

@ -2,20 +2,26 @@ package ch.zhaw.pm2.racetrack;
import ch.zhaw.pm2.racetrack.given.ConfigSpecification; import ch.zhaw.pm2.racetrack.given.ConfigSpecification;
import org.junit.jupiter.api.*; import org.junit.jupiter.api.*;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/**
* This Class tests the track
*
* @author Andrin Fassbind
*/
public class TrackTest { public class TrackTest {
Track trackObj; Track trackObj;
@Nested @Nested
@DisplayName("Positiv Test Cases") @DisplayName("Positive Test Cases")
class positivClass { class positiveClass {
/**
* Sets up the Test cases by loading the file and setting a new track.
*/
@BeforeEach @BeforeEach
void setup() { void setup() {
File file = new File(".\\tracks\\challenge.txt"); File file = new File(".\\tracks\\challenge.txt");
@ -28,12 +34,18 @@ public class TrackTest {
} }
/**
* Checks if correct amount of cars has been instantiated
*/
@Test @Test
@DisplayName("Create correct amount of Car instance") @DisplayName("Create correct amount of Car instance")
void checkCars() { void checkCars() {
Assertions.assertEquals(2, trackObj.getCarCount()); Assertions.assertEquals(2, trackObj.getCarCount());
} }
/**
* Checks if car id matches track file symbol
*/
@Test @Test
@DisplayName("Create Car instance with correct Symbols / Id") @DisplayName("Create Car instance with correct Symbols / Id")
void checkCarId() { void checkCarId() {
@ -41,13 +53,18 @@ public class TrackTest {
Assertions.assertEquals('b', trackObj.getCarId(1)); Assertions.assertEquals('b', trackObj.getCarId(1));
} }
/**
* checks if track reads space typ from track file correctly
*/
@Test @Test
@DisplayName("Check getSpaceTyp") @DisplayName("Check getSpaceTyp")
void getSpaceTyp() { void getSpaceTyp() {
Assertions.assertEquals(ConfigSpecification.SpaceType.FINISH_RIGHT, trackObj.getSpaceType(new PositionVector(22, 24))); Assertions.assertEquals(ConfigSpecification.SpaceType.FINISH_RIGHT, trackObj.getSpaceType(new PositionVector(22, 24)));
} }
/**
* checks if track finds the finish line at the correct positions
*/
@Test @Test
@DisplayName("Find FinishLine") @DisplayName("Find FinishLine")
void findFinish() { void findFinish() {
@ -58,8 +75,11 @@ public class TrackTest {
Assertions.assertEquals(expected, trackObj.getFinishLine()); Assertions.assertEquals(expected, trackObj.getFinishLine());
} }
/**
* Checks if track does read in track file correctly
*/
@Test @Test
@DisplayName("Converts Trackfile correctly to List<String>") @DisplayName("Converts track file correctly to List<String>")
void checkTrack() { void checkTrack() {
Track trackObj; Track trackObj;
try { try {
@ -85,6 +105,9 @@ public class TrackTest {
} }
} }
/**
* checks if track does process car move correctly
*/
@Test @Test
@DisplayName("Make Car move down on track") @DisplayName("Make Car move down on track")
void makeCarMoveDown() { void makeCarMoveDown() {
@ -96,6 +119,9 @@ public class TrackTest {
Assertions.assertEquals(beforeMove.getX(), afterMove.getX()); Assertions.assertEquals(beforeMove.getX(), afterMove.getX());
} }
/**
* Checks if car does not move if there is no acceleration
*/
@Test @Test
@DisplayName("Make Car move with (0,0) acceleration on track") @DisplayName("Make Car move with (0,0) acceleration on track")
void makeCarStay() { void makeCarStay() {
@ -106,6 +132,9 @@ public class TrackTest {
Assertions.assertEquals(beforeMove.getX(), afterMove.getX()); Assertions.assertEquals(beforeMove.getX(), afterMove.getX());
} }
/**
* Checks if car does crash
*/
@Test @Test
@DisplayName("Will Car Crash") @DisplayName("Will Car Crash")
void willCarCrash() { void willCarCrash() {
@ -118,6 +147,9 @@ public class TrackTest {
} }
/**
* checks if track is updated after car crash
*/
@Test @Test
@DisplayName("Make Car Crash") @DisplayName("Make Car Crash")
void makeCarCrash() { void makeCarCrash() {
@ -127,10 +159,17 @@ public class TrackTest {
} }
} }
/**
* This testcase does check for negative test cases
*/
@Nested @Nested
@DisplayName("Negative TestCase") @DisplayName("Negative TestCase")
class negativeClass { class negativeClass {
File file; File file;
/**
* Sets up the negative Test cases by loading the file and setting a new track.
*/
@BeforeEach @BeforeEach
void setup() { void setup() {
file = new File(".\\tracks\\challenge.txt"); file = new File(".\\tracks\\challenge.txt");
@ -142,6 +181,9 @@ public class TrackTest {
} }
} }
/**
* Tries to read not valid file
*/
@Test @Test
@DisplayName("Throw error if File not found") @DisplayName("Throw error if File not found")
void canReadFile() { void canReadFile() {
@ -149,11 +191,14 @@ public class TrackTest {
Assertions.assertThrows(FileNotFoundException.class, () -> new Track(file)); Assertions.assertThrows(FileNotFoundException.class, () -> new Track(file));
} }
/**
* Tries to read not valid file with 2 cars with same symbol
*/
@Test @Test
@DisplayName("Throw error if File is invalid") @DisplayName("Throw error if File is invalid")
void invalidTrackFile() { void invalidTrackFile() {
File testfile = new File(".\\src\\test\\InvalidTracks\\sameCar.txt"); File testFile = new File(".\\src\\test\\InvalidTracks\\sameCar.txt");
Assertions.assertThrows(InvalidTrackFormatException.class, () -> new Track(testfile)); Assertions.assertThrows(InvalidTrackFormatException.class, () -> new Track(testFile));
} }
} }
} }