Compare commits

...

10 Commits
lol ... main

29 changed files with 441 additions and 547 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

View File

@ -210,7 +210,7 @@
<directory>src/test/java</directory>
</resource>
<resource>
<directory>src/main/java/resources</directory>
<directory>src/main/resources</directory>
</resource>
</resources>
<testSourceDirectory>src/test/java</testSourceDirectory>

View File

@ -2,9 +2,11 @@ package GUI;
import domain.*;
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 HitoriGameTimer gameTimer;
@ -16,38 +18,58 @@ public class GameUIController {
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 HitoriGameTimer();
this.gameScores = new HitoriGameScores();
public GameUIController(int[][] initialBoard, HitoriDialogManager dialogManager, Stage primaryStage, HitoriGameMain existingGame) {
this.primaryStage = primaryStage;
this.dialogManager = dialogManager;
this.isPaused = false;
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();
checkWin();
}
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();
checkWin();
}
private void checkWin() {
if (gameSolver.isSolved()) {
handleWin();
}
}
public void togglePause() {
@ -65,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();
// Main application will handle 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() {
@ -111,8 +151,11 @@ public class GameUIController {
}
public void saveGame() {
HitoriGameMain gameState = new HitoriGameMain(gameMoves.getBoard());
gameState.saveGameState();
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.");
}
@ -128,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;
@ -175,7 +218,9 @@ public class GameUIController {
private void updateUI() {
boardPanel.updateBoard();
updateMistakeLabel();
checkWin();
if (gameSolver.isSolved()) {
handleWin();
}
}
private void updateTimerLabel() {
@ -183,7 +228,7 @@ public class GameUIController {
}
private void updateMistakeLabel() {
controlPanel.updateMistakeLabel(gameMoves.getMistakeCount());
controlPanel.updateMistakeLabel(gameSolver.getMistakeCount());
}
private void updateHighScoreDisplay() {
@ -195,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) {
@ -208,8 +265,8 @@ public class GameUIController {
}
public void cleanup() {
stopTimer();
saveGame();
stopTimer();
}
public HitoriBoardPanel getBoardPanel() {

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()) {

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

@ -3,11 +3,11 @@ package Main;
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;
import javafx.stage.Stage;
import GUI.GameUIController;
public class MainMethod extends Application {
private HitoriBoardLoader boardLoader;
@ -24,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);
}
@ -46,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);
}

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

@ -24,14 +24,14 @@ class GameBaseTest {
@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();

View File

@ -1,5 +1,6 @@
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;
@ -28,13 +29,10 @@ class GameUIControllerTest {
this.stage = stage;
}
@BeforeEach
void setUp() {
dialogManager = mock(HitoriDialogManager.class);
controller = new GameUIController(TEST_BOARD, dialogManager);
}
@Test
// Checkt ob alle Elemente initialisiert wurden
void testInitialization() {
assertNotNull(controller);
assertNotNull(controller.getBoardPanel());
@ -68,12 +66,12 @@ class GameUIControllerTest {
@Test
void testResetGame() {
// Make some moves first
// Feldermarkierungen
controller.handleLeftClick(0, 0);
controller.handleRightClick(1, 1);
controller.resetGame();
// Verify the board is reset
// Verifiziert Boardlöschung
var boardPanel = controller.getBoardPanel();
assertEquals(9, boardPanel.getChildren().size()); // 3x3 board
}
@ -83,13 +81,13 @@ class GameUIControllerTest {
controller.handleLeftClick(0, 0);
controller.undo();
controller.redo();
// Verify the board state
// Verifiziert den Board Status
assertTrue(controller.getBoardPanel().getChildren().size() > 0);
}
@Test
void testShowErrors() {
// Create some errors first
// Fehler werden kreiert
controller.handleLeftClick(0, 0);
controller.handleLeftClick(0, 1);
@ -113,6 +111,7 @@ class GameUIControllerTest {
@Test
// Fehlerspeicherung
void testConvertErrorsToSet() {
List<int[]> errors = List.of(
new int[]{0, 0},
@ -129,10 +128,9 @@ class GameUIControllerTest {
assertFalse(controller.isPaused());
}
// Helper method to simulate winning moves
// Hilfsmethode
private void makeWinningMoves() {
// This would need to be implemented according to your winning condition
// For test purposes, we'll just make some moves
// Win
controller.handleLeftClick(0, 2);
controller.handleRightClick(0, 0);
controller.handleRightClick(0, 1);

View File

@ -31,12 +31,14 @@ class HitoriBoardPanelTest {
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();
@ -44,6 +46,7 @@ class HitoriBoardPanelTest {
assertEquals("1", firstButton.getText());
}
// Testet das Styling der Buttons
@Test
void testButtonStyling() {
var children = boardPanel.getChildren();
@ -51,21 +54,23 @@ class HitoriBoardPanelTest {
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() {
// Mark some cells as black to create errors
gameMoves.markCellAsBlack(0, 0);
gameMoves.markCellAsBlack(0, 1);
boardPanel.showErrors();
// Verify that error cells are marked red
// Verifiziere, dass fehlerhafte Zellen rot markiert werden
var children = boardPanel.getChildren();
for (var child : children) {
Button button = (Button) child;

View File

@ -19,19 +19,22 @@ class HitoriControlPanelTest {
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(5);
assertNotNull(42);
}
// Testet das Aktualisieren des Fehler-Labels
@Test
void testUpdateMistakeLabel() {
controlPanel.updateMistakeLabel(5);
@ -39,6 +42,7 @@ class HitoriControlPanelTest {
assertNotNull(5);
}
// Testet das Setzen des Textes für den Pause-Button
@Test
void testSetPauseButtonText() {
controlPanel.setPauseButtonText("Test");
@ -46,6 +50,7 @@ class HitoriControlPanelTest {
assertNotNull(pauseButton);
}
// Testet die Aktion des Pause-Buttons
@Test
void testPauseButtonAction() {
var pauseButton = findButtonByText(controlPanel, "Pause");
@ -55,6 +60,7 @@ class HitoriControlPanelTest {
verify(controller, times(1)).togglePause();
}
// Testet die Aktion des Reset-Buttons
@Test
void testResetButtonAction() {
var resetButton = findButtonByText(controlPanel, "Reset");
@ -64,6 +70,7 @@ class HitoriControlPanelTest {
verify(controller, times(1)).resetGame();
}
// Testet die Aktion des Undo-Buttons
@Test
void testUndoButtonAction() {
var undoButton = findButtonByText(controlPanel, "Undo");
@ -73,6 +80,7 @@ class HitoriControlPanelTest {
verify(controller, times(1)).undo();
}
// Testet die Aktion des Redo-Buttons
@Test
void testRedoButtonAction() {
var redoButton = findButtonByText(controlPanel, "Redo");
@ -82,6 +90,7 @@ class HitoriControlPanelTest {
verify(controller, times(1)).redo();
}
// Testet die Aktion des Check-Solution-Buttons
@Test
void testCheckSolutionButtonAction() {
var checkButton = findButtonByText(controlPanel, "Check Solution");
@ -91,6 +100,7 @@ class HitoriControlPanelTest {
verify(controller, times(1)).checkSolution();
}
// Testet die Aktion des New-Game-Buttons
@Test
void testNewGameButtonAction() {
var newGameButton = findButtonByText(controlPanel, "New Game");
@ -100,6 +110,7 @@ class HitoriControlPanelTest {
verify(controller, times(1)).newGame();
}
// Testet die Aktion des Show-Errors-Buttons
@Test
void testShowErrorsButtonAction() {
var showErrorsButton = findButtonByText(controlPanel, "Show Errors");
@ -109,6 +120,7 @@ class HitoriControlPanelTest {
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))
@ -116,6 +128,7 @@ class HitoriControlPanelTest {
.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())

View File

@ -1,63 +0,0 @@
import domain.HitoriBoardLoader;
import GUI.HitoriDialogManager;
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.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@ExtendWith(ApplicationExtension.class)
class HitoriDialogManagerTest {
private HitoriDialogManager dialogManager;
private Stage stage;
private HitoriBoardLoader boardLoader;
@Start
private void start(Stage stage) {
this.stage = stage;
}
@BeforeEach
void setUp() {
boardLoader = mock(HitoriBoardLoader.class);
dialogManager = new HitoriDialogManager(stage);
}
@Test
void testShowAlert() {
// Cannot directly test Alert dialogs in headless mode
assertDoesNotThrow(() ->
dialogManager.showAlert("Test", "Test Message")
);
}
@Test
void testConfirmDeleteHighScores() {
// Cannot directly test confirmation dialogs in headless mode
assertDoesNotThrow(() ->
dialogManager.confirmDeleteHighScores()
);
}
@Test
void testConfirmNewGame() {
// Cannot directly test confirmation dialogs in headless mode
assertDoesNotThrow(() ->
dialogManager.confirmNewGame()
);
}
@Test
void testConfirmLoadSavedGame() {
// Cannot directly test confirmation dialogs in headless mode
assertDoesNotThrow(() ->
dialogManager.confirmLoadSavedGame()
);
}
}

View File

@ -19,12 +19,12 @@ class HitoriGameMovesTest {
@Test
void testMarkCellAsBlack() {
// Test valid black marking
// 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)
}
@ -42,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);
@ -103,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();
@ -121,7 +119,7 @@ class HitoriGameMovesTest {
}
}
// Verify undo/redo history is cleared
// Verified undo/redo history ist closed
assertFalse(game.undo());
assertFalse(game.redo());
}

View File

@ -16,14 +16,16 @@ class HitoriGameSolverTest {
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() {
// Apply known solution
game.markCellAsBlack(0, 2);
game.markCellAsWhite(0, 0);
game.markCellAsWhite(0, 1);
@ -37,19 +39,21 @@ class HitoriGameSolverTest {
assertFalse(game.isSolved());
}
// Testet eine falsche Lösung, bei der alle Zellen weiß markiert sind
@Test
void testIncorrectSolution() {
// Mark all cells white
for (int i = 0; i < TEST_BOARD.length; i++) {
for (int j = 0; j < TEST_BOARD[0].length; j++) {
game.markCellAsWhite(i, j);
}
}
// Should not be solved due to duplicate numbers
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
@ -59,21 +63,22 @@ class HitoriGameSolverTest {
game.markCellAsBlack(1, 1);
game.markCellAsWhite(2, 2);
// Should not be solved due to disconnected white cells
assertFalse(game.isSolved());
}
// Testet die Erkennung von ungültigen schwarzen Markierungen (benachbarte schwarze Zellen)
@Test
void testFindIncorrectBlackMarks() {
// Create adjacent black cells
game.markCellAsBlack(0, 0);
game.markCellAsBlack(0, 1); // This creates an invalid situation
game.markCellAsBlack(0, 1);
List<int[]> errors = game.findIncorrectBlackMarks();
assertFalse(errors.isEmpty());
assertEquals(2, errors.size()); // Both cells should be reported
assertEquals(2, errors.size());
// Verify error coordinates
boolean foundFirst = false;
boolean foundSecond = false;
for (int[] error : errors) {
@ -83,9 +88,10 @@ class HitoriGameSolverTest {
assertTrue(foundFirst && foundSecond);
}
// Testet den Fall, dass es keine ungültigen schwarzen Markierungen gibt
@Test
void testNoIncorrectBlackMarks() {
// Make valid black marks
game.markCellAsBlack(0, 0);
game.markCellAsBlack(2, 2);
@ -93,34 +99,37 @@ class HitoriGameSolverTest {
assertTrue(errors.isEmpty());
}
// Testet, ob das Puzzle nicht gelöst ist, wenn einige Zellen unmarkiert bleiben
@Test
void testUnmarkedCellsNotSolved() {
// Leave some cells unmarked
game.markCellAsWhite(0, 0);
game.markCellAsBlack(0, 1);
// Don't mark other cells
assertFalse(game.isSolved());
}
// Testet den Fall von doppelten Zahlen in einer Zeile
@Test
void testDuplicateNumbersInRows() {
// Mark cells to create duplicate numbers in a row
game.markCellAsWhite(0, 0);
game.markCellAsWhite(0, 1);
game.markCellAsWhite(0, 2);
// Row now has duplicate numbers
assertFalse(game.isSolved());
}
// Testet den Fall von doppelten Zahlen in einer Spalte
@Test
void testDuplicateNumbersInColumns() {
// Mark cells to create duplicate numbers in a column
game.markCellAsWhite(0, 0);
game.markCellAsWhite(1, 0);
game.markCellAsWhite(2, 0);
// Column now has duplicate numbers
assertFalse(game.isSolved());
}

View File

@ -14,19 +14,24 @@ class HitoriGameTest {
@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() {
// Test timer functionality
// Testet die Integration verschiedener Spielfunktionen
// Timer starten und prüfen, ob Zeit gezählt wird
game.startTimer();
try {
Thread.sleep(1100);
@ -35,31 +40,33 @@ class HitoriGameTest {
}
assertTrue(game.getElapsedTimeInSeconds() >= 1);
// Test game moves
// Testet das Markieren von Zellen
game.markCellAsBlack(0, 2);
assertTrue(game.getBlackCells()[0][2]);
// Test scoring
// Testet das Hinzufügen von Highscores
game.addHighScore("TestPlayer", 100);
assertFalse(game.getHighScoresWithAverage().isEmpty());
}
@Test
void testSaveAndLoadGameState() {
// Make some moves
// 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);
Thread.sleep(1000); // 1 Sekunde warten
} catch (InterruptedException e) {
fail("Timer test interrupted");
}
// Save game state
game.saveGameState();
// Load game state
// Spielstand laden und prüfen
HitoriGameMain loadedGame = HitoriGameMain.loadGameState();
assertNotNull(loadedGame);
assertTrue(loadedGame.getWhiteCells()[0][0]);
@ -69,15 +76,17 @@ class HitoriGameTest {
@Test
void testResetAll() {
// Setup some game state
// Testet das vollständige Zurücksetzen des Spiels
// Spielzustand einrichten
game.markCellAsBlack(0, 0);
game.startTimer();
game.addHighScore("TestPlayer", 100);
// Reset everything
game.reset();
// Verify reset
// Prüfen, ob das Spiel zurückgesetzt wurde
assertFalse(game.getBlackCells()[0][0]);
assertEquals(0, game.getElapsedTimeInSeconds());
assertEquals(0, game.getMistakeCount());
@ -85,10 +94,11 @@ class HitoriGameTest {
@Test
void testCompleteGameFlow() {
// Start game
// Testet einen kompletten Spielablauf
game.startTimer();
// Make some moves towards solution
// Zellen markieren, um das Spiel zu lösen
game.markCellAsBlack(0, 2);
game.markCellAsWhite(0, 0);
game.markCellAsWhite(0, 1);
@ -99,14 +109,14 @@ class HitoriGameTest {
game.markCellAsWhite(2, 1);
game.markCellAsBlack(2, 2);
// Verify solution
assertFalse(game.isSolved());
// Add score
game.stopTimer();
game.addHighScore("Winner", game.getElapsedTimeInSeconds());
// Verify score was recorded
// Prüfen, ob der Highscore gespeichert wurde
boolean found = false;
for (String score : game.getHighScoresWithAverage()) {
if (score.contains("Winner")) {
@ -119,6 +129,9 @@ class HitoriGameTest {
@Test
void testTimerPauseResume() {
// Testet das Pausieren und Fortsetzen des Timers
game.startTimer();
try {
Thread.sleep(1000);
@ -126,6 +139,7 @@ class HitoriGameTest {
fail("Timer test interrupted");
}
// Timer pausieren und aktuelle Zeit speichern
game.pauseTimer();
long pausedTime = game.getElapsedTimeInSeconds();
@ -135,17 +149,18 @@ class HitoriGameTest {
fail("Timer test interrupted");
}
// Time should not increase while paused
// Zeit sollte während der Pause nicht weiterlaufen
assertEquals(pausedTime, game.getElapsedTimeInSeconds());
game.startTimer(); // Resume
// Timer fortsetzen und prüfen, ob Zeit weiterläuft
game.startTimer();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
fail("Timer test interrupted");
}
// Time should increase after resume
assertTrue(game.getElapsedTimeInSeconds() > pausedTime);
}
}

View File

@ -10,17 +10,19 @@ class HitoriGameTimerTest {
timer = new HitoriGameTimer();
}
// Testet den Anfangszustand des Timers
@Test
void testInitialState() {
assertEquals(0, timer.getElapsedTimeInSeconds());
assertFalse(timer.isPaused());
assertTrue(timer.isPaused());
}
// Testet das Starten des Timers
@Test
void testStartTimer() {
timer.startTimer();
try {
Thread.sleep(1100); // Wait a bit more than 1 second
Thread.sleep(1100);
} catch (InterruptedException e) {
fail("Timer test interrupted");
}
@ -29,6 +31,7 @@ class HitoriGameTimerTest {
assertFalse(timer.isPaused());
}
// Testet das Pausieren des Timers
@Test
void testPauseTimer() {
timer.startTimer();
@ -48,10 +51,11 @@ class HitoriGameTimerTest {
fail("Timer test interrupted");
}
// Time should not have increased while paused
assertEquals(pausedTime, timer.getElapsedTimeInSeconds());
}
// Testet das Fortsetzen des Timers nach einer Pause
@Test
void testResumeTimer() {
timer.startTimer();
@ -64,7 +68,7 @@ class HitoriGameTimerTest {
timer.pauseTimer();
long pausedTime = timer.getElapsedTimeInSeconds();
timer.startTimer(); // Resume
timer.startTimer();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
@ -74,6 +78,7 @@ class HitoriGameTimerTest {
assertTrue(timer.getElapsedTimeInSeconds() > pausedTime);
}
// Testet das Stoppen des Timers
@Test
void testStopTimer() {
timer.startTimer();
@ -95,6 +100,7 @@ class HitoriGameTimerTest {
assertEquals(stoppedTime, timer.getElapsedTimeInSeconds());
}
// Testet das Zurücksetzen des Timers
@Test
void testResetTimer() {
timer.startTimer();
@ -106,9 +112,10 @@ class HitoriGameTimerTest {
timer.resetTimer();
assertEquals(0, timer.getElapsedTimeInSeconds());
assertFalse(timer.isPaused());
assertTrue(timer.isPaused());
}
// Testet das mehrfache Pausieren und Fortsetzen des Timers
@Test
void testMultiplePauseResume() {
timer.startTimer();