Compare commits

..

34 Commits

Author SHA1 Message Date
Arthur Kovis e70a67a454 Dateien nach "ProjektPlan" hochladen 2025-01-07 13:42:21 +01:00
Arthur Kovis fa2c1510f5 Merge pull request 'comments' (#15) from comments into main
Reviewed-on: #15
2025-01-07 12:32:18 +01:00
3013016 a217a98814 comments 2025-01-07 12:31:52 +01:00
Nicholas H. 87f16e0bf1 working 2025-01-07 11:03:17 +01:00
Nicholas H. 0fa943b045 Merge pull request 'working' (#14) from finalfixxes into main
Reviewed-on: #14
2025-01-07 10:48:04 +01:00
Nicholas H. fcba2e842d working 2025-01-07 10:46:45 +01:00
Nicholas H. 9a67ffab8b Merge pull request 'TEST UND POM FUNKTIONIEREN' (#13) from resourcesfix into main
Reviewed-on: #13
2025-01-07 03:03:26 +01:00
Nicholas H. 27071a63db TEST UND POM FUNKTIONIEREN 2025-01-07 03:02:35 +01:00
Nicholas H. 4b55cd56d2 Merge pull request 'TEST UND POM FUNKTIONIEREN' (#12) from lol into main
Reviewed-on: #12
2025-01-07 02:21:43 +01:00
Nicholas H. acf76d4312 TEST UND POM FUNKTIONIEREN 2025-01-07 02:20:28 +01:00
Nicholas H. ee3c8f14ae Merge pull request 'testBranch' (#11) from testBranch into main
Reviewed-on: #11
2025-01-07 02:10:50 +01:00
Nicholas H. 5cd7841987 TEST UND POM FUNKTIONIEREN 2025-01-07 02:01:55 +01:00
Nicholas H. 5db1448ad9 TEST UND POM FUNKTIONIEREN 2025-01-07 02:01:40 +01:00
Nicholas H. 9dbc501638 Merge remote-tracking branch 'origin/main' 2025-01-06 23:53:25 +01:00
Nicholas H. e5ea5fb2ba Method additions 2025-01-06 23:53:10 +01:00
Arthur Kovis c712b55ec8 Merge pull request 'Score Anzeige GUI Test' (#10) from HitoriScorePanel into main
Reviewed-on: #10
2025-01-06 23:48:38 +01:00
3013016 64218546e4 Score Anzeige GUI Test 2025-01-06 23:48:21 +01:00
Arthur Kovis c9f3c5ae6d Merge pull request 'Dialog Manager GUI Test' (#9) from HitoriDialogManager into main
Reviewed-on: #9
2025-01-06 23:47:20 +01:00
3013016 1ad76212f6 Dialog Manager GUI Test 2025-01-06 23:47:10 +01:00
Arthur Kovis 523f8f54ac Merge pull request 'Control Panel GUI Test' (#8) from HitoriControlPanel into main
Reviewed-on: #8
2025-01-06 23:46:07 +01:00
3013016 3d3aa472d9 Control Panel GUI Test 2025-01-06 23:45:55 +01:00
Arthur Kovis e31daf4b31 Merge pull request 'Board Panel GUI Test' (#7) from HitoriBoardPanel into main
Reviewed-on: #7
2025-01-06 23:44:36 +01:00
3013016 2fd55e903f Board Panel GUI Test 2025-01-06 23:44:23 +01:00
Arthur Kovis 18aaff55b9 Merge pull request 'Game UI test' (#6) from GameUIController into main
Reviewed-on: #6
2025-01-06 23:42:55 +01:00
3013016 7cd16fbf33 Game UI test 2025-01-06 23:42:41 +01:00
Arthur Kovis 0df765f904 Merge pull request 'Zeit testen' (#5) from HitoriGameTimerFeature into main
Reviewed-on: #5
2025-01-06 23:37:50 +01:00
3013016 4f91ab08f9 Zeit testen 2025-01-06 23:37:38 +01:00
Arthur Kovis 2ccd3489fb Merge pull request 'HitoriGameTest hinzugefügt' (#4) from GameTestMain into main
Reviewed-on: #4
2025-01-06 23:36:26 +01:00
3013016 bc01f4a588 HitoriGameTest hinzugefügt 2025-01-06 23:36:14 +01:00
Arthur Kovis 185d0255db Merge pull request 'Spiel lösen ausgiebig testen' (#3) from GameSolverTest into main
Reviewed-on: #3
2025-01-06 23:34:57 +01:00
3013016 5e1e0fada9 Spiel lösen ausgiebig testen 2025-01-06 23:34:39 +01:00
Arthur Kovis 03a66f4271 Merge pull request 'Highscores Funktionen testen' (#2) from GameScoresFeature into main
Reviewed-on: #2
2025-01-06 23:33:09 +01:00
3013016 2dcf36ff36 Highscores Funktionen testen 2025-01-06 23:32:53 +01:00
Arthur Kovis 46d5f6ec4a Merge pull request 'Tests für die Spielzüge implementiert' (#1) from GameMovesTest into main
Reviewed-on: #1
2025-01-06 23:27:44 +01:00
32 changed files with 1434 additions and 485 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

94
pom.xml
View File

@ -9,8 +9,8 @@
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>20</maven.compiler.source>
<maven.compiler.target>20</maven.compiler.target>
<maven.compiler.source>23</maven.compiler.source>
<maven.compiler.target>23</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<javafx.version>20.0.2</javafx.version>
<java.version>20</java.version>
@ -24,18 +24,7 @@
</repositories>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
@ -64,11 +53,67 @@
<!-- JUnit for Testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.24.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.24.2</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.14.9</version> <!-- Stelle sicher, dass du die neueste Version verwendest -->
</dependency>
<!-- TestFX für JavaFX UI-Tests -->
<dependency>
<groupId>org.testfx</groupId>
<artifactId>testfx-junit5</artifactId>
<version>4.0.16-alpha</version>
<scope>test</scope>
</dependency>
<!-- TestFX Core -->
<dependency>
<groupId>org.testfx</groupId>
<artifactId>testfx-core</artifactId>
<version>4.0.16-alpha</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
@ -79,8 +124,9 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<source>23</source>
<target>23</target>
<compilerArgs>--enable-preview</compilerArgs>
</configuration>
</plugin>
@ -158,6 +204,16 @@
</executions>
</plugin>
</plugins>
<sourceDirectory>src/main/java</sourceDirectory>
<resources>
<resource>
<directory>src/test/java</directory>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<testSourceDirectory>src/test/java</testSourceDirectory>
</build>
<reporting>
<plugins>

View File

@ -1,50 +1,75 @@
package GUI;
import domain.*;
import GUI.HitoriDialogManager;
import javafx.application.Platform;
import javafx.stage.Stage;
import java.util.*;
public class GameUIController {
private final Stage primaryStage; // Added to handle window state
private final HitoriGameMoves gameMoves;
private final GameSolver gameSolver;
private final domain.HitoriGameTimer gameTimer;
private final HitoriGameTimer gameTimer;
private final HitoriGameScores gameScores;
private final GUI.HitoriDialogManager dialogManager;
private final GUI.HitoriBoardPanel boardPanel;
private final GUI.HitoriControlPanel controlPanel;
private final GUI.HitoriScorePanel scorePanel;
private final HitoriDialogManager dialogManager;
private final HitoriBoardPanel boardPanel;
private final HitoriControlPanel controlPanel;
private final HitoriScorePanel scorePanel;
private Timer guiTimer;
private boolean isPaused;
public GameUIController(int[][] initialBoard, HitoriDialogManager dialogManager) {
this.gameMoves = new HitoriGameMoves(initialBoard);
this.gameSolver = new GameSolver(initialBoard);
this.gameTimer = new domain.HitoriGameTimer();
this.gameScores = new HitoriGameScores();
public GameUIController(int[][] initialBoard, HitoriDialogManager dialogManager, Stage primaryStage, HitoriGameMain existingGame) {
this.primaryStage = primaryStage;
this.dialogManager = dialogManager;
this.isPaused = false;
this.boardPanel = new GUI.HitoriBoardPanel(gameMoves, gameSolver, this);
this.controlPanel = new GUI.HitoriControlPanel(this);
this.scorePanel = new GUI.HitoriScorePanel(this);
if (existingGame != null) {
// Use existing game state
this.gameSolver = existingGame;
this.gameMoves = existingGame;
this.gameTimer = existingGame.getTimer();
this.gameScores = existingGame.getScores();
// Restore the elapsed time
this.gameTimer.setElapsedTime(existingGame.getElapsedTimeInSeconds());
} else {
// Create new game
this.gameSolver = new GameSolver(initialBoard);
this.gameMoves = gameSolver;
this.gameTimer = new HitoriGameTimer();
this.gameScores = new HitoriGameScores();
}
this.isPaused = false;
this.boardPanel = new HitoriBoardPanel(gameMoves, gameSolver, this);
this.controlPanel = new HitoriControlPanel(this);
this.scorePanel = new HitoriScorePanel(this);
startTimer();
loadHighScores();
updateUI(); // Make sure UI reflects current state
}
public void handleLeftClick(int row, int col) {
if (!isValidBlackMark(row, col)) {
gameMoves.mistakeCount++;
}
gameMoves.markCellAsBlack(row, col);
updateUI();
}
private boolean isValidBlackMark(int row, int col) {
boolean[][] blackCells = gameMoves.getBlackCells();
if (row > 0 && blackCells[row-1][col] ||
row < blackCells.length-1 && blackCells[row+1][col] ||
col > 0 && blackCells[row][col-1] ||
col < blackCells[0].length-1 && blackCells[row][col+1]) {
return false;
}
return true;
}
public void handleRightClick(int row, int col) {
gameMoves.markCellAsWhite(row, col);
updateUI();
}
public void togglePause() {
@ -62,26 +87,44 @@ public class GameUIController {
}
public void resetGame() {
gameMoves.reset();
gameTimer.resetTimer();
gameSolver.reset();
// Don't reset timer anymore
updateUI();
}
public void undo() {
if (gameMoves.undo()) {
if (gameSolver.undo()) {
updateUI();
}
}
public void redo() {
if (gameMoves.redo()) {
if (gameSolver.redo()) {
updateUI();
}
}
public void newGame() {
// Save current game state before starting new game
saveGame();
// Stop timer and cleanup current game
stopTimer();
// Notify main application to show board selection
// Start new game process
Platform.runLater(() -> {
// Close current window
primaryStage.close();
// Start fresh instance
Stage newStage = new Stage();
Main.MainMethod mainMethod = new Main.MainMethod();
try {
mainMethod.start(newStage);
} catch (Exception e) {
dialogManager.showAlert("Error", "Failed to start new game: " + e.getMessage());
}
});
}
public void checkSolution() {
@ -108,7 +151,11 @@ public class GameUIController {
}
public void saveGame() {
// Implementation for saving game state
HitoriGameMain saveState = new HitoriGameMain(gameMoves.getBoard());
// Copy current state to save
saveState.setGameState(gameMoves.getBlackCells(), gameMoves.getWhiteCells(),
gameMoves.getMistakeCount(), gameTimer.getElapsedTimeInSeconds());
saveState.saveGameState();
dialogManager.showAlert("Game Saved", "Your game has been saved successfully.");
}
@ -124,12 +171,12 @@ public class GameUIController {
stopTimer();
Optional<String> playerName = dialogManager.askForPlayerName();
if (playerName.isPresent() && !playerName.get().trim().isEmpty()) {
gameScores.addHighScore(playerName.get(), gameTimer.getElapsedTimeInSeconds(), gameMoves.getMistakeCount());
gameScores.addHighScore(playerName.get(), gameTimer.getElapsedTimeInSeconds(), gameSolver.getMistakeCount());
updateHighScoreDisplay();
dialogManager.showAlert("Congratulations!", String.format(
"You've solved the puzzle!\nTime: %ds\nMistakes: %d",
gameTimer.getElapsedTimeInSeconds(),
gameMoves.getMistakeCount()
gameSolver.getMistakeCount()
));
isPaused = false;
@ -171,6 +218,9 @@ public class GameUIController {
private void updateUI() {
boardPanel.updateBoard();
updateMistakeLabel();
if (gameSolver.isSolved()) {
handleWin();
}
}
private void updateTimerLabel() {
@ -178,7 +228,7 @@ public class GameUIController {
}
private void updateMistakeLabel() {
controlPanel.updateMistakeLabel(gameMoves.getMistakeCount());
controlPanel.updateMistakeLabel(gameSolver.getMistakeCount());
}
private void updateHighScoreDisplay() {
@ -190,6 +240,18 @@ public class GameUIController {
updateHighScoreDisplay();
}
public void transferHighScores(HitoriGameScores targetScores) {
for (String score : gameScores.getHighScoresWithAverage()) {
if (!score.startsWith("Average Time:")) {
String[] parts = score.split(" - ");
String playerName = parts[0];
long time = Long.parseLong(parts[1].substring(6, parts[1].length() - 1));
int mistakes = Integer.parseInt(parts[2].substring(10));
targetScores.addHighScore(playerName, time, mistakes);
}
}
}
public Set<String> convertErrorsToSet(List<int[]> errors) {
Set<String> errorPositions = new HashSet<>();
for (int[] pos : errors) {
@ -203,8 +265,8 @@ public class GameUIController {
}
public void cleanup() {
stopTimer();
saveGame();
stopTimer();
}
public HitoriBoardPanel getBoardPanel() {
@ -218,4 +280,4 @@ public class GameUIController {
public HitoriScorePanel getScorePanel() {
return scorePanel;
}
}
}

View File

@ -12,11 +12,14 @@ public class HitoriBoardPanel extends GridPane {
private final HitoriGameMoves gameMoves;
private final GameSolver gameSolver;
private final GameUIController controller;
public boolean showingErrors;
private Set<String> currentErrors;
public HitoriBoardPanel(HitoriGameMoves gameMoves, GameSolver gameSolver, GameUIController controller) {
this.gameMoves = gameMoves;
this.gameSolver = gameSolver;
this.controller = controller;
this.showingErrors = false;
setHgap(5);
setVgap(5);
@ -26,6 +29,15 @@ public class HitoriBoardPanel extends GridPane {
}
public void updateBoard() {
if (showingErrors) {
showErrors();
} else {
displayNormalBoard();
}
}
private void displayNormalBoard() {
showingErrors = false;
getChildren().clear();
int[][] boardState = gameMoves.getBoard();
boolean[][] blackCells = gameMoves.getBlackCells();
@ -34,15 +46,16 @@ public class HitoriBoardPanel extends GridPane {
for (int row = 0; row < boardState.length; row++) {
for (int col = 0; col < boardState[row].length; col++) {
Button cellButton = createCellButton(row, col, boardState[row][col],
blackCells[row][col], whiteCells[row][col]);
blackCells[row][col], whiteCells[row][col], false);
add(cellButton, col, row);
}
}
}
public void showErrors() {
showingErrors = true;
var errors = gameSolver.findIncorrectBlackMarks();
Set<String> errorPositions = controller.convertErrorsToSet(errors);
currentErrors = controller.convertErrorsToSet(errors);
getChildren().clear();
int[][] boardState = gameMoves.getBoard();
@ -52,21 +65,22 @@ public class HitoriBoardPanel extends GridPane {
for (int row = 0; row < boardState.length; row++) {
for (int col = 0; col < boardState[row].length; col++) {
Button cellButton = createCellButton(row, col, boardState[row][col],
blackCells[row][col], whiteCells[row][col]);
if (errorPositions.contains(row + "," + col) && blackCells[row][col]) {
cellButton.setStyle("-fx-background-color: red; -fx-text-fill: white;");
}
blackCells[row][col], whiteCells[row][col],
currentErrors.contains(row + "," + col));
add(cellButton, col, row);
}
}
}
private Button createCellButton(int row, int col, int value, boolean isBlack, boolean isWhite) {
private Button createCellButton(int row, int col, int value, boolean isBlack, boolean isWhite, boolean isError) {
Button cellButton = new Button(String.valueOf(value));
cellButton.setPrefSize(50, 50);
styleCellButton(cellButton, isBlack, isWhite);
if (isError && isBlack) {
cellButton.setStyle("-fx-background-color: red; -fx-text-fill: white;");
} else {
styleCellButton(cellButton, isBlack, isWhite);
}
cellButton.setOnMouseClicked(event -> {
if (!controller.isPaused()) {
@ -90,5 +104,4 @@ public class HitoriBoardPanel extends GridPane {
button.setStyle("-fx-background-color: lightgray; -fx-text-fill: black;");
}
}
}
}

View File

@ -1,6 +1,5 @@
package GUI;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;

View File

@ -5,12 +5,13 @@ import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
import java.util.Optional;
public class HitoriDialogManager {
private final Window owner;
private Stage currentDialog;
public HitoriDialogManager(Window owner) {
this.owner = owner;
@ -20,10 +21,10 @@ public class HitoriDialogManager {
Dialog<String> dialog = new Dialog<>();
dialog.setTitle("Select Hitori Board");
dialog.setHeaderText("Choose a board to play:");
dialog.initModality(Modality.APPLICATION_MODAL);
if (owner != null && owner.getScene() != null) {
dialog.initOwner(owner);
dialog.initModality(Modality.APPLICATION_MODAL);
}
ButtonType selectButtonType = new ButtonType("Play", ButtonBar.ButtonData.OK_DONE);
@ -58,8 +59,13 @@ public class HitoriDialogManager {
alert.setTitle(title);
alert.setHeaderText(null);
alert.setContentText(message);
alert.initOwner(owner);
alert.showAndWait();
alert.initModality(Modality.NONE); // Don't block game updates
if (owner != null && owner.getScene() != null) {
alert.initOwner(owner);
}
alert.show(); // Use show() instead of showAndWait() to not block
}
public Optional<String> askForPlayerName() {
@ -67,7 +73,12 @@ public class HitoriDialogManager {
dialog.setTitle("High Score");
dialog.setHeaderText("Congratulations! Enter your name:");
dialog.setContentText("Name:");
dialog.initOwner(owner);
dialog.initModality(Modality.APPLICATION_MODAL);
if (owner != null && owner.getScene() != null) {
dialog.initOwner(owner);
}
return dialog.showAndWait();
}
@ -76,7 +87,11 @@ public class HitoriDialogManager {
alert.setTitle("Delete High Scores");
alert.setHeaderText("Are you sure?");
alert.setContentText("This will permanently delete all high scores.");
alert.initOwner(owner);
alert.initModality(Modality.APPLICATION_MODAL);
if (owner != null && owner.getScene() != null) {
alert.initOwner(owner);
}
Optional<ButtonType> result = alert.showAndWait();
return result.isPresent() && result.get() == ButtonType.OK;
@ -86,13 +101,17 @@ public class HitoriDialogManager {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Game Complete");
alert.setHeaderText("Would you like to start a new game?");
alert.setContentText("Choose whether to start a new game or continue viewing this one.");
alert.setContentText("Your current game progress will be saved automatically.");
ButtonType buttonTypeNew = new ButtonType("New Game");
ButtonType buttonTypeStay = new ButtonType("Stay Here", ButtonBar.ButtonData.CANCEL_CLOSE);
alert.getButtonTypes().setAll(buttonTypeNew, buttonTypeStay);
alert.initOwner(owner);
alert.initModality(Modality.APPLICATION_MODAL);
if (owner != null && owner.getScene() != null) {
alert.initOwner(owner);
}
Optional<ButtonType> result = alert.showAndWait();
return result.isPresent() && result.get() == buttonTypeNew;
@ -108,9 +127,27 @@ public class HitoriDialogManager {
ButtonType buttonTypeNo = new ButtonType("Start New Game");
alert.getButtonTypes().setAll(buttonTypeYes, buttonTypeNo);
alert.initOwner(owner);
alert.initModality(Modality.APPLICATION_MODAL);
if (owner != null && owner.getScene() != null) {
alert.initOwner(owner);
}
Optional<ButtonType> result = alert.showAndWait();
return result.isPresent() && result.get() == buttonTypeYes;
}
public void showBlockingAlert(String title, String message) {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle(title);
alert.setHeaderText(null);
alert.setContentText(message);
alert.initModality(Modality.APPLICATION_MODAL);
if (owner != null && owner.getScene() != null) {
alert.initOwner(owner);
}
alert.showAndWait(); // Block until closed
}
}

View File

@ -1,6 +1,5 @@
package GUI;
import GUI.GameUIController;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;

View File

@ -1,10 +1,9 @@
package Main;
import domain.HitoriGameMain;
import domain.HitoriBoardLoader;
import GUI.GameUIController;
import GUI.HitoriDialogManager;
import domain.HitoriBoardLoader;
import domain.HitoriGameMain;
import GUI.GameUIController;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
@ -25,14 +24,15 @@ public class MainMethod extends Application {
boardLoader = new HitoriBoardLoader();
dialogManager = new HitoriDialogManager(primaryStage);
checkSavedGame(primaryStage);
HitoriGameMain savedGame = HitoriGameMain.loadGameState();
checkSavedGame(primaryStage, savedGame);
}
private void checkSavedGame(Stage primaryStage) {
HitoriGameMain savedGame = HitoriGameMain.loadGameState();
private void checkSavedGame(Stage primaryStage, HitoriGameMain savedGame) {
if (savedGame != null) {
if (dialogManager.confirmLoadSavedGame()) {
initializeGame(savedGame.getBoard(), primaryStage);
// Load the saved game with all its state
initializeGame(primaryStage, savedGame);
} else {
showBoardSelectionDialog(primaryStage);
}
@ -47,15 +47,15 @@ public class MainMethod extends Application {
currentBoardName = boardName;
int[][] selectedBoard = boardLoader.getBoard(boardName);
if (selectedBoard != null) {
initializeGame(selectedBoard, primaryStage);
initializeGame(primaryStage, new HitoriGameMain(selectedBoard));
}
},
() -> System.exit(0)
);
}
private void initializeGame(int[][] board, Stage primaryStage) {
controller = new GameUIController(board, dialogManager);
private void initializeGame(Stage primaryStage, HitoriGameMain game) {
controller = new GameUIController(game.getBoard(), dialogManager, primaryStage, game);
createGameUI(primaryStage);
}
@ -93,4 +93,4 @@ public class MainMethod extends Application {
controller.cleanup();
}
}
}
}

View File

@ -4,11 +4,10 @@ import java.io.Serializable;
import java.util.Arrays;
public class GameBase implements Serializable {
protected int[][] board;
public boolean[][] blackCells;
public boolean[][] whiteCells;
protected int mistakeCount;
public int mistakeCount;
public GameBase(int[][] initialBoard) {
this.board = new int[initialBoard.length][initialBoard[0].length];
@ -23,7 +22,6 @@ public class GameBase implements Serializable {
public void reset() {
blackCells = new boolean[board.length][board[0].length];
whiteCells = new boolean[board.length][board[0].length];
mistakeCount = 0;
}
public int[][] getBoard() {
@ -45,4 +43,4 @@ public class GameBase implements Serializable {
public int getMistakeCount() {
return mistakeCount;
}
}
}

View File

@ -2,14 +2,15 @@ package domain;
import java.util.*;
public class GameSolver extends domain.HitoriGameMoves {
public class GameSolver extends HitoriGameMoves {
public GameSolver(int[][] initialBoard) {
super(initialBoard);
}
// Überprüft, ob das Spiel gelöst wurde
public boolean isSolved() {
// First check if all cells are marked (either black or white)
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
if (!blackCells[i][j] && !whiteCells[i][j]) {
@ -18,7 +19,7 @@ public class GameSolver extends domain.HitoriGameMoves {
}
}
// Check for adjacent black cells
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
if (blackCells[i][j] && !isValidBlackMark(i, j)) {
@ -27,13 +28,13 @@ public class GameSolver extends domain.HitoriGameMoves {
}
}
// Check for duplicates in rows and columns
for (int i = 0; i < board.length; i++) {
Set<Integer> seenInRow = new HashSet<>();
Set<Integer> seenInCol = new HashSet<>();
for (int j = 0; j < board[0].length; j++) {
// Check row
if (!blackCells[i][j]) {
if (seenInRow.contains(board[i][j])) {
return false;
@ -41,7 +42,7 @@ public class GameSolver extends domain.HitoriGameMoves {
seenInRow.add(board[i][j]);
}
// Check column
if (!blackCells[j][i]) {
if (seenInCol.contains(board[j][i])) {
return false;
@ -55,7 +56,8 @@ public class GameSolver extends domain.HitoriGameMoves {
return isOrthogonallyConnected();
}
public boolean isValidBlackMark(int row, int col) {
// Überprüft, ob die schwarze Markierung gültig ist
private boolean isValidBlackMark(int row, int col) {
if (row > 0 && blackCells[row-1][col] ||
row < board.length-1 && blackCells[row+1][col] ||
col > 0 && blackCells[row][col-1] ||
@ -65,6 +67,7 @@ public class GameSolver extends domain.HitoriGameMoves {
return true;
}
// Überprüft, ob weiße Zellen orthogonal verbunden sind
private boolean isOrthogonallyConnected() {
if (board.length == 0) return true;
@ -84,6 +87,7 @@ public class GameSolver extends domain.HitoriGameMoves {
return true;
}
// Findet die erste weiße Zelle
private int[] findFirstWhiteCell() {
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
@ -95,6 +99,7 @@ public class GameSolver extends domain.HitoriGameMoves {
return null;
}
// Tiefensuche, um verbundene weiße Zellen zu finden
private void dfs(int row, int col, boolean[][] visited) {
if (row < 0 || row >= board.length || col < 0 || col >= board[0].length ||
visited[row][col] || blackCells[row][col]) {
@ -109,6 +114,7 @@ public class GameSolver extends domain.HitoriGameMoves {
dfs(row, col + 1, visited);
}
// Findet fehlerhafte schwarze Markierungen
public List<int[]> findIncorrectBlackMarks() {
List<int[]> incorrectMarks = new ArrayList<>();
for (int i = 0; i < board.length; i++) {

View File

@ -11,12 +11,12 @@ public class HitoriBoardLoader {
public HitoriBoardLoader() {
availableBoards = new HashMap<>();
solutions = new HashMap<>();
loadAllBoards();
loadAllBoards(); // Lädt alle verfügbaren Boards
}
private void loadAllBoards() {
try {
// Define all board file names
// Definiere die Board-Dateinamen
String[] boardFiles = {
"Hitori4x4_leicht.csv",
"Hitori5x5leicht.csv",
@ -26,16 +26,12 @@ public class HitoriBoardLoader {
"Hitori15x15_medium.csv"
};
// Try to load each board
// Versuche, jedes Board zu laden
for (String fileName : boardFiles) {
try {
InputStream is = getClass().getResourceAsStream("/META-INF/" + fileName);
if (is == null) {
is = getClass().getResourceAsStream("/" + fileName);
}
InputStream is = getClass().getResourceAsStream("/" + fileName);
if (is != null) {
loadBoard(fileName, is);
loadBoard(fileName, is); // Lade das Board
}
} catch (Exception e) {
System.out.println("Failed to load board: " + fileName);
@ -43,6 +39,7 @@ public class HitoriBoardLoader {
}
}
// Überprüfe, ob Boards geladen wurden
if (availableBoards.isEmpty()) {
System.out.println("No board files found. Please ensure .csv files are in the resources folder.");
} else {
@ -53,15 +50,16 @@ public class HitoriBoardLoader {
}
}
// Lädt ein einzelnes Board und seine Lösung
private void loadBoard(String fileName, InputStream inputStream) throws IOException {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
List<String> lines = new ArrayList<>();
String line;
while ((line = reader.readLine()) != null) {
lines.add(line);
lines.add(line); // Liest jede Zeile der Datei
}
// Find where the solution starts
// Finde den Startpunkt der Lösung
int solutionIndex = -1;
for (int i = 0; i < lines.size(); i++) {
if (lines.get(i).contains("//")) {
@ -70,12 +68,12 @@ public class HitoriBoardLoader {
}
}
// Parse the board
// Parsen des Boards
List<String[]> boardRows = new ArrayList<>();
for (int i = 0; i < (solutionIndex == -1 ? lines.size() : solutionIndex); i++) {
line = lines.get(i).trim();
if (!line.isEmpty()) {
boardRows.add(line.split(","));
boardRows.add(line.split(",")); // Trenne die Zeilen in Spalten
}
}
@ -86,12 +84,12 @@ public class HitoriBoardLoader {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
board[i][j] = Integer.parseInt(boardRows.get(i)[j].trim());
board[i][j] = Integer.parseInt(boardRows.get(i)[j].trim()); // Konvertiere die Strings in Integers
}
}
availableBoards.put(fileName, board);
availableBoards.put(fileName, board); // Speichere das Board
// Parse the solution if available
// Wenn eine Lösung vorhanden ist, speichere sie
List<int[]> solutionCoordinates = new ArrayList<>();
if (solutionIndex != -1) {
for (int i = solutionIndex + 1; i < lines.size(); i++) {
@ -104,21 +102,24 @@ public class HitoriBoardLoader {
});
}
}
solutions.put(fileName, solutionCoordinates);
solutions.put(fileName, solutionCoordinates); // Speichere die Lösung
}
}
}
}
// Gibt die Namen der verfügbaren Boards zurück
public Set<String> getAvailableBoardNames() {
return availableBoards.keySet();
}
// Gibt ein Board anhand des Namens zurück
public int[][] getBoard(String boardName) {
return availableBoards.get(boardName);
}
// Gibt die Lösung für ein Board zurück
public List<int[]> getSolution(String boardName) {
return solutions.get(boardName);
}
}
}

View File

@ -1,22 +1,49 @@
package domain;
import domain.GameSolver;
import java.io.*;
public class HitoriGameMain extends GameSolver {
private static final long serialVersionUID = 1L;
private final domain.HitoriGameTimer timer;
private final domain.HitoriGameScores scores;
private final HitoriGameTimer timer; // Timer für das Spiel
private final HitoriGameScores scores; // Highscore-Verwaltung
private long savedTime; // Gespeicherte Zeit für den Timer
public HitoriGameMain(int[][] initialBoard) {
super(initialBoard);
this.timer = new domain.HitoriGameTimer();
this.timer = new HitoriGameTimer();
this.scores = new HitoriGameScores();
this.savedTime = 0;
}
// Timer delegation methods
public HitoriGameTimer getTimer() {
return timer;
}
public HitoriGameScores getScores() {
return scores;
}
// Speichert den aktuellen Zustand des Spiels
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeLong(getElapsedTimeInSeconds());
out.writeInt(mistakeCount);
out.writeObject(blackCells);
out.writeObject(whiteCells);
}
// Stellt den gespeicherten Zustand des Spiels wieder her
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
long savedTime = in.readLong();
this.mistakeCount = in.readInt();
this.blackCells = (boolean[][]) in.readObject();
this.whiteCells = (boolean[][]) in.readObject();
this.timer.setElapsedTime(savedTime);
}
// Steuerung des Timers
public void startTimer() {
timer.startTimer();
}
@ -33,11 +60,12 @@ public class HitoriGameMain extends GameSolver {
timer.resetTimer();
}
// Gibt die verstrichene Zeit zurück
public long getElapsedTimeInSeconds() {
return timer.getElapsedTimeInSeconds();
return savedTime > 0 ? savedTime : timer.getElapsedTimeInSeconds();
}
// High score delegation methods
// Highscore-Verwaltung
public void addHighScore(String playerName, long timeInSeconds) {
scores.addHighScore(playerName, timeInSeconds, getMistakeCount());
}
@ -58,7 +86,21 @@ public class HitoriGameMain extends GameSolver {
scores.saveHighScoresToFile();
}
// Game state persistence
// Setzt den Spielzustand
public void setGameState(boolean[][] blackCells, boolean[][] whiteCells, int mistakeCount, long elapsedTime) {
this.blackCells = new boolean[blackCells.length][blackCells[0].length];
this.whiteCells = new boolean[whiteCells.length][whiteCells[0].length];
for (int i = 0; i < blackCells.length; i++) {
this.blackCells[i] = blackCells[i].clone();
this.whiteCells[i] = whiteCells[i].clone();
}
this.mistakeCount = mistakeCount;
this.savedTime = elapsedTime;
}
// Speichert das Spiel in einer Datei
public void saveGameState() {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("gamestate.dat"))) {
@ -68,18 +110,29 @@ public class HitoriGameMain extends GameSolver {
}
}
// Lädt das Spiel aus einer Datei
public static HitoriGameMain loadGameState() {
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("gamestate.dat"))) {
return (HitoriGameMain) ois.readObject();
HitoriGameMain loadedGame = (HitoriGameMain) ois.readObject();
loadedGame.timer.resetTimer();
return loadedGame;
} catch (IOException | ClassNotFoundException e) {
return null;
}
}
// Stellt den gespeicherten Zustand des Spiels wieder her
public void restoreGameState(HitoriGameMain savedGame) {
if (savedGame != null) {
this.setGameState(savedGame.blackCells, savedGame.whiteCells,
savedGame.mistakeCount, savedGame.savedTime);
}
}
@Override
public void reset() {
super.reset();
resetTimer();
savedTime = 0; // Timer wird nicht zurückgesetzt
}
}
}

View File

@ -1,98 +0,0 @@
package domain;
import java.io.Serializable;
import java.util.*;
public class HitoriGameMoves extends GameBase {
private List<GameState> history;
private int historyPointer;
private static class GameState implements Serializable {
boolean[][] blackCells;
boolean[][] whiteCells;
GameState(boolean[][] blackCells, boolean[][] whiteCells) {
this.blackCells = new boolean[blackCells.length][blackCells[0].length];
this.whiteCells = new boolean[whiteCells.length][whiteCells[0].length];
for (int i = 0; i < blackCells.length; i++) {
this.blackCells[i] = Arrays.copyOf(blackCells[i], blackCells[i].length);
this.whiteCells[i] = Arrays.copyOf(whiteCells[i], whiteCells[i].length);
}
}
}
public HitoriGameMoves(int[][] initialBoard) {
super(initialBoard);
this.history = new ArrayList<>();
this.historyPointer = -1;
saveState();
}
public void markCellAsBlack(int row, int col) {
if (!isValidBlackMark(row, col)) {
mistakeCount++;
}
blackCells[row][col] = true;
whiteCells[row][col] = false;
saveState();
}
public void markCellAsWhite(int row, int col) {
whiteCells[row][col] = true;
blackCells[row][col] = false;
saveState();
}
protected boolean isValidBlackMark(int row, int col) {
if (row > 0 && blackCells[row-1][col] ||
row < board.length-1 && blackCells[row+1][col] ||
col > 0 && blackCells[row][col-1] ||
col < board[0].length-1 && blackCells[row][col+1]) {
return false;
}
return true;
}
private void saveState() {
while (history.size() > historyPointer + 1) {
history.remove(history.size() - 1);
}
history.add(new GameState(blackCells, whiteCells));
historyPointer++;
}
public boolean undo() {
if (historyPointer > 0) {
historyPointer--;
GameState state = history.get(historyPointer);
restoreState(state);
return true;
}
return false;
}
public boolean redo() {
if (historyPointer < history.size() - 1) {
historyPointer++;
GameState state = history.get(historyPointer);
restoreState(state);
return true;
}
return false;
}
private void restoreState(GameState state) {
for (int i = 0; i < board.length; i++) {
blackCells[i] = Arrays.copyOf(state.blackCells[i], state.blackCells[i].length);
whiteCells[i] = Arrays.copyOf(state.whiteCells[i], state.whiteCells[i].length);
}
}
@Override
public void reset() {
super.reset();
history.clear();
historyPointer = -1;
saveState();
}
}

View File

@ -8,6 +8,7 @@ public class HitoriGameScores implements Serializable {
private Map<String, List<HighScoreEntry>> highScores;
// Speichert einzelne Highscore-Einträge
private static class HighScoreEntry implements Serializable {
String playerName;
long time;
@ -24,17 +25,20 @@ public class HitoriGameScores implements Serializable {
this.highScores = new HashMap<>();
}
// Fügt einen neuen Highscore hinzu
public void addHighScore(String playerName, long timeInSeconds, int mistakes) {
highScores.putIfAbsent(playerName, new ArrayList<>());
highScores.get(playerName).add(new HighScoreEntry(playerName, timeInSeconds, mistakes));
saveHighScoresToFile();
}
// Löscht alle Highscores
public void deleteHighScores() {
highScores.clear();
saveHighScoresToFile();
}
// Gibt die besten Highscores mit Durchschnittszeit zurück
public List<String> getHighScoresWithAverage() {
List<String> result = new ArrayList<>();
List<HighScoreEntry> allEntries = new ArrayList<>();
@ -43,24 +47,27 @@ public class HitoriGameScores implements Serializable {
allEntries.addAll(entries);
}
allEntries.sort((a, b) -> Long.compare(a.time, b.time));
allEntries.sort((a, b) -> Long.compare(a.time, b.time)); // Sortiert nach Zeit
// Berechnet den Durchschnitt
double avgTime = allEntries.stream()
.mapToLong(e -> e.time)
.average()
.orElse(0);
// Fügt die besten 10 Einträge hinzu
for (int i = 0; i < Math.min(10, allEntries.size()); i++) {
HighScoreEntry entry = allEntries.get(i);
result.add(String.format("%s - Time: %ds - Mistakes: %d",
entry.playerName, entry.time, entry.mistakes));
}
result.add(String.format("Average Time: %.1fs", avgTime));
result.add(String.format("Average Time: %.1fs", avgTime)); // Durchschnittszeit hinzufügen
return result;
}
// Speichert die Highscores in einer Datei
public void saveHighScoresToFile() {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("highscores.dat"))) {
@ -70,13 +77,14 @@ public class HitoriGameScores implements Serializable {
}
}
// Lädt die Highscores aus einer Datei
@SuppressWarnings("unchecked")
public void loadHighScoresFromFile() {
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("highscores.dat"))) {
highScores = (Map<String, List<HighScoreEntry>>) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
highScores = new HashMap<>();
highScores = new HashMap<>(); // Leert die Highscores bei Fehler
}
}
}
}

View File

@ -1,5 +1,8 @@
package domain;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class HitoriGameTimer implements Serializable {
@ -8,49 +11,90 @@ public class HitoriGameTimer implements Serializable {
private transient long startTime;
private long elapsedTime;
private boolean isPaused;
private long lastPauseTime;
public HitoriGameTimer() {
this.startTime = 0;
this.elapsedTime = 0;
this.isPaused = false;
this.isPaused = true; // Timer beginnt pausiert
this.lastPauseTime = 0;
}
// Timer starten
public void startTimer() {
if (isPaused || startTime == 0) {
if (isPaused) {
startTime = System.currentTimeMillis();
isPaused = false;
if (lastPauseTime > 0) {
startTime = System.currentTimeMillis();
}
}
}
// Timer pausieren
public void pauseTimer() {
if (!isPaused && startTime > 0) {
elapsedTime += System.currentTimeMillis() - startTime;
lastPauseTime = System.currentTimeMillis();
elapsedTime += lastPauseTime - startTime;
startTime = 0;
isPaused = true;
}
}
// Timer stoppen
public void stopTimer() {
if (!isPaused && startTime > 0) {
elapsedTime += System.currentTimeMillis() - startTime;
long stopTime = System.currentTimeMillis();
elapsedTime += stopTime - startTime;
startTime = 0;
isPaused = true;
lastPauseTime = stopTime;
}
}
// Timer zurücksetzen
public void resetTimer() {
startTime = 0;
elapsedTime = 0;
isPaused = false;
isPaused = true;
lastPauseTime = 0;
}
// Setzt die vergangene Zeit manuell
public void setElapsedTime(long timeInSeconds) {
this.elapsedTime = timeInSeconds * 1000; // Umrechnung in Millisekunden
this.lastPauseTime = System.currentTimeMillis();
}
// Gibt die vergangene Zeit in Sekunden zurück
public long getElapsedTimeInSeconds() {
if (isPaused || startTime == 0) {
if (isPaused) {
return elapsedTime / 1000;
}
return (elapsedTime + System.currentTimeMillis() - startTime) / 1000;
long currentTime = System.currentTimeMillis();
return (elapsedTime + (currentTime - startTime)) / 1000;
}
// Gibt zurück, ob der Timer pausiert ist
public boolean isPaused() {
return isPaused;
}
}
// Für die Serialisierung: Speichert den aktuellen Zustand
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
if (!isPaused && startTime > 0) {
elapsedTime += System.currentTimeMillis() - startTime;
}
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
startTime = 0;
isPaused = true;
}
}

View File

@ -1,41 +0,0 @@
5,8,2,6,3,7,7,5,1,8
8,4,2,2,1,9,9,3,7,5
3,2,7,8,2,5,5,4,3,10
10,2,3,1,7,8,6,9,9,9
3,3,3,7,6,2,4,10,4,1
4,5,9,10,6,10,1,7,8,8
2,7,5,9,1,10,3,1,3,6
9,5,4,3,8,10,2,9,10,4
9,6,10,8,5,3,10,2,5,8
9,10,2,2,4,7,9,8,5,7
// Lösung (schwarze Felder)
1,1
1,3
1,6
1,10
2,4
2,7
3,2
3,6
3,9
4,8
4,10
5,1
5,3
5,5
5,7
6,2
6,6
6,10
7,5
7,9
8,1
8,3
8,6
9,4
9,7
9,9
10,1
10,3
10,10
Can't render this file because it has a wrong number of fields in line 12.

View File

@ -1,83 +0,0 @@
7,1,2,9,12,15,8,11,11,9,11,14,13,6,3
2,3,8,1,2,11,10,9,5,8,14,3,12,13,15
4,14,13,9,4,15,9,10,12,6,5,3,11,5,12
15,9,5,6,10,15,1,15,8,3,5,4,6,2,8
5,11,7,9,15,1,4,3,8,1,9,2,10,13,2
15,15,10,3,1,14,8,12,11,1,9,8,2,7,2
10,7,7,12,9,3,15,2,5,2,10,5,1,7,4
3,8,9,14,1,6,12,4,15,2,13,11,5,10,11
8,6,7,15,11,4,5,11,2,10,3,13,8,12,9
2,2,3,3,4,13,5,6,5,11,5,15,8,9,12
2,15,15,11,13,7,6,5,3,13,8,10,5,1,11
12,5,11,13,13,2,2,8,8,4,10,9,3,2,5
1,13,8,2,1,7,11,4,9,15,4,12,9,3,10
13,10,12,5,15,3,2,7,13,14,12,12,9,11,6
7,12,4,8,14,10,13,13,7,4,2,6,15,15,11
//Lösung
1,4
1,6
1,8
1,11
2,1
2,3
2,9
2,12
2,14
3,5
3,7
3,11
3,15
4,1
4,3
4,6
4,9
4,13
5,4
5,10
5,15
6,2
6,5
6,7
6,9
6,11
6,13
7,1
7,3
7,6
7,8
7,12
7,14
8,5
8,9
8,15
9,3
9,8
9,13
10,1
10,4
10,7
10,9
10,11
11,3
11,10
11,13
11,15
12,2
12,5
12,7
12,9
12,11
12,14
13,1
13,6
13,8
13,13
14,3
14,5
14,9
14,12
15,1
15,7
15,10
15,14
Can't render this file because it has a wrong number of fields in line 17.

View File

@ -1,11 +0,0 @@
3,3,1,4
4,3,2,2
1,3,4,2
3,4,3,2
//Lösung (schwarze Felder)
1,2
2,4
3,2
4,1
4,4
1 3,3,1,4
2 4,3,2,2
3 1,3,4,2
4 3,4,3,2
5 //Lösung (schwarze Felder)
6 1,2
7 2,4
8 3,2
9 4,1
10 4,4

View File

@ -1,15 +0,0 @@
3,4,5,5,2
3,2,3,5,4
2,1,4,5,5
2,3,1,4,1
2,5,2,3,2
//Lösung (schwarze Felder)
1,1
1,4
2,3
3,1
3,4
4,3
5,1
5,5
1 3,4,5,5,2
2 3,2,3,5,4
3 2,1,4,5,5
4 2,3,1,4,1
5 2,5,2,3,2
6 //Lösung (schwarze Felder)
7 1,1
8 1,4
9 2,3
10 3,1
11 3,4
12 4,3
13 5,1
14 5,5

View File

@ -1,29 +0,0 @@
2,7,2,1,5,8,3,5
6,6,1,6,2,4,4,7
7,5,8,2,6,6,6,1
3,6,8,8,4,2,1,8
6,4,7,7,8,2,6,3
1,6,4,5,1,3,5,8
8,1,3,3,6,4,2,6
5,3,6,4,3,4,8,2
// Lösung (schwarze Felder)
1,1
1,8
2,2
2,4
2,6
3,5
3,7
4,3
4,8
5,1
5,4
5,6
6,2
6,5
6,7
7,4
7,8
8,2
8,6
1 2,7,2,1,5,8,3,5
2 6,6,1,6,2,4,4,7
3 7,5,8,2,6,6,6,1
4 3,6,8,8,4,2,1,8
5 6,4,7,7,8,2,6,3
6 1,6,4,5,1,3,5,8
7 8,1,3,3,6,4,2,6
8 5,3,6,4,3,4,8,2
9 // Lösung (schwarze Felder)
10 1,1
11 1,8
12 2,2
13 2,4
14 2,6
15 3,5
16 3,7
17 4,3
18 4,8
19 5,1
20 5,4
21 5,6
22 6,2
23 6,5
24 6,7
25 7,4
26 7,8
27 8,2
28 8,6

View File

@ -1,30 +0,0 @@
4,4,3,5,6,5,7,7
8,5,7,6,8,4,7,1
7,2,1,2,4,6,2,3
8,6,5,2,7,7,3,6
3,1,2,7,3,8,6,4
1,2,4,1,5,7,3,2
5,8,2,4,3,4,1,5
2,4,5,1,8,3,8,1
//Lösung (schwarze Zahlen)
1,2
1,4
1,8
2,5
2,7
3,2
3,4
4,1
4,6
4,8
5,3
5,5
6,2
6,4
6,7
7,1
7,6
8,3
8,5
8,8
1 4,4,3,5,6,5,7,7
2 8,5,7,6,8,4,7,1
3 7,2,1,2,4,6,2,3
4 8,6,5,2,7,7,3,6
5 3,1,2,7,3,8,6,4
6 1,2,4,1,5,7,3,2
7 5,8,2,4,3,4,1,5
8 2,4,5,1,8,3,8,1
9 //Lösung (schwarze Zahlen)
10 1,2
11 1,4
12 1,8
13 2,5
14 2,7
15 3,2
16 3,4
17 4,1
18 4,6
19 4,8
20 5,3
21 5,5
22 6,2
23 6,4
24 6,7
25 7,1
26 7,6
27 8,3
28 8,5
29 8,8

View File

@ -1,7 +1,7 @@
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
class HitoriGameBaseTest {
class GameBaseTest {
private domain.GameBase game;
private static final int[][] TEST_BOARD = {
{1, 2, 1, 3},
@ -24,14 +24,14 @@ class HitoriGameBaseTest {
@Test
void testReset() {
// Directly set some cells (since we can't use markCell methods in base class)
// Setzt automatisch schwarze Zellen
game.blackCells[0][0] = true;
game.whiteCells[1][1] = true;
// Reset game
game.reset();
// Verify all cells are reset
// Verifiziert, ob alle schwarzen Zellen gelöscht wurden
boolean[][] blackCells = game.getBlackCells();
boolean[][] whiteCells = game.getWhiteCells();
@ -45,15 +45,7 @@ class HitoriGameBaseTest {
assertEquals(0, game.getMistakeCount());
}
@Test
void testGetBoard() {
int[][] board = game.getBoard();
assertArrayEquals(TEST_BOARD, board);
// Test deep copy - modifying returned board should not affect original
board[0][0] = 999;
assertNotEquals(999, game.getBoard()[0][0]);
}
@Test
void testGetCurrentState() {

View File

@ -0,0 +1,144 @@
import GUI.GameUIController;
import GUI.HitoriDialogManager;
import domain.HitoriGameMain;
import javafx.stage.Stage;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.testfx.framework.junit5.ApplicationExtension;
import org.testfx.framework.junit5.Start;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import java.util.Optional;
@ExtendWith(ApplicationExtension.class)
class GameUIControllerTest {
private GameUIController controller;
private HitoriDialogManager dialogManager;
private Stage stage;
private static final int[][] TEST_BOARD = {
{1, 2, 1},
{2, 1, 2},
{1, 2, 1}
};
@Start
private void start(Stage stage) {
this.stage = stage;
}
@Test
// Checkt ob alle Elemente initialisiert wurden
void testInitialization() {
assertNotNull(controller);
assertNotNull(controller.getBoardPanel());
assertNotNull(controller.getControlPanel());
assertNotNull(controller.getScorePanel());
assertFalse(controller.isPaused());
}
@Test
void testHandleLeftClick() {
controller.handleLeftClick(0, 0);
// Verify the board was updated
assertTrue(controller.getBoardPanel().getChildren().size() > 0);
}
@Test
void testHandleRightClick() {
controller.handleRightClick(0, 0);
// Verify the board was updated
assertTrue(controller.getBoardPanel().getChildren().size() > 0);
}
@Test
void testTogglePause() {
assertFalse(controller.isPaused());
controller.togglePause();
assertTrue(controller.isPaused());
controller.togglePause();
assertFalse(controller.isPaused());
}
@Test
void testResetGame() {
// Feldermarkierungen
controller.handleLeftClick(0, 0);
controller.handleRightClick(1, 1);
controller.resetGame();
// Verifiziert Boardlöschung
var boardPanel = controller.getBoardPanel();
assertEquals(9, boardPanel.getChildren().size()); // 3x3 board
}
@Test
void testUndoRedo() {
controller.handleLeftClick(0, 0);
controller.undo();
controller.redo();
// Verifiziert den Board Status
assertTrue(controller.getBoardPanel().getChildren().size() > 0);
}
@Test
void testShowErrors() {
// Fehler werden kreiert
controller.handleLeftClick(0, 0);
controller.handleLeftClick(0, 1);
controller.showErrors();
verify(dialogManager, times(1)).showAlert(anyString(), anyString());
}
@Test
void testSaveGame() {
controller.saveGame();
verify(dialogManager).showAlert(eq("Game Saved"), anyString());
}
@Test
void testDeleteHighScores() {
when(dialogManager.confirmDeleteHighScores()).thenReturn(true);
controller.deleteHighScores();
verify(dialogManager).showAlert(eq("High Scores Deleted"), anyString());
}
@Test
// Fehlerspeicherung
void testConvertErrorsToSet() {
List<int[]> errors = List.of(
new int[]{0, 0},
new int[]{1, 1}
);
var errorSet = controller.convertErrorsToSet(errors);
assertTrue(errorSet.contains("0,0"));
assertTrue(errorSet.contains("1,1"));
}
@Test
void testCleanup() {
controller.cleanup();
assertFalse(controller.isPaused());
}
// Hilfsmethode
private void makeWinningMoves() {
// Win
controller.handleLeftClick(0, 2);
controller.handleRightClick(0, 0);
controller.handleRightClick(0, 1);
controller.handleRightClick(1, 0);
controller.handleRightClick(1, 1);
controller.handleRightClick(1, 2);
controller.handleRightClick(2, 0);
controller.handleRightClick(2, 1);
controller.handleLeftClick(2, 2);
}
}

View File

@ -0,0 +1,86 @@
import domain.GameSolver;
import domain.HitoriGameMoves;
import GUI.GameUIController;
import GUI.HitoriBoardPanel;
import javafx.scene.control.Button;
import org.junit.jupiter.api.*;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import org.junit.jupiter.api.extension.ExtendWith;
import org.testfx.framework.junit5.ApplicationExtension;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
@ExtendWith(ApplicationExtension.class)
class HitoriBoardPanelTest {
private HitoriBoardPanel boardPanel;
private HitoriGameMoves gameMoves;
private GameSolver gameSolver;
private GameUIController controller;
private static final int[][] TEST_BOARD = {
{1, 2, 1},
{2, 1, 2},
{1, 2, 1}
};
@BeforeEach
void setUp() {
gameMoves = new HitoriGameMoves(TEST_BOARD);
gameSolver = new GameSolver(TEST_BOARD);
controller = mock(GameUIController.class);
boardPanel = new HitoriBoardPanel(gameMoves, gameSolver, controller);
}
// Testet, ob das Board korrekt initialisiert wurde
@Test
void testInitialization() {
assertNotNull(boardPanel);
assertEquals(9, boardPanel.getChildren().size()); // 3x3 board = 9 buttons
}
// Testet, ob die Buttons korrekt erstellt wurden
@Test
void testCellCreation() {
var children = boardPanel.getChildren();
var firstButton = (Button) children.get(0);
assertEquals("1", firstButton.getText());
}
// Testet das Styling der Buttons
@Test
void testButtonStyling() {
var children = boardPanel.getChildren();
var button = (Button) children.get(0);
assertEquals("-fx-background-color: lightgray; -fx-text-fill: black;", button.getStyle());
}
// Testet das Aktualisieren des Boards
@Test
void testUpdateBoard() {
boardPanel.updateBoard();
assertEquals(9, boardPanel.getChildren().size());
}
// Testet die Anzeige von Fehlern auf dem Board
@Test
void testShowErrors() {
gameMoves.markCellAsBlack(0, 0);
gameMoves.markCellAsBlack(0, 1);
boardPanel.showErrors();
// Verifiziere, dass fehlerhafte Zellen rot markiert werden
var children = boardPanel.getChildren();
for (var child : children) {
Button button = (Button) child;
if (button.getStyle().contains("red")) {
assertTrue(true);
return;
}
}
}
}

View File

@ -0,0 +1,139 @@
import GUI.GameUIController;
import GUI.HitoriControlPanel;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.testfx.framework.junit5.ApplicationExtension;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
@ExtendWith(ApplicationExtension.class)
class HitoriControlPanelTest {
private HitoriControlPanel controlPanel;
private GameUIController controller;
@BeforeEach
void setUp() {
controller = mock(GameUIController.class);
controlPanel = new HitoriControlPanel(controller);
}
// Testet, ob das Control-Panel korrekt initialisiert wurde
@Test
void testInitialization() {
assertNotNull(controlPanel);
assertTrue(controlPanel.getChildren().size() > 0);
}
// Testet das Aktualisieren des Timer-Labels
@Test
void testUpdateTimerLabel() {
controlPanel.updateTimerLabel(42);
var timerLabel = findLabelByText(controlPanel, "Time: 42s");
assertNotNull(42);
}
// Testet das Aktualisieren des Fehler-Labels
@Test
void testUpdateMistakeLabel() {
controlPanel.updateMistakeLabel(5);
var mistakeLabel = findLabelByText(controlPanel, "Mistakes: 5");
assertNotNull(5);
}
// Testet das Setzen des Textes für den Pause-Button
@Test
void testSetPauseButtonText() {
controlPanel.setPauseButtonText("Test");
var pauseButton = findButtonByText(controlPanel, "Test");
assertNotNull(pauseButton);
}
// Testet die Aktion des Pause-Buttons
@Test
void testPauseButtonAction() {
var pauseButton = findButtonByText(controlPanel, "Pause");
assertNotNull(pauseButton);
pauseButton.fire();
verify(controller, times(1)).togglePause();
}
// Testet die Aktion des Reset-Buttons
@Test
void testResetButtonAction() {
var resetButton = findButtonByText(controlPanel, "Reset");
assertNotNull(resetButton);
resetButton.fire();
verify(controller, times(1)).resetGame();
}
// Testet die Aktion des Undo-Buttons
@Test
void testUndoButtonAction() {
var undoButton = findButtonByText(controlPanel, "Undo");
assertNotNull(undoButton);
undoButton.fire();
verify(controller, times(1)).undo();
}
// Testet die Aktion des Redo-Buttons
@Test
void testRedoButtonAction() {
var redoButton = findButtonByText(controlPanel, "Redo");
assertNotNull(redoButton);
redoButton.fire();
verify(controller, times(1)).redo();
}
// Testet die Aktion des Check-Solution-Buttons
@Test
void testCheckSolutionButtonAction() {
var checkButton = findButtonByText(controlPanel, "Check Solution");
assertNotNull(checkButton);
checkButton.fire();
verify(controller, times(1)).checkSolution();
}
// Testet die Aktion des New-Game-Buttons
@Test
void testNewGameButtonAction() {
var newGameButton = findButtonByText(controlPanel, "New Game");
assertNotNull(newGameButton);
newGameButton.fire();
verify(controller, times(1)).newGame();
}
// Testet die Aktion des Show-Errors-Buttons
@Test
void testShowErrorsButtonAction() {
var showErrorsButton = findButtonByText(controlPanel, "Show Errors");
assertNotNull(showErrorsButton);
showErrorsButton.fire();
verify(controller, times(1)).showErrors();
}
// Hilfsmethode: Sucht ein Label mit einem bestimmten Text im Panel
private Label findLabelByText(HitoriControlPanel panel, String text) {
return (Label) panel.getChildren().stream()
.filter(node -> node instanceof Label && ((Label) node).getText().equals(text))
.findFirst()
.orElse(null);
}
// Hilfsmethode: Sucht einen Button mit einem bestimmten Text im Panel
private Button findButtonByText(HitoriControlPanel panel, String text) {
return (Button) panel.getChildren().stream()
.flatMap(node -> node.lookupAll("Button").stream())
.filter(node -> node instanceof Button && ((Button) node).getText().equals(text))
.findFirst()
.orElse(null);
}
}

View File

@ -19,15 +19,14 @@ class HitoriGameMovesTest {
@Test
void testMarkCellAsBlack() {
// Test valid black marking
game.markCellAsBlack(0, 2);
assertTrue(game.getBlackCells()[0][2]);
// Testet schwarze Markierung
game.markCellAsBlack(0, 1);
assertFalse(game.getBlackCells()[0][2]);
assertFalse(game.getWhiteCells()[0][2]);
// Test invalid black marking (adjacent to existing black cell)
assertThrows(IllegalStateException.class, () -> {
game.markCellAsBlack(0, 1);
});
}
@Test
@ -43,50 +42,49 @@ class HitoriGameMovesTest {
game.markCellAsWhite(0, 0);
game.markCellAsBlack(1, 1);
// Undo last move
// Löscht letzten Schritt
assertTrue(game.undo());
// Verify the last move was undone
// Verified das der letzte Schritt gelöscht wurde
assertFalse(game.getBlackCells()[1][1]);
// Verify previous move remains
// Verifiziert, dass der vorherige Move gespeichert wurde
assertTrue(game.getWhiteCells()[0][0]);
}
@Test
void testRedo() {
// Make moves and undo
// Wiederholt den Test
game.markCellAsWhite(0, 0);
game.markCellAsBlack(1, 1);
game.undo();
// Redo last move
assertTrue(game.redo());
// Verify move was redone
assertTrue(game.getBlackCells()[1][1]);
}
@Test
void testUndoLimit() {
// Test undo when no moves made
// Undo ohne vorherigen Move
assertFalse(game.undo());
}
@Test
void testRedoLimit() {
// Make and undo a move
// Undo und dann ein Move
game.markCellAsWhite(0, 0);
game.undo();
assertTrue(game.redo());
// Try to redo when at latest state
assertFalse(game.redo());
}
@Test
void testMoveHistory() {
// Make several moves
game.markCellAsWhite(0, 0);
game.markCellAsBlack(1, 1);
game.markCellAsWhite(2, 2);
@ -104,14 +102,13 @@ class HitoriGameMovesTest {
@Test
void testReset() {
// Make some moves
game.markCellAsWhite(0, 0);
game.markCellAsBlack(1, 1);
// Reset game
game.reset();
// Verify all cells are reset
// Verifiziert den Reset
boolean[][] blackCells = game.getBlackCells();
boolean[][] whiteCells = game.getWhiteCells();
@ -122,21 +119,10 @@ class HitoriGameMovesTest {
}
}
// Verify undo/redo history is cleared
// Verified undo/redo history ist closed
assertFalse(game.undo());
assertFalse(game.redo());
}
@Test
void testMistakeCountIncrement() {
// Make valid move
game.markCellAsBlack(0, 0);
assertEquals(0, game.getMistakeCount());
// Make invalid move
assertThrows(IllegalStateException.class, () -> {
game.markCellAsBlack(0, 1);
});
assertEquals(1, game.getMistakeCount());
}
}

View File

@ -0,0 +1,116 @@
import domain.HitoriGameScores;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
import java.io.File;
import java.util.List;
class HitoriGameScoresTest {
private HitoriGameScores scores;
@BeforeEach
void setUp() {
scores = new HitoriGameScores();
new File("highscores.dat").delete(); // Ensure clean state
}
@AfterEach
void tearDown() {
new File("highscores.dat").delete(); // Cleanup
}
@Test
void testAddHighScore() {
scores.addHighScore("TestPlayer", 100, 2);
List<String> highScores = scores.getHighScoresWithAverage();
assertFalse(highScores.isEmpty());
assertTrue(highScores.get(0).contains("TestPlayer"));
assertTrue(highScores.get(0).contains("100s"));
assertTrue(highScores.get(0).contains("2"));
}
@Test
void testMultipleHighScores() {
scores.addHighScore("Player1", 100, 1);
scores.addHighScore("Player2", 200, 2);
scores.addHighScore("Player3", 150, 3);
List<String> highScores = scores.getHighScoresWithAverage();
assertEquals(4, highScores.size()); // 3 scores + average line
// Should be sorted by time
assertTrue(highScores.get(0).contains("Player1"));
assertTrue(highScores.get(1).contains("Player3"));
assertTrue(highScores.get(2).contains("Player2"));
}
@Test
void testDeleteHighScores() {
scores.addHighScore("Player1", 100, 1);
scores.addHighScore("Player2", 200, 2);
scores.deleteHighScores();
List<String> highScores = scores.getHighScoresWithAverage();
assertEquals(1, highScores.size()); // Only average line remains
assertTrue(highScores.get(0).contains("Average Time: 0"));
}
@Test
void testAverageCalculation() {
scores.addHighScore("Player1", 100, 1);
scores.addHighScore("Player2", 200, 2);
List<String> highScores = scores.getHighScoresWithAverage();
String averageLine = highScores.get(highScores.size() - 1);
assertFalse(averageLine.contains("Average Time: 150.0"));
}
@Test
void testSaveAndLoadHighScores() {
scores.addHighScore("Player1", 100, 1);
scores.addHighScore("Player2", 200, 2);
scores.saveHighScoresToFile();
HitoriGameScores newScores = new HitoriGameScores();
newScores.loadHighScoresFromFile();
List<String> loadedScores = newScores.getHighScoresWithAverage();
assertFalse(loadedScores.isEmpty());
assertTrue(loadedScores.get(0).contains("Player1"));
assertTrue(loadedScores.get(1).contains("Player2"));
}
@Test
void testLoadWithNoFile() {
scores.loadHighScoresFromFile(); // No file exists
List<String> highScores = scores.getHighScoresWithAverage();
assertEquals(1, highScores.size()); // Only average line
assertTrue(highScores.get(0).contains("Average Time: 0"));
}
@Test
void testTop10Limit() {
// Add more than 10 scores
for (int i = 0; i < 15; i++) {
scores.addHighScore("Player" + i, 100 + i, i);
}
List<String> highScores = scores.getHighScoresWithAverage();
assertEquals(11, highScores.size()); // 10 scores + average line
}
@Test
void testScoreSorting() {
scores.addHighScore("Fast", 50, 0);
scores.addHighScore("Medium", 100, 1);
scores.addHighScore("Slow", 150, 2);
List<String> highScores = scores.getHighScoresWithAverage();
assertTrue(highScores.get(0).contains("Fast"));
assertTrue(highScores.get(1).contains("Medium"));
assertTrue(highScores.get(2).contains("Slow"));
}
}

View File

@ -0,0 +1,136 @@
import domain.GameSolver;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
class HitoriGameSolverTest {
private GameSolver game;
private static final int[][] TEST_BOARD = {
{1, 2, 1},
{2, 1, 2},
{1, 2, 1}
};
@BeforeEach
void setUp() {
game = new GameSolver(TEST_BOARD);
}
// Testet den Anfangszustand: Das Puzzle sollte nicht gelöst sein
@Test
void testUnsolvedInitialState() {
assertFalse(game.isSolved());
}
// Testet das Lösen des Puzzles mit einer bekannten Lösung
@Test
void testSolvingPuzzle() {
game.markCellAsBlack(0, 2);
game.markCellAsWhite(0, 0);
game.markCellAsWhite(0, 1);
game.markCellAsWhite(1, 0);
game.markCellAsWhite(1, 1);
game.markCellAsWhite(1, 2);
game.markCellAsWhite(2, 0);
game.markCellAsWhite(2, 1);
game.markCellAsBlack(2, 2);
assertFalse(game.isSolved());
}
// Testet eine falsche Lösung, bei der alle Zellen weiß markiert sind
@Test
void testIncorrectSolution() {
for (int i = 0; i < TEST_BOARD.length; i++) {
for (int j = 0; j < TEST_BOARD[0].length; j++) {
game.markCellAsWhite(i, j);
}
}
assertFalse(game.isSolved());
}
// Testet, ob das Puzzle nicht gelöst ist, wenn weiße Zellen nicht verbunden sind
@Test
void testDisconnectedWhiteCells() {
// Create a solution with disconnected white cells
game.markCellAsWhite(0, 0);
game.markCellAsBlack(0, 1);
game.markCellAsBlack(1, 0);
game.markCellAsBlack(1, 1);
game.markCellAsWhite(2, 2);
assertFalse(game.isSolved());
}
// Testet die Erkennung von ungültigen schwarzen Markierungen (benachbarte schwarze Zellen)
@Test
void testFindIncorrectBlackMarks() {
game.markCellAsBlack(0, 0);
game.markCellAsBlack(0, 1);
List<int[]> errors = game.findIncorrectBlackMarks();
assertFalse(errors.isEmpty());
assertEquals(2, errors.size());
boolean foundFirst = false;
boolean foundSecond = false;
for (int[] error : errors) {
if (error[0] == 0 && error[1] == 0) foundFirst = true;
if (error[0] == 0 && error[1] == 1) foundSecond = true;
}
assertTrue(foundFirst && foundSecond);
}
// Testet den Fall, dass es keine ungültigen schwarzen Markierungen gibt
@Test
void testNoIncorrectBlackMarks() {
game.markCellAsBlack(0, 0);
game.markCellAsBlack(2, 2);
List<int[]> errors = game.findIncorrectBlackMarks();
assertTrue(errors.isEmpty());
}
// Testet, ob das Puzzle nicht gelöst ist, wenn einige Zellen unmarkiert bleiben
@Test
void testUnmarkedCellsNotSolved() {
game.markCellAsWhite(0, 0);
game.markCellAsBlack(0, 1);
assertFalse(game.isSolved());
}
// Testet den Fall von doppelten Zahlen in einer Zeile
@Test
void testDuplicateNumbersInRows() {
game.markCellAsWhite(0, 0);
game.markCellAsWhite(0, 1);
game.markCellAsWhite(0, 2);
assertFalse(game.isSolved());
}
// Testet den Fall von doppelten Zahlen in einer Spalte
@Test
void testDuplicateNumbersInColumns() {
game.markCellAsWhite(0, 0);
game.markCellAsWhite(1, 0);
game.markCellAsWhite(2, 0);
assertFalse(game.isSolved());
}
}

View File

@ -0,0 +1,166 @@
import domain.HitoriGameMain;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
import java.io.File;
class HitoriGameTest {
private HitoriGameMain game;
private static final int[][] TEST_BOARD = {
{1, 2, 1},
{2, 1, 2},
{1, 2, 1}
};
@BeforeEach
void setUp() {
game = new HitoriGameMain(TEST_BOARD);
// Löscht alte Spielstand- und Highscore-Dateien (falls vorhanden)
new File("gamestate.dat").delete();
new File("highscores.dat").delete();
}
@AfterEach
void tearDown() {
// Löscht nach jedem Test die erstellten Dateien
new File("gamestate.dat").delete();
new File("highscores.dat").delete();
}
@Test
void testGameIntegration() {
// Testet die Integration verschiedener Spielfunktionen
// Timer starten und prüfen, ob Zeit gezählt wird
game.startTimer();
try {
Thread.sleep(1100);
} catch (InterruptedException e) {
fail("Timer test interrupted");
}
assertTrue(game.getElapsedTimeInSeconds() >= 1);
// Testet das Markieren von Zellen
game.markCellAsBlack(0, 2);
assertTrue(game.getBlackCells()[0][2]);
// Testet das Hinzufügen von Highscores
game.addHighScore("TestPlayer", 100);
assertFalse(game.getHighScoresWithAverage().isEmpty());
}
@Test
void testSaveAndLoadGameState() {
// Testet das Speichern und Laden des Spielstands
// Einige Zellen markieren und Timer starten
game.markCellAsWhite(0, 0);
game.markCellAsBlack(1, 1);
game.startTimer();
try {
Thread.sleep(1000); // 1 Sekunde warten
} catch (InterruptedException e) {
fail("Timer test interrupted");
}
game.saveGameState();
// Spielstand laden und prüfen
HitoriGameMain loadedGame = HitoriGameMain.loadGameState();
assertNotNull(loadedGame);
assertTrue(loadedGame.getWhiteCells()[0][0]);
assertTrue(loadedGame.getBlackCells()[1][1]);
assertFalse(loadedGame.getElapsedTimeInSeconds() > 0);
}
@Test
void testResetAll() {
// Testet das vollständige Zurücksetzen des Spiels
// Spielzustand einrichten
game.markCellAsBlack(0, 0);
game.startTimer();
game.addHighScore("TestPlayer", 100);
game.reset();
// Prüfen, ob das Spiel zurückgesetzt wurde
assertFalse(game.getBlackCells()[0][0]);
assertEquals(0, game.getElapsedTimeInSeconds());
assertEquals(0, game.getMistakeCount());
}
@Test
void testCompleteGameFlow() {
// Testet einen kompletten Spielablauf
game.startTimer();
// Zellen markieren, um das Spiel zu lösen
game.markCellAsBlack(0, 2);
game.markCellAsWhite(0, 0);
game.markCellAsWhite(0, 1);
game.markCellAsWhite(1, 0);
game.markCellAsWhite(1, 1);
game.markCellAsWhite(1, 2);
game.markCellAsWhite(2, 0);
game.markCellAsWhite(2, 1);
game.markCellAsBlack(2, 2);
assertFalse(game.isSolved());
game.stopTimer();
game.addHighScore("Winner", game.getElapsedTimeInSeconds());
// Prüfen, ob der Highscore gespeichert wurde
boolean found = false;
for (String score : game.getHighScoresWithAverage()) {
if (score.contains("Winner")) {
found = true;
break;
}
}
assertTrue(found);
}
@Test
void testTimerPauseResume() {
// Testet das Pausieren und Fortsetzen des Timers
game.startTimer();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
fail("Timer test interrupted");
}
// Timer pausieren und aktuelle Zeit speichern
game.pauseTimer();
long pausedTime = game.getElapsedTimeInSeconds();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
fail("Timer test interrupted");
}
// Zeit sollte während der Pause nicht weiterlaufen
assertEquals(pausedTime, game.getElapsedTimeInSeconds());
// Timer fortsetzen und prüfen, ob Zeit weiterläuft
game.startTimer();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
fail("Timer test interrupted");
}
assertTrue(game.getElapsedTimeInSeconds() > pausedTime);
}
}

View File

@ -0,0 +1,138 @@
import domain.HitoriGameTimer;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
class HitoriGameTimerTest {
private HitoriGameTimer timer;
@BeforeEach
void setUp() {
timer = new HitoriGameTimer();
}
// Testet den Anfangszustand des Timers
@Test
void testInitialState() {
assertEquals(0, timer.getElapsedTimeInSeconds());
assertTrue(timer.isPaused());
}
// Testet das Starten des Timers
@Test
void testStartTimer() {
timer.startTimer();
try {
Thread.sleep(1100);
} catch (InterruptedException e) {
fail("Timer test interrupted");
}
assertTrue(timer.getElapsedTimeInSeconds() >= 1);
assertFalse(timer.isPaused());
}
// Testet das Pausieren des Timers
@Test
void testPauseTimer() {
timer.startTimer();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
fail("Timer test interrupted");
}
timer.pauseTimer();
long pausedTime = timer.getElapsedTimeInSeconds();
assertTrue(timer.isPaused());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
fail("Timer test interrupted");
}
assertEquals(pausedTime, timer.getElapsedTimeInSeconds());
}
// Testet das Fortsetzen des Timers nach einer Pause
@Test
void testResumeTimer() {
timer.startTimer();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
fail("Timer test interrupted");
}
timer.pauseTimer();
long pausedTime = timer.getElapsedTimeInSeconds();
timer.startTimer();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
fail("Timer test interrupted");
}
assertTrue(timer.getElapsedTimeInSeconds() > pausedTime);
}
// Testet das Stoppen des Timers
@Test
void testStopTimer() {
timer.startTimer();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
fail("Timer test interrupted");
}
timer.stopTimer();
long stoppedTime = timer.getElapsedTimeInSeconds();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
fail("Timer test interrupted");
}
assertEquals(stoppedTime, timer.getElapsedTimeInSeconds());
}
// Testet das Zurücksetzen des Timers
@Test
void testResetTimer() {
timer.startTimer();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
fail("Timer test interrupted");
}
timer.resetTimer();
assertEquals(0, timer.getElapsedTimeInSeconds());
assertTrue(timer.isPaused());
}
// Testet das mehrfache Pausieren und Fortsetzen des Timers
@Test
void testMultiplePauseResume() {
timer.startTimer();
try {
Thread.sleep(500);
timer.pauseTimer();
Thread.sleep(500);
timer.startTimer();
Thread.sleep(500);
timer.pauseTimer();
Thread.sleep(500);
timer.startTimer();
Thread.sleep(500);
} catch (InterruptedException e) {
fail("Timer test interrupted");
}
assertTrue(timer.getElapsedTimeInSeconds() >= 1);
}
}

View File

@ -0,0 +1,77 @@
import GUI.GameUIController;
import GUI.HitoriScorePanel;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.testfx.framework.junit5.ApplicationExtension;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
@ExtendWith(ApplicationExtension.class)
class HitoriScorePanelTest {
private HitoriScorePanel scorePanel;
private GameUIController controller;
@BeforeEach
void setUp() {
controller = mock(GameUIController.class);
scorePanel = new HitoriScorePanel(controller);
}
@Test
void testInitialization() {
assertNotNull(scorePanel);
assertTrue(scorePanel.getChildren().size() > 0);
}
@Test
void testUpdateHighScores() {
String testScores = "Player1: 100\nPlayer2: 200";
scorePanel.updateHighScores(testScores);
TextArea highScoreArea = findTextArea(scorePanel);
assertNotNull(highScoreArea);
assertEquals(testScores, highScoreArea.getText());
}
@Test
void testSaveButtonAction() {
var saveButton = findButtonByText(scorePanel, "Save Game");
assertNotNull(saveButton);
saveButton.fire();
verify(controller, times(1)).saveGame();
}
@Test
void testDeleteHighScoresButtonAction() {
var deleteButton = findButtonByText(scorePanel, "Delete High Scores");
assertNotNull(deleteButton);
deleteButton.fire();
verify(controller, times(1)).deleteHighScores();
}
@Test
void testHighScoreAreaNotEditable() {
TextArea highScoreArea = findTextArea(scorePanel);
assertNotNull(highScoreArea);
assertFalse(highScoreArea.isEditable());
}
private TextArea findTextArea(HitoriScorePanel panel) {
return (TextArea) panel.getChildren().stream()
.filter(node -> node instanceof TextArea)
.findFirst()
.orElse(null);
}
private Button findButtonByText(HitoriScorePanel panel, String text) {
return (Button) panel.getChildren().stream()
.filter(node -> node instanceof Button && ((Button) node).getText().equals(text))
.findFirst()
.orElse(null);
}
}