Compare commits

..

No commits in common. "main" and "lol" have entirely different histories.
main ... lol

29 changed files with 547 additions and 441 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

BIN
gamestate.dat 100644

Binary file not shown.

View File

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

View File

@ -2,11 +2,9 @@ package GUI;
import domain.*; import domain.*;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.stage.Stage;
import java.util.*; import java.util.*;
public class GameUIController { public class GameUIController {
private final Stage primaryStage; // Added to handle window state
private final HitoriGameMoves gameMoves; private final HitoriGameMoves gameMoves;
private final GameSolver gameSolver; private final GameSolver gameSolver;
private final HitoriGameTimer gameTimer; private final HitoriGameTimer gameTimer;
@ -18,58 +16,38 @@ public class GameUIController {
private Timer guiTimer; private Timer guiTimer;
private boolean isPaused; private boolean isPaused;
public GameUIController(int[][] initialBoard, HitoriDialogManager dialogManager, Stage primaryStage, HitoriGameMain existingGame) { public GameUIController(int[][] initialBoard, HitoriDialogManager dialogManager) {
this.primaryStage = primaryStage; this.gameMoves = new HitoriGameMoves(initialBoard);
this.dialogManager = dialogManager;
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.gameSolver = new GameSolver(initialBoard);
this.gameMoves = gameSolver;
this.gameTimer = new HitoriGameTimer(); this.gameTimer = new HitoriGameTimer();
this.gameScores = new HitoriGameScores(); this.gameScores = new HitoriGameScores();
} this.dialogManager = dialogManager;
this.isPaused = false; this.isPaused = false;
this.boardPanel = new HitoriBoardPanel(gameMoves, gameSolver, this); this.boardPanel = new HitoriBoardPanel(gameMoves, gameSolver, this);
this.controlPanel = new HitoriControlPanel(this); this.controlPanel = new HitoriControlPanel(this);
this.scorePanel = new HitoriScorePanel(this); this.scorePanel = new HitoriScorePanel(this);
startTimer(); startTimer();
loadHighScores(); loadHighScores();
updateUI(); // Make sure UI reflects current state
} }
public void handleLeftClick(int row, int col) { public void handleLeftClick(int row, int col) {
if (!isValidBlackMark(row, col)) {
gameMoves.mistakeCount++;
}
gameMoves.markCellAsBlack(row, col); gameMoves.markCellAsBlack(row, col);
updateUI(); 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) { public void handleRightClick(int row, int col) {
gameMoves.markCellAsWhite(row, col); gameMoves.markCellAsWhite(row, col);
updateUI(); updateUI();
checkWin();
}
private void checkWin() {
if (gameSolver.isSolved()) {
handleWin();
}
} }
public void togglePause() { public void togglePause() {
@ -87,44 +65,26 @@ public class GameUIController {
} }
public void resetGame() { public void resetGame() {
gameSolver.reset(); gameMoves.reset();
// Don't reset timer anymore gameTimer.resetTimer();
updateUI(); updateUI();
} }
public void undo() { public void undo() {
if (gameSolver.undo()) { if (gameMoves.undo()) {
updateUI(); updateUI();
} }
} }
public void redo() { public void redo() {
if (gameSolver.redo()) { if (gameMoves.redo()) {
updateUI(); updateUI();
} }
} }
public void newGame() { public void newGame() {
// Save current game state before starting new game
saveGame();
// Stop timer and cleanup current game
stopTimer(); 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() { public void checkSolution() {
@ -151,11 +111,8 @@ public class GameUIController {
} }
public void saveGame() { public void saveGame() {
HitoriGameMain saveState = new HitoriGameMain(gameMoves.getBoard()); HitoriGameMain gameState = new HitoriGameMain(gameMoves.getBoard());
// Copy current state to save gameState.saveGameState();
saveState.setGameState(gameMoves.getBlackCells(), gameMoves.getWhiteCells(),
gameMoves.getMistakeCount(), gameTimer.getElapsedTimeInSeconds());
saveState.saveGameState();
dialogManager.showAlert("Game Saved", "Your game has been saved successfully."); dialogManager.showAlert("Game Saved", "Your game has been saved successfully.");
} }
@ -171,12 +128,12 @@ public class GameUIController {
stopTimer(); stopTimer();
Optional<String> playerName = dialogManager.askForPlayerName(); Optional<String> playerName = dialogManager.askForPlayerName();
if (playerName.isPresent() && !playerName.get().trim().isEmpty()) { if (playerName.isPresent() && !playerName.get().trim().isEmpty()) {
gameScores.addHighScore(playerName.get(), gameTimer.getElapsedTimeInSeconds(), gameSolver.getMistakeCount()); gameScores.addHighScore(playerName.get(), gameTimer.getElapsedTimeInSeconds(), gameMoves.getMistakeCount());
updateHighScoreDisplay(); updateHighScoreDisplay();
dialogManager.showAlert("Congratulations!", String.format( dialogManager.showAlert("Congratulations!", String.format(
"You've solved the puzzle!\nTime: %ds\nMistakes: %d", "You've solved the puzzle!\nTime: %ds\nMistakes: %d",
gameTimer.getElapsedTimeInSeconds(), gameTimer.getElapsedTimeInSeconds(),
gameSolver.getMistakeCount() gameMoves.getMistakeCount()
)); ));
isPaused = false; isPaused = false;
@ -218,9 +175,7 @@ public class GameUIController {
private void updateUI() { private void updateUI() {
boardPanel.updateBoard(); boardPanel.updateBoard();
updateMistakeLabel(); updateMistakeLabel();
if (gameSolver.isSolved()) { checkWin();
handleWin();
}
} }
private void updateTimerLabel() { private void updateTimerLabel() {
@ -228,7 +183,7 @@ public class GameUIController {
} }
private void updateMistakeLabel() { private void updateMistakeLabel() {
controlPanel.updateMistakeLabel(gameSolver.getMistakeCount()); controlPanel.updateMistakeLabel(gameMoves.getMistakeCount());
} }
private void updateHighScoreDisplay() { private void updateHighScoreDisplay() {
@ -240,18 +195,6 @@ public class GameUIController {
updateHighScoreDisplay(); 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) { public Set<String> convertErrorsToSet(List<int[]> errors) {
Set<String> errorPositions = new HashSet<>(); Set<String> errorPositions = new HashSet<>();
for (int[] pos : errors) { for (int[] pos : errors) {
@ -265,8 +208,8 @@ public class GameUIController {
} }
public void cleanup() { public void cleanup() {
saveGame();
stopTimer(); stopTimer();
saveGame();
} }
public HitoriBoardPanel getBoardPanel() { public HitoriBoardPanel getBoardPanel() {

View File

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

View File

@ -5,13 +5,12 @@ import javafx.scene.Scene;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.stage.Modality; import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window; import javafx.stage.Window;
import java.util.Optional; import java.util.Optional;
public class HitoriDialogManager { public class HitoriDialogManager {
private final Window owner; private final Window owner;
private Stage currentDialog;
public HitoriDialogManager(Window owner) { public HitoriDialogManager(Window owner) {
this.owner = owner; this.owner = owner;
@ -21,10 +20,10 @@ public class HitoriDialogManager {
Dialog<String> dialog = new Dialog<>(); Dialog<String> dialog = new Dialog<>();
dialog.setTitle("Select Hitori Board"); dialog.setTitle("Select Hitori Board");
dialog.setHeaderText("Choose a board to play:"); dialog.setHeaderText("Choose a board to play:");
dialog.initModality(Modality.APPLICATION_MODAL);
if (owner != null && owner.getScene() != null) { if (owner != null && owner.getScene() != null) {
dialog.initOwner(owner); dialog.initOwner(owner);
dialog.initModality(Modality.APPLICATION_MODAL);
} }
ButtonType selectButtonType = new ButtonType("Play", ButtonBar.ButtonData.OK_DONE); ButtonType selectButtonType = new ButtonType("Play", ButtonBar.ButtonData.OK_DONE);
@ -59,13 +58,8 @@ public class HitoriDialogManager {
alert.setTitle(title); alert.setTitle(title);
alert.setHeaderText(null); alert.setHeaderText(null);
alert.setContentText(message); alert.setContentText(message);
alert.initModality(Modality.NONE); // Don't block game updates
if (owner != null && owner.getScene() != null) {
alert.initOwner(owner); alert.initOwner(owner);
} alert.showAndWait();
alert.show(); // Use show() instead of showAndWait() to not block
} }
public Optional<String> askForPlayerName() { public Optional<String> askForPlayerName() {
@ -73,12 +67,7 @@ public class HitoriDialogManager {
dialog.setTitle("High Score"); dialog.setTitle("High Score");
dialog.setHeaderText("Congratulations! Enter your name:"); dialog.setHeaderText("Congratulations! Enter your name:");
dialog.setContentText("Name:"); dialog.setContentText("Name:");
dialog.initModality(Modality.APPLICATION_MODAL);
if (owner != null && owner.getScene() != null) {
dialog.initOwner(owner); dialog.initOwner(owner);
}
return dialog.showAndWait(); return dialog.showAndWait();
} }
@ -87,11 +76,7 @@ public class HitoriDialogManager {
alert.setTitle("Delete High Scores"); alert.setTitle("Delete High Scores");
alert.setHeaderText("Are you sure?"); alert.setHeaderText("Are you sure?");
alert.setContentText("This will permanently delete all high scores."); alert.setContentText("This will permanently delete all high scores.");
alert.initModality(Modality.APPLICATION_MODAL);
if (owner != null && owner.getScene() != null) {
alert.initOwner(owner); alert.initOwner(owner);
}
Optional<ButtonType> result = alert.showAndWait(); Optional<ButtonType> result = alert.showAndWait();
return result.isPresent() && result.get() == ButtonType.OK; return result.isPresent() && result.get() == ButtonType.OK;
@ -101,17 +86,13 @@ public class HitoriDialogManager {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION); Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Game Complete"); alert.setTitle("Game Complete");
alert.setHeaderText("Would you like to start a new game?"); alert.setHeaderText("Would you like to start a new game?");
alert.setContentText("Your current game progress will be saved automatically."); alert.setContentText("Choose whether to start a new game or continue viewing this one.");
ButtonType buttonTypeNew = new ButtonType("New Game"); ButtonType buttonTypeNew = new ButtonType("New Game");
ButtonType buttonTypeStay = new ButtonType("Stay Here", ButtonBar.ButtonData.CANCEL_CLOSE); ButtonType buttonTypeStay = new ButtonType("Stay Here", ButtonBar.ButtonData.CANCEL_CLOSE);
alert.getButtonTypes().setAll(buttonTypeNew, buttonTypeStay); alert.getButtonTypes().setAll(buttonTypeNew, buttonTypeStay);
alert.initModality(Modality.APPLICATION_MODAL);
if (owner != null && owner.getScene() != null) {
alert.initOwner(owner); alert.initOwner(owner);
}
Optional<ButtonType> result = alert.showAndWait(); Optional<ButtonType> result = alert.showAndWait();
return result.isPresent() && result.get() == buttonTypeNew; return result.isPresent() && result.get() == buttonTypeNew;
@ -127,27 +108,9 @@ public class HitoriDialogManager {
ButtonType buttonTypeNo = new ButtonType("Start New Game"); ButtonType buttonTypeNo = new ButtonType("Start New Game");
alert.getButtonTypes().setAll(buttonTypeYes, buttonTypeNo); alert.getButtonTypes().setAll(buttonTypeYes, buttonTypeNo);
alert.initModality(Modality.APPLICATION_MODAL);
if (owner != null && owner.getScene() != null) {
alert.initOwner(owner); alert.initOwner(owner);
}
Optional<ButtonType> result = alert.showAndWait(); Optional<ButtonType> result = alert.showAndWait();
return result.isPresent() && result.get() == buttonTypeYes; 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 GUI.HitoriDialogManager;
import domain.HitoriBoardLoader; import domain.HitoriBoardLoader;
import domain.HitoriGameMain; import domain.HitoriGameMain;
import GUI.GameUIController;
import javafx.application.Application; import javafx.application.Application;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import javafx.stage.Stage; import javafx.stage.Stage;
import GUI.GameUIController;
public class MainMethod extends Application { public class MainMethod extends Application {
private HitoriBoardLoader boardLoader; private HitoriBoardLoader boardLoader;
@ -24,15 +24,14 @@ public class MainMethod extends Application {
boardLoader = new HitoriBoardLoader(); boardLoader = new HitoriBoardLoader();
dialogManager = new HitoriDialogManager(primaryStage); dialogManager = new HitoriDialogManager(primaryStage);
HitoriGameMain savedGame = HitoriGameMain.loadGameState(); checkSavedGame(primaryStage);
checkSavedGame(primaryStage, savedGame);
} }
private void checkSavedGame(Stage primaryStage, HitoriGameMain savedGame) { private void checkSavedGame(Stage primaryStage) {
HitoriGameMain savedGame = HitoriGameMain.loadGameState();
if (savedGame != null) { if (savedGame != null) {
if (dialogManager.confirmLoadSavedGame()) { if (dialogManager.confirmLoadSavedGame()) {
// Load the saved game with all its state initializeGame(savedGame.getBoard(), primaryStage);
initializeGame(primaryStage, savedGame);
} else { } else {
showBoardSelectionDialog(primaryStage); showBoardSelectionDialog(primaryStage);
} }
@ -47,15 +46,15 @@ public class MainMethod extends Application {
currentBoardName = boardName; currentBoardName = boardName;
int[][] selectedBoard = boardLoader.getBoard(boardName); int[][] selectedBoard = boardLoader.getBoard(boardName);
if (selectedBoard != null) { if (selectedBoard != null) {
initializeGame(primaryStage, new HitoriGameMain(selectedBoard)); initializeGame(selectedBoard, primaryStage);
} }
}, },
() -> System.exit(0) () -> System.exit(0)
); );
} }
private void initializeGame(Stage primaryStage, HitoriGameMain game) { private void initializeGame(int[][] board, Stage primaryStage) {
controller = new GameUIController(game.getBoard(), dialogManager, primaryStage, game); controller = new GameUIController(board, dialogManager);
createGameUI(primaryStage); createGameUI(primaryStage);
} }

View File

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

View File

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

View File

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

View File

@ -1,49 +1,22 @@
package domain; package domain;
import domain.GameSolver;
import java.io.*; import java.io.*;
public class HitoriGameMain extends GameSolver { public class HitoriGameMain extends GameSolver {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final HitoriGameTimer timer; // Timer für das Spiel private final domain.HitoriGameTimer timer;
private final HitoriGameScores scores; // Highscore-Verwaltung private final domain.HitoriGameScores scores;
private long savedTime; // Gespeicherte Zeit für den Timer
public HitoriGameMain(int[][] initialBoard) { public HitoriGameMain(int[][] initialBoard) {
super(initialBoard); super(initialBoard);
this.timer = new HitoriGameTimer(); this.timer = new domain.HitoriGameTimer();
this.scores = new HitoriGameScores(); this.scores = new HitoriGameScores();
this.savedTime = 0;
} }
public HitoriGameTimer getTimer() { // Timer delegation methods
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() { public void startTimer() {
timer.startTimer(); timer.startTimer();
} }
@ -60,12 +33,11 @@ public class HitoriGameMain extends GameSolver {
timer.resetTimer(); timer.resetTimer();
} }
// Gibt die verstrichene Zeit zurück
public long getElapsedTimeInSeconds() { public long getElapsedTimeInSeconds() {
return savedTime > 0 ? savedTime : timer.getElapsedTimeInSeconds(); return timer.getElapsedTimeInSeconds();
} }
// Highscore-Verwaltung // High score delegation methods
public void addHighScore(String playerName, long timeInSeconds) { public void addHighScore(String playerName, long timeInSeconds) {
scores.addHighScore(playerName, timeInSeconds, getMistakeCount()); scores.addHighScore(playerName, timeInSeconds, getMistakeCount());
} }
@ -86,21 +58,7 @@ public class HitoriGameMain extends GameSolver {
scores.saveHighScoresToFile(); scores.saveHighScoresToFile();
} }
// Setzt den Spielzustand // Game state persistence
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() { public void saveGameState() {
try (ObjectOutputStream oos = new ObjectOutputStream( try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("gamestate.dat"))) { new FileOutputStream("gamestate.dat"))) {
@ -110,29 +68,18 @@ public class HitoriGameMain extends GameSolver {
} }
} }
// Lädt das Spiel aus einer Datei
public static HitoriGameMain loadGameState() { public static HitoriGameMain loadGameState() {
try (ObjectInputStream ois = new ObjectInputStream( try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("gamestate.dat"))) { new FileInputStream("gamestate.dat"))) {
HitoriGameMain loadedGame = (HitoriGameMain) ois.readObject(); return (HitoriGameMain) ois.readObject();
loadedGame.timer.resetTimer();
return loadedGame;
} catch (IOException | ClassNotFoundException e) { } catch (IOException | ClassNotFoundException e) {
return null; 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 @Override
public void reset() { public void reset() {
super.reset(); super.reset();
savedTime = 0; // Timer wird nicht zurückgesetzt resetTimer();
} }
} }

View File

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

View File

@ -1,8 +1,5 @@
package domain; package domain;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; import java.io.Serializable;
public class HitoriGameTimer implements Serializable { public class HitoriGameTimer implements Serializable {
@ -11,90 +8,49 @@ public class HitoriGameTimer implements Serializable {
private transient long startTime; private transient long startTime;
private long elapsedTime; private long elapsedTime;
private boolean isPaused; private boolean isPaused;
private long lastPauseTime;
public HitoriGameTimer() { public HitoriGameTimer() {
this.startTime = 0; this.startTime = 0;
this.elapsedTime = 0; this.elapsedTime = 0;
this.isPaused = true; // Timer beginnt pausiert this.isPaused = false;
this.lastPauseTime = 0;
} }
// Timer starten
public void startTimer() { public void startTimer() {
if (isPaused) { if (isPaused || startTime == 0) {
startTime = System.currentTimeMillis(); startTime = System.currentTimeMillis();
isPaused = false; isPaused = false;
if (lastPauseTime > 0) {
startTime = System.currentTimeMillis();
}
} }
} }
// Timer pausieren
public void pauseTimer() { public void pauseTimer() {
if (!isPaused && startTime > 0) { if (!isPaused && startTime > 0) {
lastPauseTime = System.currentTimeMillis(); elapsedTime += System.currentTimeMillis() - startTime;
elapsedTime += lastPauseTime - startTime;
startTime = 0; startTime = 0;
isPaused = true; isPaused = true;
} }
} }
// Timer stoppen
public void stopTimer() { public void stopTimer() {
if (!isPaused && startTime > 0) { if (!isPaused && startTime > 0) {
long stopTime = System.currentTimeMillis(); elapsedTime += System.currentTimeMillis() - startTime;
elapsedTime += stopTime - startTime;
startTime = 0; startTime = 0;
isPaused = true;
lastPauseTime = stopTime;
} }
} }
// Timer zurücksetzen
public void resetTimer() { public void resetTimer() {
startTime = 0; startTime = 0;
elapsedTime = 0; elapsedTime = 0;
isPaused = true; isPaused = false;
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() { public long getElapsedTimeInSeconds() {
if (isPaused) { if (isPaused || startTime == 0) {
return elapsedTime / 1000; return elapsedTime / 1000;
} }
long currentTime = System.currentTimeMillis(); return (elapsedTime + System.currentTimeMillis() - startTime) / 1000;
return (elapsedTime + (currentTime - startTime)) / 1000;
} }
// Gibt zurück, ob der Timer pausiert ist
public boolean isPaused() { public boolean isPaused() {
return 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

@ -0,0 +1,41 @@
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

@ -0,0 +1,83 @@
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

@ -0,0 +1,11 @@
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

@ -0,0 +1,15 @@
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

@ -0,0 +1,29 @@
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

@ -0,0 +1,30 @@
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 @Test
void testReset() { void testReset() {
// Setzt automatisch schwarze Zellen // Directly set some cells (since we can't use markCell methods in base class)
game.blackCells[0][0] = true; game.blackCells[0][0] = true;
game.whiteCells[1][1] = true; game.whiteCells[1][1] = true;
// Reset game // Reset game
game.reset(); game.reset();
// Verifiziert, ob alle schwarzen Zellen gelöscht wurden // Verify all cells are reset
boolean[][] blackCells = game.getBlackCells(); boolean[][] blackCells = game.getBlackCells();
boolean[][] whiteCells = game.getWhiteCells(); boolean[][] whiteCells = game.getWhiteCells();

View File

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

View File

@ -31,14 +31,12 @@ class HitoriBoardPanelTest {
boardPanel = new HitoriBoardPanel(gameMoves, gameSolver, controller); boardPanel = new HitoriBoardPanel(gameMoves, gameSolver, controller);
} }
// Testet, ob das Board korrekt initialisiert wurde
@Test @Test
void testInitialization() { void testInitialization() {
assertNotNull(boardPanel); assertNotNull(boardPanel);
assertEquals(9, boardPanel.getChildren().size()); // 3x3 board = 9 buttons assertEquals(9, boardPanel.getChildren().size()); // 3x3 board = 9 buttons
} }
// Testet, ob die Buttons korrekt erstellt wurden
@Test @Test
void testCellCreation() { void testCellCreation() {
var children = boardPanel.getChildren(); var children = boardPanel.getChildren();
@ -46,7 +44,6 @@ class HitoriBoardPanelTest {
assertEquals("1", firstButton.getText()); assertEquals("1", firstButton.getText());
} }
// Testet das Styling der Buttons
@Test @Test
void testButtonStyling() { void testButtonStyling() {
var children = boardPanel.getChildren(); var children = boardPanel.getChildren();
@ -54,23 +51,21 @@ class HitoriBoardPanelTest {
assertEquals("-fx-background-color: lightgray; -fx-text-fill: black;", button.getStyle()); assertEquals("-fx-background-color: lightgray; -fx-text-fill: black;", button.getStyle());
} }
// Testet das Aktualisieren des Boards
@Test @Test
void testUpdateBoard() { void testUpdateBoard() {
boardPanel.updateBoard(); boardPanel.updateBoard();
assertEquals(9, boardPanel.getChildren().size()); assertEquals(9, boardPanel.getChildren().size());
} }
// Testet die Anzeige von Fehlern auf dem Board
@Test @Test
void testShowErrors() { void testShowErrors() {
// Mark some cells as black to create errors
gameMoves.markCellAsBlack(0, 0); gameMoves.markCellAsBlack(0, 0);
gameMoves.markCellAsBlack(0, 1); gameMoves.markCellAsBlack(0, 1);
boardPanel.showErrors(); boardPanel.showErrors();
// Verifiziere, dass fehlerhafte Zellen rot markiert werden // Verify that error cells are marked red
var children = boardPanel.getChildren(); var children = boardPanel.getChildren();
for (var child : children) { for (var child : children) {
Button button = (Button) child; Button button = (Button) child;

View File

@ -19,22 +19,19 @@ class HitoriControlPanelTest {
controlPanel = new HitoriControlPanel(controller); controlPanel = new HitoriControlPanel(controller);
} }
// Testet, ob das Control-Panel korrekt initialisiert wurde
@Test @Test
void testInitialization() { void testInitialization() {
assertNotNull(controlPanel); assertNotNull(controlPanel);
assertTrue(controlPanel.getChildren().size() > 0); assertTrue(controlPanel.getChildren().size() > 0);
} }
// Testet das Aktualisieren des Timer-Labels
@Test @Test
void testUpdateTimerLabel() { void testUpdateTimerLabel() {
controlPanel.updateTimerLabel(42); controlPanel.updateTimerLabel(42);
var timerLabel = findLabelByText(controlPanel, "Time: 42s"); var timerLabel = findLabelByText(controlPanel, "Time: 42s");
assertNotNull(42); assertNotNull(5);
} }
// Testet das Aktualisieren des Fehler-Labels
@Test @Test
void testUpdateMistakeLabel() { void testUpdateMistakeLabel() {
controlPanel.updateMistakeLabel(5); controlPanel.updateMistakeLabel(5);
@ -42,7 +39,6 @@ class HitoriControlPanelTest {
assertNotNull(5); assertNotNull(5);
} }
// Testet das Setzen des Textes für den Pause-Button
@Test @Test
void testSetPauseButtonText() { void testSetPauseButtonText() {
controlPanel.setPauseButtonText("Test"); controlPanel.setPauseButtonText("Test");
@ -50,7 +46,6 @@ class HitoriControlPanelTest {
assertNotNull(pauseButton); assertNotNull(pauseButton);
} }
// Testet die Aktion des Pause-Buttons
@Test @Test
void testPauseButtonAction() { void testPauseButtonAction() {
var pauseButton = findButtonByText(controlPanel, "Pause"); var pauseButton = findButtonByText(controlPanel, "Pause");
@ -60,7 +55,6 @@ class HitoriControlPanelTest {
verify(controller, times(1)).togglePause(); verify(controller, times(1)).togglePause();
} }
// Testet die Aktion des Reset-Buttons
@Test @Test
void testResetButtonAction() { void testResetButtonAction() {
var resetButton = findButtonByText(controlPanel, "Reset"); var resetButton = findButtonByText(controlPanel, "Reset");
@ -70,7 +64,6 @@ class HitoriControlPanelTest {
verify(controller, times(1)).resetGame(); verify(controller, times(1)).resetGame();
} }
// Testet die Aktion des Undo-Buttons
@Test @Test
void testUndoButtonAction() { void testUndoButtonAction() {
var undoButton = findButtonByText(controlPanel, "Undo"); var undoButton = findButtonByText(controlPanel, "Undo");
@ -80,7 +73,6 @@ class HitoriControlPanelTest {
verify(controller, times(1)).undo(); verify(controller, times(1)).undo();
} }
// Testet die Aktion des Redo-Buttons
@Test @Test
void testRedoButtonAction() { void testRedoButtonAction() {
var redoButton = findButtonByText(controlPanel, "Redo"); var redoButton = findButtonByText(controlPanel, "Redo");
@ -90,7 +82,6 @@ class HitoriControlPanelTest {
verify(controller, times(1)).redo(); verify(controller, times(1)).redo();
} }
// Testet die Aktion des Check-Solution-Buttons
@Test @Test
void testCheckSolutionButtonAction() { void testCheckSolutionButtonAction() {
var checkButton = findButtonByText(controlPanel, "Check Solution"); var checkButton = findButtonByText(controlPanel, "Check Solution");
@ -100,7 +91,6 @@ class HitoriControlPanelTest {
verify(controller, times(1)).checkSolution(); verify(controller, times(1)).checkSolution();
} }
// Testet die Aktion des New-Game-Buttons
@Test @Test
void testNewGameButtonAction() { void testNewGameButtonAction() {
var newGameButton = findButtonByText(controlPanel, "New Game"); var newGameButton = findButtonByText(controlPanel, "New Game");
@ -110,7 +100,6 @@ class HitoriControlPanelTest {
verify(controller, times(1)).newGame(); verify(controller, times(1)).newGame();
} }
// Testet die Aktion des Show-Errors-Buttons
@Test @Test
void testShowErrorsButtonAction() { void testShowErrorsButtonAction() {
var showErrorsButton = findButtonByText(controlPanel, "Show Errors"); var showErrorsButton = findButtonByText(controlPanel, "Show Errors");
@ -120,7 +109,6 @@ class HitoriControlPanelTest {
verify(controller, times(1)).showErrors(); verify(controller, times(1)).showErrors();
} }
// Hilfsmethode: Sucht ein Label mit einem bestimmten Text im Panel
private Label findLabelByText(HitoriControlPanel panel, String text) { private Label findLabelByText(HitoriControlPanel panel, String text) {
return (Label) panel.getChildren().stream() return (Label) panel.getChildren().stream()
.filter(node -> node instanceof Label && ((Label) node).getText().equals(text)) .filter(node -> node instanceof Label && ((Label) node).getText().equals(text))
@ -128,7 +116,6 @@ class HitoriControlPanelTest {
.orElse(null); .orElse(null);
} }
// Hilfsmethode: Sucht einen Button mit einem bestimmten Text im Panel
private Button findButtonByText(HitoriControlPanel panel, String text) { private Button findButtonByText(HitoriControlPanel panel, String text) {
return (Button) panel.getChildren().stream() return (Button) panel.getChildren().stream()
.flatMap(node -> node.lookupAll("Button").stream()) .flatMap(node -> node.lookupAll("Button").stream())

View File

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

View File

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

View File

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

View File

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