Compare commits

..

No commits in common. "f336f1b15b2036f0590976271642f10b4d9d2615" and "d27c7ab4cd92e6d041150f94882313afdbbbf438" have entirely different histories.

21 changed files with 235 additions and 626 deletions

View File

@ -1,113 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId> <groupId>org.example</groupId>
<artifactId>HitoriTeamProjekt</artifactId> <artifactId>HitoriTeamProjekt</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>21</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target> <maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
<dependencies> <dependencies>
<!-- AssertJ Swing Test-Framework -->
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.assertj</groupId>
<artifactId>junit-jupiter-api</artifactId> <artifactId>assertj-swing</artifactId>
<version>5.8.1</version> <version>3.17.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId> <artifactId>junit-jupiter</artifactId>
<version>5.8.1</version> <version>5.8.2</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.logging.log4j</groupId> <groupId>org.mockito</groupId>
<artifactId>log4j-core</artifactId> <artifactId>mockito-core</artifactId>
<version>2.24.2</version> <version>4.5.1</version>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<!-- Compiler -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version> <version>3.11.0</version>
<configuration> <configuration>
<source>${maven.compiler.source}</source> <source>21</source> <!-- Java-Version -->
<target>${maven.compiler.target}</target> <target>21</target> <!-- Java-Version -->
</configuration> </configuration>
</plugin> </plugin>
<!-- JAR creation -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
<version>3.6.0</version> <version>3.2.0</version>
<configuration> <configuration>
<createDependencyReducedPom>false</createDependencyReducedPom> <archive>
<manifest>
<mainClass>PR2.HitoriSpiel.Main.Main</mainClass> <!-- Vollqualifizierter Name deiner Main-Klasse -->
</manifest>
</archive>
</configuration> </configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass> PR2.HitoriSpiel.Main.Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
<!-- Code coverage, cf.: target/site/jacoco -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Static code analysis, cf: target/site/pmd.html -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.26.0</version>
<configuration>
<failOnViolation>false</failOnViolation>
<printFailingErrors>true</printFailingErrors>
</configuration>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<reporting> </project>
<plugins>
<!-- generate Javadocs via "mvn site" and find them in the site
folder -->
</plugins>
</reporting>
</project>

View File

@ -6,4 +6,3 @@ Test4,200,Hitori15x15_medium.csv,10
IOANA VERSUCH 156,105,Hitori5x5_leicht.csv,0 IOANA VERSUCH 156,105,Hitori5x5_leicht.csv,0
IOANA VERSUCH 439,44,Hitori5x5_leicht.csv,0 IOANA VERSUCH 439,44,Hitori5x5_leicht.csv,0
Yovu,46,Hitori4x4_leicht.csv,0 Yovu,46,Hitori4x4_leicht.csv,0
hi,18,Hitori4x4_leicht.csv,0

View File

@ -1,27 +1,11 @@
package PR2.HitoriSpiel.Domain; package PR2.HitoriSpiel.Domain;
/**
* Die Klasse Action repräsentiert eine Aktion im Hitori-Spiel, die eine Änderung
* an einer bestimmten Zelle des Spielfelds beschreibt, einschließlich der Koordinaten,
* des alten Zustands und des neuen Zustands.
*/
public class Action { public class Action {
private final int row; // Zeile, in der die Änderung erfolgt ist private final int row; // Zeile, in der die Änderung erfolgt ist
private final int col; // Spalte, in der die Änderung erfolgt ist private final int col; // Spalte, in der die Änderung erfolgt ist
private final String oldState; // Alter Zustand der Zelle private final String oldState; // Alter Zustand der Zelle
private final String newState; // Neuer Zustand der Zelle private final String newState; // Neuer Zustand der Zelle
/**
* Erstellt ein neues Action-Objekt mit den angegebenen Koordinaten,
* dem alten Zustand und dem neuen Zustand.
*
* @param row Die Zeile, in der die Änderung erfolgt ist.
* @param col Die Spalte, in der die Änderung erfolgt ist.
* @param oldState Der alte Zustand der Zelle.
* @param newState Der neue Zustand der Zelle.
*/
public Action(int row, int col, String oldState, String newState) { public Action(int row, int col, String oldState, String newState) {
this.row = row; this.row = row;
this.col = col; this.col = col;
@ -29,15 +13,6 @@ public class Action {
this.newState = newState; this.newState = newState;
} }
/**
* Erstellt ein neues Action-Objekt mit den angegebenen Koordinaten
* und dem neuen Zustand; der alte Zustand wird auf "GRAY" gesetzt.
*
* @param row Die Zeile, in der die Änderung erfolgt ist.
* @param col Die Spalte, in der die Änderung erfolgt ist.
* @param newState Der neue Zustand der Zelle.
*/
// Alternative Konstruktor für Standardwerte // Alternative Konstruktor für Standardwerte
public Action(int row, int col, String newState) { public Action(int row, int col, String newState) {
this(row, col, "GRAY", newState); this(row, col, "GRAY", newState);
@ -77,13 +52,6 @@ public class Action {
newState.equals(action.newState); newState.equals(action.newState);
} }
/**
* Berechnet den Hashcode für das Action-Objekt basierend
* auf den Attributen row, col, oldState und newState.
*
* @return Der berechnete Hashcode.
*/
@Override @Override
public int hashCode() { public int hashCode() {
int result = row; int result = row;

View File

@ -3,8 +3,7 @@ package PR2.HitoriSpiel.Domain;
import java.util.List; import java.util.List;
/** /**
* Die Klasse HitoriBoard repräsentiert das Spielfeld des Hitori-Spiels. * Represents the Hitori game board.
* Sie enthält die Zellen des Spielfelds, die Lösungskonfiguration und den Namen des Spielfelds.
*/ */
public class HitoriBoard { public class HitoriBoard {
@ -13,15 +12,6 @@ public class HitoriBoard {
private final String boardName; // Name des Spielfelds private final String boardName; // Name des Spielfelds
/**
* Konstruktor, der das Spielfeld mit den Zahlen, der Lösungskonfiguration
* und dem Namen des Spielfelds initialisiert.
*
* @param numbers 2D-Array mit den Zahlen des Spielfelds.
* @param solutionCoordinates Liste der Koordinaten, die zur Lösung gehören.
* @param boardName Name des Spielfelds.
*/
public HitoriBoard(int[][] numbers, List<String> solutionCoordinates, String boardName) { public HitoriBoard(int[][] numbers, List<String> solutionCoordinates, String boardName) {
this.board = new HitoriCell[numbers.length][numbers[0].length]; this.board = new HitoriCell[numbers.length][numbers[0].length];
this.solutionCoordinates = solutionCoordinates; this.solutionCoordinates = solutionCoordinates;
@ -91,6 +81,32 @@ public class HitoriBoard {
} }
} }
/**
* Gibt das gesamte Spielfeld als 2D-Array zurück.
*
* @return 2D-Array mit den Zahlen des Spielfelds.
*/
public int[][] getNumbers() {
int[][] numbers = new int[board.length][board[0].length];
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
numbers[i][j] = board[i][j].getNumber();
}
}
return numbers;
}
/**
* Setzt die Zahlen des Spielfelds basierend auf einem 2D-Array.
*
* @param numbers 2D-Array mit den neuen Zahlen.
*/
public void setNumbers(int[][] numbers) {
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
board[i][j].setNumber(numbers[i][j]);
}
}
}
} }

View File

@ -1,28 +1,17 @@
package PR2.HitoriSpiel.Domain; package PR2.HitoriSpiel.Domain;
/** /**
* Die Klasse HitoriCell repräsentiert eine einzelne Zelle auf dem Hitori-Spielfeld. * Represents a single cell on the Hitori board.
* Sie speichert die Zahl der Zelle und deren Zustand (GRAY, WHITE, BLACK).
*/ */
public class HitoriCell { public class HitoriCell {
/**
* Enum zur Darstellung der möglichen Zustände einer Zelle.
*/
public enum CellState { public enum CellState {
GRAY, WHITE, BLACK GRAY, WHITE, BLACK
} }
private int number; private int number;
private CellState state; private CellState state;
/**
* Konstruktor, der eine Zelle mit einer Zahl erstellt und den Zustand auf GRAY setzt.
*
* @param number Die Zahl der Zelle.
*/
public HitoriCell(int number) { public HitoriCell(int number) {
this.number = number; this.number = number;
this.state = CellState.GRAY; this.state = CellState.GRAY;

View File

@ -3,44 +3,32 @@ package PR2.HitoriSpiel.Domain;
import java.util.List; import java.util.List;
/** /**
* Die Klasse HitoriValidator überprüft, ob das aktuelle Spielfeld des Hitori-Spiels korrekt gelöst ist, * Validates the Hitori board against the solution.
* indem es mit der angegebenen Lösung verglichen wird.
*/ */
public class HitoriValidator { public class HitoriValidator {
private final HitoriBoard board; private final HitoriBoard board;
/**
* Konstruktor, der den Validator mit einem Hitori-Spielfeld initialisiert.
*
* @param board Das zu überprüfende Hitori-Spielfeld.
*/
public HitoriValidator(HitoriBoard board) { public HitoriValidator(HitoriBoard board) {
this.board = board; this.board = board;
} }
/** /**
* Überprüft, ob das aktuelle Spielfeld der Lösung entspricht. * Validates the current board against the solution.
* * @param solutionCoordinates The coordinates of the correct black cells in "row,column" format.
* @param solutionCoordinates Die Koordinaten der korrekten schwarzen Felder im Format "row,column". * @return true if the current board matches the solution, false otherwise.
* @return true, wenn das aktuelle Spielfeld der Lösung entspricht, andernfalls false.
*/ */
public boolean validateBoard(List<String> solutionCoordinates) { public boolean validateBoard(List<String> solutionCoordinates) {
// Überprüfe, ob alle Koordinaten der Lösung korrekt schwarz markiert sind
for (String coordinate : solutionCoordinates) { for (String coordinate : solutionCoordinates) {
String[] parts = coordinate.split(","); String[] parts = coordinate.split(",");
int row = Integer.parseInt(parts[0]) - 1; int row = Integer.parseInt(parts[0]) - 1;
int col = Integer.parseInt(parts[1]) - 1; int col = Integer.parseInt(parts[1]) - 1;
if (board.getCell(row,col).getState() != HitoriCell.CellState.BLACK){ if (board.getCell(row,col).getState() != HitoriCell.CellState.BLACK){
return false; // Ein Feld, das schwarz sein sollte, ist aber nicht return false;
} }
} }
// Überprüfe alle Zellen des Spielfelds
for (int i = 0; i < board.getSize(); i++) { for (int i = 0; i < board.getSize(); i++) {
for (int j = 0; j < board.getSize(); j++) { for (int j = 0; j < board.getSize(); j++) {
HitoriCell cell = board.getCell(i, j); HitoriCell cell = board.getCell(i, j);
@ -56,7 +44,7 @@ public class HitoriValidator {
} }
} }
return true; // Das Spielfeld entspricht der Lösung return true;
} }
} }

View File

@ -1,23 +1,17 @@
package PR2.HitoriSpiel.Domain; package PR2.HitoriSpiel.Domain;
import java.io.*; import java.io.*;
import java.nio.file.*; import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Stack; import java.util.Stack;
/**
* Die Klasse StateFileManager verwaltet das Speichern und Laden der Zustände des Undo- und Redo-Stacks
* in eine Datei. Sie stellt sicher, dass der Speicherort existiert und ermöglicht so die Persistenz der Spielzustände.
*/
public class StateFileManager { public class StateFileManager {
// Speicherort für die Undo/Redo-Datei im Benutzerverzeichnis // Speicherort für die Undo/Redo-Datei im Benutzerverzeichnis
private static final String BASE_DIR = System.getProperty("user.home") + "/HitoriGame"; private static final String BASE_DIR = System.getProperty("user.home") + "/HitoriGame";
private static final String FILE_NAME = BASE_DIR + "/undoRedoLog.txt"; private static final String FILE_NAME = BASE_DIR + "/undoRedoLog.txt";
/** // Stellt sicher, dass der Ordner und die Datei existieren
* Stellt sicher, dass der Ordner und die Datei existieren.
*/
private static void ensureFileExists() { private static void ensureFileExists() {
try { try {
// Erstellt den Ordner, falls er nicht existiert // Erstellt den Ordner, falls er nicht existiert
@ -38,13 +32,7 @@ public class StateFileManager {
} }
} }
/** // Speichert den Zustand in der Datei
* Speichert den Zustand der Undo- und Redo-Stacks in der Datei.
*
* @param undoStack Der aktuelle Undo-Stack.
* @param redoStack Der aktuelle Redo-Stack.
*/
public static void saveState(Stack<Action> undoStack, Stack<Action> redoStack) { public static void saveState(Stack<Action> undoStack, Stack<Action> redoStack) {
ensureFileExists(); // Stellt sicher, dass die Datei existiert ensureFileExists(); // Stellt sicher, dass die Datei existiert
try (BufferedWriter writer = new BufferedWriter(new FileWriter(FILE_NAME))) { try (BufferedWriter writer = new BufferedWriter(new FileWriter(FILE_NAME))) {
@ -66,13 +54,7 @@ public class StateFileManager {
} }
} }
/** // Lädt den Zustand aus der Datei
* Lädt den Zustand der Undo- und Redo-Stacks aus der Datei.
*
* @param undoStack Der Stack, in den die Undo-Zustände geladen werden.
* @param redoStack Der Stack, in den die Redo-Zustände geladen werden.
*/
public static void loadState(Stack<Action> undoStack, Stack<Action> redoStack) { public static void loadState(Stack<Action> undoStack, Stack<Action> redoStack) {
ensureFileExists(); // Stellt sicher, dass die Datei existiert ensureFileExists(); // Stellt sicher, dass die Datei existiert
try (BufferedReader reader = new BufferedReader(new FileReader(FILE_NAME))) { try (BufferedReader reader = new BufferedReader(new FileReader(FILE_NAME))) {

View File

@ -1,24 +0,0 @@
package PR2.HitoriSpiel.Fassade;
import PR2.HitoriSpiel.Domain.HitoriBoard;
import java.util.List;
/**
* Die Klasse BoardManager dient als Fassade, um ein HitoriBoard-Objekt zu erstellen
* und die Spielfeld-Initialisierung zu kapseln.
*/
public class BoardManager {
/**
* Erstellt und gibt ein HitoriBoard-Objekt basierend auf den angegebenen Parametern zurück.
*
* @param boardData Die Daten des Spielfelds.
* @param solutionCoordinates Die Koordinaten der Lösung.
* @param selectedFile Die ausgewählte Datei mit den Spielfeldinformationen.
* @return Das erstellte HitoriBoard-Objekt.
*/
public HitoriBoard createBoard(int[][] boardData, List<String> solutionCoordinates, String selectedFile) {
return new HitoriBoard(boardData, solutionCoordinates, selectedFile);
}
}

View File

@ -10,26 +10,15 @@ import PR2.HitoriSpiel.GUI.StartMenu;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
/**
* Die Klasse GameBoard repräsentiert das Spielfeld des Hitori-Spiels mit zugehörigen Steuerungselementen.
* Sie bietet Funktionalitäten wie Timer, Spielfeldvalidierung, Undo/Redo und Highscore-Management.
*/
public class GameBoard extends JPanel { public class GameBoard extends JPanel {
private final HitoriBoard board; // Das Hitori-Spielfeld-Objekt private final HitoriBoard board;
private Timer timer; // Timer für die Spielzeitmessung private Timer timer;
private long startTime; // Startzeit des Timers private long startTime;
private JLabel timerLabel; // Anzeigeelement für die aktuelle Spielzeit private JLabel timerLabel;
private long pausedTime = 0; // Zeit, die beim Pausieren bereits abgelaufen ist private long pausedTime = 0; // Zeit, die beim Pausieren bereits abgelaufen ist
private JPanel gamePanel; // Das Spielfeld als JPanel private JPanel gamePanel; // Das Spielfeld als JPanel
private final StateManager stateManager = new StateManager(); // Manager für Undo/Redo-Operationen private final StateManager stateManager = new StateManager();
private long elapsedTime = 0;
/**
* Konstruktor, der das Spielfeld und die Steuerungselemente initialisiert.
*
* @param board Das Hitori-Spielfeld.
*/
public GameBoard(HitoriBoard board) { public GameBoard(HitoriBoard board) {
this.board = board; this.board = board;
@ -43,17 +32,13 @@ public class GameBoard extends JPanel {
// Spielfeld erstellen // Spielfeld erstellen
gamePanel = createGamePanel(); gamePanel = createGamePanel();
add(gamePanel, BorderLayout.CENTER); // Spielfeld im Zentrum hinzufügen add(gamePanel, BorderLayout.CENTER);
// Kontroll-Buttons unten hinzufügen // Kontroll-Buttons unten hinzufügen
JPanel controlPanel = createControlPanel(); JPanel controlPanel = createControlPanel();
add(controlPanel, BorderLayout.SOUTH); // Buttons am unteren Rand platzieren add(controlPanel, BorderLayout.SOUTH);
} }
/**
* Initialisiert das Timer-Label.
*/
private void initializeTimerLabel() { private void initializeTimerLabel() {
timerLabel = new JLabel("Zeit: 0 Sekunden"); timerLabel = new JLabel("Zeit: 0 Sekunden");
timerLabel.setHorizontalAlignment(SwingConstants.CENTER); timerLabel.setHorizontalAlignment(SwingConstants.CENTER);
@ -61,12 +46,42 @@ public class GameBoard extends JPanel {
add(timerLabel, BorderLayout.NORTH); add(timerLabel, BorderLayout.NORTH);
} }
private void startTimer() {
if (timer == null) { // Timer nur beim ersten Start erstellen
timer = new Timer(1000, e -> {
long elapsedTime = (System.currentTimeMillis() - startTime + pausedTime) / 1000; // Zeit berechnen
timerLabel.setText("Zeit: " + elapsedTime + " Sekunden");
});
}
startTime = System.currentTimeMillis(); // Startzeit setzen, bevor der Timer gestartet wird
timer.start(); // Timer starten
System.out.println("Startzeit: " + startTime);
System.out.println("Pausierte Zeit: " + pausedTime);
}
private void stopTimer() {
if (timer != null && timer.isRunning()) {
timer.stop(); // Timer anhalten
pausedTime += System.currentTimeMillis() - startTime; // Zeit während der Pause speichern
System.out.println("Timer gestoppt. Pausierte Zeit: " + pausedTime);
}
}
public void resumeTimer() {
startTime = System.currentTimeMillis(); // Startzeit neu setzen
startTimer(); // Timer erneut starten
System.out.println("Timer fortgesetzt. Startzeit: " + startTime + ", Pausierte Zeit: " + pausedTime);
}
private void resetTimer() {
if (timer != null) {
timer.stop();
}
startTime = 0;
pausedTime = 0;
timerLabel.setText("Zeit: 0 Sekunden");
}
/**
* Erstellt das Steuerungspanel mit Kontroll-Buttons.
*
* @return Das erstellte Panel.
*/
private JPanel createControlPanel() { private JPanel createControlPanel() {
JPanel controlPanel = new JPanel(); JPanel controlPanel = new JPanel();
@ -80,36 +95,24 @@ public class GameBoard extends JPanel {
return controlPanel; return controlPanel;
} }
/**
* Erstellt den "Zurücksetzen"-Button.
*
* @return Der Reset-Button.
*/
private JButton createResetButton() { private JButton createResetButton() {
JButton resetButton = Setup.createGameBoardButton("Zurücksetzen", 150, 30); JButton resetButton = Setup.createGameBoardButton("Zurücksetzen", 150, 30);
resetButton.addActionListener(e -> { resetButton.addActionListener(e -> {
//saveStateForUndo(); //saveStateForUndo();
resetBoard(); // Spielfeld zurücksetzen resetBoard();
}); });
return resetButton; return resetButton;
} }
/**
* Erstellt den "Undo"-Button.
*
* @return Der Undo-Button.
*/
private JButton createUndoButton() { private JButton createUndoButton() {
JButton undoButton = Setup.createGameBoardButton("Undo", 80, 30); JButton undoButton = Setup.createGameBoardButton("Undo", 80, 30);
undoButton.addActionListener(e -> { undoButton.addActionListener(e -> {
Action action = stateManager.undo(); // Letzte Aktion rückgängig machen Action action = stateManager.undo();
if (action != null) { if (action != null) {
HitoriCell cell = board.getCell(action.getRow(), action.getCol()); // Betroffene Zelle abrufen HitoriCell cell = board.getCell(action.getRow(), action.getCol());
cell.setState(HitoriCell.CellState.valueOf(action.getOldState())); // Zustand der Zelle zurücksetzen cell.setState(HitoriCell.CellState.valueOf(action.getOldState()));
JButton button = (JButton) gamePanel.getComponent(action.getRow() * board.getSize() + action.getCol()); JButton button = (JButton) gamePanel.getComponent(action.getRow() * board.getSize() + action.getCol());
updateButtonState(button, cell); // Button-Status aktualisieren updateButtonState(button, cell);
} else { } else {
JOptionPane.showMessageDialog(this, "Kein Zustand zum Zurücksetzen vorhanden.", "Undo", JOptionPane.INFORMATION_MESSAGE); JOptionPane.showMessageDialog(this, "Kein Zustand zum Zurücksetzen vorhanden.", "Undo", JOptionPane.INFORMATION_MESSAGE);
} }
@ -118,16 +121,10 @@ public class GameBoard extends JPanel {
return undoButton; return undoButton;
} }
/**
* Erstellt den "Redo"-Button.
*
* @return Der Redo-Button.
*/
private JButton createRedoButton() { private JButton createRedoButton() {
JButton redoButton = Setup.createGameBoardButton("Redo", 80, 30); JButton redoButton = Setup.createGameBoardButton("Redo", 80, 30);
redoButton.addActionListener(e -> { redoButton.addActionListener(e -> {
Action action = stateManager.redo(); // Letzte rückgängig gemachte Aktion wiederherstellen Action action = stateManager.redo();
if (action != null) { if (action != null) {
HitoriCell cell = board.getCell(action.getRow(), action.getCol()); HitoriCell cell = board.getCell(action.getRow(), action.getCol());
cell.setState(HitoriCell.CellState.valueOf(action.getNewState())); cell.setState(HitoriCell.CellState.valueOf(action.getNewState()));
@ -140,18 +137,12 @@ public class GameBoard extends JPanel {
return redoButton; return redoButton;
} }
/**
* Erstellt den "Lösen"-Button.
*
* @return Der Validate-Button.
*/
private JButton createValidateButton() { private JButton createValidateButton() {
JButton validateButton = Setup.createGameBoardButton("Lösen", 80, 30); JButton validateButton = Setup.createGameBoardButton("Lösen", 80, 30);
validateButton.addActionListener(e -> { validateButton.addActionListener(e -> {
if (validateCurrentBoard()) { // Prüfen, ob das Spielfeld korrekt gelöst ist if (validateCurrentBoard()) {
stopTimer(); // Timer anhalten stopTimer();
handleHighscore(); // Highscore bearbeiten handleHighscore();
} else { } else {
JOptionPane.showMessageDialog(this, "Das Spielfeld enthält Fehler!", "Fehler", JOptionPane.ERROR_MESSAGE); JOptionPane.showMessageDialog(this, "Das Spielfeld enthält Fehler!", "Fehler", JOptionPane.ERROR_MESSAGE);
} }
@ -159,12 +150,6 @@ public class GameBoard extends JPanel {
return validateButton; return validateButton;
} }
/**
* Erstellt den "Pause"-Button.
*
* @return Der Pause-Button.
*/
private JButton createPauseButton() { private JButton createPauseButton() {
JButton pauseButton = Setup.createGameBoardButton("Pause", 80, 30); JButton pauseButton = Setup.createGameBoardButton("Pause", 80, 30);
pauseButton.addActionListener(e -> { pauseButton.addActionListener(e -> {
@ -174,28 +159,18 @@ public class GameBoard extends JPanel {
return pauseButton; return pauseButton;
} }
/**
* Validiert das aktuelle Spielfeld gegen die Lösung.
*
* @return true, wenn das Spielfeld korrekt ist, sonst false.
*/
public boolean validateCurrentBoard() { public boolean validateCurrentBoard() {
HitoriValidator validator = new HitoriValidator(board); HitoriValidator validator = new HitoriValidator(board);
return validator.validateBoard(board.getSolutionCoordinates()); // Prüfen, ob das Spielfeld korrekt ist return validator.validateBoard(board.getSolutionCoordinates());
} }
/**
* Verarbeitet die Highscore-Logik, einschließlich der Überprüfung, ob ein neuer Highscore erreicht wurde,
* und speichert den Highscore, wenn der Spieler seinen Namen eingibt.
*/
private void handleHighscore() { private void handleHighscore() {
int elapsedTime = (int) ((System.currentTimeMillis() - startTime) / 1000); // Spielzeit in Sekunden
String boardName = board.getBoardName(); String boardName = board.getBoardName();
int errors = 0; // Aktuelle Fehlerzahl (falls vorhanden, muss noch angepasst werden) int errors = 0; // Aktuelle Fehlerzahl (falls vorhanden, muss noch angepasst werden)
HighscoreManager manager = new HighscoreManager(); HighscoreManager manager = new HighscoreManager();
boolean isNewHighscore = manager.isNewHighscore((int)elapsedTime, boardName); // Prüfen, ob ein neuer Highscore erreicht wurde boolean isNewHighscore = manager.isNewHighscore(elapsedTime, boardName);
// Zeige ein Dialogfenster zur Namenseingabe // Zeige ein Dialogfenster zur Namenseingabe
String playerName = JOptionPane.showInputDialog(this, String playerName = JOptionPane.showInputDialog(this,
@ -211,7 +186,7 @@ public class GameBoard extends JPanel {
} }
// Speichere den Highscore // Speichere den Highscore
manager.addHighscore(playerName, (int)elapsedTime, boardName, errors); manager.addHighscore(playerName, elapsedTime, boardName, errors);
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
isNewHighscore isNewHighscore
@ -225,10 +200,6 @@ public class GameBoard extends JPanel {
} }
/**
* Zeigt das Pausenmenü an.
*/
private void showPauseMenu() { private void showPauseMenu() {
stopTimer(); // Timer pausieren stopTimer(); // Timer pausieren
PauseMenu pauseMenu = new PauseMenu( PauseMenu pauseMenu = new PauseMenu(
@ -241,15 +212,6 @@ public class GameBoard extends JPanel {
pauseMenu.setVisible(true); pauseMenu.setVisible(true);
} }
/**
* Erstellt einen Button für eine Zelle des Spielfelds.
*
* @param cell Die Zelle.
* @param row Die Zeile der Zelle.
* @param col Die Spalte der Zelle.
* @return Der Button.
*/
private JButton createCellButton(HitoriCell cell, int row, int col) { private JButton createCellButton(HitoriCell cell, int row, int col) {
JButton button = new JButton(String.valueOf(cell.getNumber())); JButton button = new JButton(String.valueOf(cell.getNumber()));
button.setBackground(Color.LIGHT_GRAY); button.setBackground(Color.LIGHT_GRAY);
@ -260,14 +222,6 @@ public class GameBoard extends JPanel {
return button; return button;
} }
/**
* Schaltet den Zustand einer Zelle um.
*
* @param cell Die zu toggelnde Zelle.
* @param row Die Zeile der Zelle.
* @param col Die Spalte der Zelle.
*/
public void toggleCellState(HitoriCell cell, int row, int col) { public void toggleCellState(HitoriCell cell, int row, int col) {
if (cell == null) { if (cell == null) {
System.err.println("Ungültige Zelle! Der Zustand kann nicht geändert werden."); System.err.println("Ungültige Zelle! Der Zustand kann nicht geändert werden.");
@ -298,13 +252,6 @@ public class GameBoard extends JPanel {
System.out.println("Aktion gespeichert: " + oldState + " -> " + newState); System.out.println("Aktion gespeichert: " + oldState + " -> " + newState);
} }
/**
* Aktualisiert die Darstellung eines Buttons basierend auf dem Zustand der Zelle.
*
* @param button Der Button.
* @param cell Die Zelle.
*/
private void updateButtonState(JButton button, HitoriCell cell) { private void updateButtonState(JButton button, HitoriCell cell) {
switch (cell.getState()) { switch (cell.getState()) {
case GRAY -> { case GRAY -> {
@ -325,9 +272,22 @@ public class GameBoard extends JPanel {
} }
} }
/** private void addHighscore(int elapsedTime, String boardName, int errors) {
* Kehrt ins Hauptmenü zurück. String playerName = JOptionPane.showInputDialog(this,
*/ "Neuer Highscore! Bitte Namen eingeben:",
"Highscore speichern",
JOptionPane.PLAIN_MESSAGE);
if (playerName == null || playerName.trim().isEmpty()) return;
HighscoreManager manager = new HighscoreManager();
manager.addHighscore(playerName, elapsedTime, boardName, errors);
JOptionPane.showMessageDialog(this,
"Highscore erfolgreich gespeichert!",
"Erfolg",
JOptionPane.INFORMATION_MESSAGE);
}
private void returnToMainMenu() { private void returnToMainMenu() {
/// Eltern-Frame abrufen /// Eltern-Frame abrufen
@ -344,10 +304,6 @@ public class GameBoard extends JPanel {
} }
} }
/**
* Setzt das Spielfeld zurück.
*/
public void resetBoard() { public void resetBoard() {
// Spiellogik zurücksetzen // Spiellogik zurücksetzen
board.resetBoard(); board.resetBoard();
@ -374,7 +330,6 @@ public class GameBoard extends JPanel {
repaint(); repaint();
} }
// Aktualisiert das Spielfeld
private void refreshBoard() { private void refreshBoard() {
remove(1); // Entferne das aktuelle Spielfeld im CENTER remove(1); // Entferne das aktuelle Spielfeld im CENTER
if (getLayout() instanceof BorderLayout) { if (getLayout() instanceof BorderLayout) {
@ -402,12 +357,6 @@ public class GameBoard extends JPanel {
repaint(); repaint();
} }
/**
* Erstellt das Spielfeld als JPanel.
*
* @return Das erstellte JPanel mit Spielfeldzellen.
*/
private JPanel createGamePanel() { private JPanel createGamePanel() {
JPanel gamePanel = new JPanel(new GridLayout(board.getSize(), board.getSize())); JPanel gamePanel = new JPanel(new GridLayout(board.getSize(), board.getSize()));
for (int i = 0; i < board.getSize(); i++) { for (int i = 0; i < board.getSize(); i++) {
@ -432,47 +381,4 @@ public class GameBoard extends JPanel {
} }
// --------Hilfsmethoden---------
// Startet den Timer
private void startTimer() {
if (timer == null) { // Timer nur beim ersten Start erstellen
timer = new Timer(1000, e -> {
elapsedTime = (System.currentTimeMillis() - startTime + pausedTime) / 1000; // Zeit berechnen
timerLabel.setText("Zeit: " + elapsedTime + " Sekunden");
});
}
startTime = System.currentTimeMillis(); // Startzeit setzen, bevor der Timer gestartet wird
timer.start(); // Timer starten
System.out.println("Startzeit: " + startTime);
System.out.println("Pausierte Zeit: " + pausedTime);
}
// Hält den Timer an
private void stopTimer() {
if (timer != null && timer.isRunning()) {
timer.stop(); // Timer anhalten
pausedTime += System.currentTimeMillis() - startTime; // Zeit während der Pause speichern
System.out.println("Timer gestoppt. Pausierte Zeit: " + pausedTime);
}
}
// Setzt den Timer fort
public void resumeTimer() {
startTime = System.currentTimeMillis(); // Startzeit neu setzen
startTimer(); // Timer erneut starten
System.out.println("Timer fortgesetzt. Startzeit: " + startTime + ", Pausierte Zeit: " + pausedTime);
}
// Setzt den Timer zurück
private void resetTimer() {
if (timer != null) {
timer.stop();
}
startTime = 0;
pausedTime = 0;
timerLabel.setText("Zeit: 0 Sekunden");
}
} }

View File

@ -1,6 +1,5 @@
package PR2.HitoriSpiel.Fassade; package PR2.HitoriSpiel.Fassade;
// TODO: Spiel fortsetzen
public class GameManager { public class GameManager {
private static GameBoard gameBoard; private static GameBoard gameBoard;

View File

@ -4,12 +4,6 @@ import java.io.*;
import java.util.*; import java.util.*;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
/**
* Die Klasse HighscoreManager verwaltet die Highscores für das Hitori-Spiel.
* Sie unterstützt das Hinzufügen, Speichern, Laden und Analysieren von Highscore-Daten
* sowie die Berechnung von Durchschnittszeiten pro Spielfeld.
*/
public class HighscoreManager { public class HighscoreManager {
private static final String HIGHSCORE_FILE = "Hitori/src/main/highscores/highscores.txt"; private static final String HIGHSCORE_FILE = "Hitori/src/main/highscores/highscores.txt";
@ -17,22 +11,11 @@ public class HighscoreManager {
// Highscore-Datenstruktur // Highscore-Datenstruktur
private final List<Highscore> highscoreList = new ArrayList<>(); private final List<Highscore> highscoreList = new ArrayList<>();
/**
* Konstruktor, der Highscores aus der Datei lädt.
*/
public HighscoreManager() { public HighscoreManager() {
loadHighscores(); loadHighscores();
} }
/** // Prüft, ob die gegebene Zeit ein neuer Highscore ist
* Überprüft, ob die gegebene Zeit ein neuer Highscore für das angegebene Spielfeld ist.
*
* @param elapsedTime Die benötigte Zeit.
* @param boardName Der Name des Spielfelds.
* @return true, wenn es ein neuer Highscore ist, sonst false.
*/
public boolean isNewHighscore(int elapsedTime, String boardName) { public boolean isNewHighscore(int elapsedTime, String boardName) {
// Filtere die Highscores für das gegebene Spielfeld // Filtere die Highscores für das gegebene Spielfeld
List<Highscore> highscoresForBoard = getHighscoresForBoard(boardName); List<Highscore> highscoresForBoard = getHighscoresForBoard(boardName);
@ -51,27 +34,13 @@ public class HighscoreManager {
return elapsedTime < bestTime; return elapsedTime < bestTime;
} }
/** // Highscore hinzufügen
* Fügt einen neuen Highscore hinzu und speichert die aktualisierten Highscores.
*
* @param playerName Der Name des Spielers.
* @param time Die benötigte Zeit.
* @param boardName Der Name des Spielfelds.
* @param errors Die Anzahl der Fehler.
*/
public synchronized void addHighscore(String playerName, int time, String boardName, int errors) { public synchronized void addHighscore(String playerName, int time, String boardName, int errors) {
highscoreList.add(new Highscore(playerName, time, boardName, errors)); highscoreList.add(new Highscore(playerName, time, boardName, errors));
saveHighscores(); saveHighscores();
} }
/** // Highscores für ein bestimmtes Spielfeld abrufen
* Gibt die Highscores für ein bestimmtes Spielfeld zurück, sortiert nach kürzester Zeit.
*
* @param boardName Der Name des Spielfelds.
* @return Eine Liste der Top 10 Highscores für das Spielfeld.
*/
public List<Highscore> getHighscoresForBoard(String boardName) { public List<Highscore> getHighscoresForBoard(String boardName) {
return highscoreList.stream() return highscoreList.stream()
.filter(highscore -> highscore.getBoardName().equals(boardName)) .filter(highscore -> highscore.getBoardName().equals(boardName))
@ -80,10 +49,7 @@ public class HighscoreManager {
.toList(); .toList();
} }
/** // Highscores speichern
* Speichert die Highscores in einer Datei.
*/
private void saveHighscores() { private void saveHighscores() {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(HIGHSCORE_FILE))) { try (BufferedWriter writer = new BufferedWriter(new FileWriter(HIGHSCORE_FILE))) {
for (Highscore highscore : highscoreList) { for (Highscore highscore : highscoreList) {
@ -95,10 +61,7 @@ public class HighscoreManager {
} }
} }
/** // Highscores laden
* Lädt die Highscores aus einer Datei.
*/
private void loadHighscores() { private void loadHighscores() {
try (BufferedReader reader = new BufferedReader(new FileReader(HIGHSCORE_FILE))) { try (BufferedReader reader = new BufferedReader(new FileReader(HIGHSCORE_FILE))) {
String line; String line;
@ -131,12 +94,7 @@ public class HighscoreManager {
} }
} }
/** // Durchschnittszeit für jedes Spielfeld berechnen
* Berechnet die Durchschnittszeiten für jedes Spielfeld.
*
* @return Eine Map mit Spielfeldnamen und deren Durchschnittszeiten.
*/
public Map<String, Double> getAverageTimesByBoard() { public Map<String, Double> getAverageTimesByBoard() {
fileLock.lock(); fileLock.lock();
try { try {
@ -159,20 +117,12 @@ public class HighscoreManager {
} }
} }
/** // Highscores abrufen
* Gibt alle Highscores zurück.
*
* @return Eine Liste aller Highscores.
*/
public List<Highscore> getHighscores() { public List<Highscore> getHighscores() {
return new ArrayList<>(highscoreList); // Modifizierbare Kopie zurückgeben return new ArrayList<>(highscoreList); // Modifizierbare Kopie zurückgeben
} }
/** // Löscht alle Highscores
* Löscht alle Highscores und aktualisiert die Datei.
*/
public void clearHighscores() { public void clearHighscores() {
fileLock.lock(); fileLock.lock();
try { try {
@ -183,25 +133,13 @@ public class HighscoreManager {
} }
} }
/** // Innere Highscore-Klasse
* Innere Klasse, die einen Highscore repräsentiert.
*/
public static class Highscore { public static class Highscore {
private final String playerName; private final String playerName;
private final int time; private final int time;
private final String boardName; private final String boardName;
private final int errors; private final int errors;
/**
* Konstruktor für einen Highscore-Eintrag.
*
* @param playerName Der Name des Spielers.
* @param score Die benötigte Zeit.
* @param boardName Der Name des Spielfelds.
* @param errors Die Anzahl der Fehler.
*/
public Highscore(String playerName, int score, String boardName, int errors) { public Highscore(String playerName, int score, String boardName, int errors) {
this.playerName = playerName; this.playerName = playerName;
this.time = score; this.time = score;

View File

@ -4,20 +4,10 @@ import java.io.*;
import java.util.*; import java.util.*;
/** /**
* Die Klasse HitoriSolutionLoader ist eine Hilfsklasse zum Laden der Lösung für ein Hitori-Spielfeld aus einer CSV-Datei. * Utility class to load the solution from a CSV file.
* Die Lösung wird im Abschnitt "//Lösung" der Datei erwartet und enthält die Koordinaten der schwarzen Zellen.
*/ */
public class HitoriSolutionLoader { public class HitoriSolutionLoader {
/**
* Lädt die Lösung für ein Hitori-Spielfeld aus einer Ressourcendatei.
*
* @param resourcePath Der Pfad zur Ressourcendatei.
* @return Eine Liste der Koordinaten der schwarzen Zellen im Format "row,column".
* @throws IOException Wenn die Datei nicht gefunden wird oder ein Lesefehler auftritt.
*/
public static List<String> loadSolution(String resourcePath) throws IOException { public static List<String> loadSolution(String resourcePath) throws IOException {
List<String> solutionCoordinates = new ArrayList<>(); List<String> solutionCoordinates = new ArrayList<>();
boolean solutionSectionFound = false; boolean solutionSectionFound = false;

View File

@ -5,10 +5,6 @@ import java.awt.*;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
/**
* Die Klasse Setup bietet zentrale Methoden und Konstanten zur Gestaltung und Vereinheitlichung der GUI-Komponenten für das Hitori-Spiel.
*/
public class Setup { public class Setup {
// Standardfarben // Standardfarben

View File

@ -6,38 +6,16 @@ import PR2.HitoriSpiel.Domain.StateFileManager;
import java.io.*; import java.io.*;
import java.util.Stack; import java.util.Stack;
/**
* Die Klasse StateManager verwaltet die Undo- und Redo-Funktionalität für das Hitori-Spiel
* und speichert Zustandsänderungen in einer Datei.
*/
public class StateManager implements Serializable { public class StateManager implements Serializable {
private final Stack<Action> undoStack = new Stack<>(); private final Stack<Action> undoStack = new Stack<>();
private final Stack<Action> redoStack = new Stack<>(); private final Stack<Action> redoStack = new Stack<>();
/**
* Speichert eine neue Aktion im Undo-Stack und leert den Redo-Stack.
* Speichert zudem den aktuellen Zustand in einer Datei.
*
* @param row Die Zeile der Aktion.
* @param col Die Spalte der Aktion.
* @param oldState Der alte Zustand.
* @param newState Der neue Zustand.
*/
public void saveAction(int row, int col, String oldState, String newState) { public void saveAction(int row, int col, String oldState, String newState) {
undoStack.push(new Action(row, col, oldState, newState)); undoStack.push(new Action(row, col, oldState, newState));
redoStack.clear(); // Redo-Stack leeren, da ein neuer Zustand hinzugefügt wurde redoStack.clear(); // Redo-Stack leeren, da ein neuer Zustand hinzugefügt wurde
StateFileManager.saveState(undoStack, redoStack); // Speichern in Datei StateFileManager.saveState(undoStack, redoStack); // Speichern in Datei
} }
/**
* Führt eine Undo-Aktion aus, verschiebt die Aktion in den Redo-Stack
* und speichert den aktuellen Zustand in einer Datei.
*
* @return Die rückgängig gemachte Aktion oder null, wenn der Undo-Stack leer ist.
*/
public Action undo() { public Action undo() {
if (!undoStack.isEmpty()) { if (!undoStack.isEmpty()) {
Action action = undoStack.pop(); Action action = undoStack.pop();
@ -48,13 +26,6 @@ public class StateManager implements Serializable {
return null; return null;
} }
/**
* Führt eine Redo-Aktion aus, verschiebt die Aktion in den Undo-Stack
* und speichert den aktuellen Zustand in einer Datei.
*
* @return Die wiederhergestellte Aktion oder null, wenn der Redo-Stack leer ist.
*/
public Action redo() { public Action redo() {
if (!redoStack.isEmpty()) { if (!redoStack.isEmpty()) {
Action action = redoStack.pop(); Action action = redoStack.pop();
@ -65,10 +36,6 @@ public class StateManager implements Serializable {
return null; return null;
} }
/**
* Lädt die Undo- und Redo-Stacks aus einer Datei, um den letzten gespeicherten Zustand wiederherzustellen.
*/
public void loadStateFromFile() { public void loadStateFromFile() {
StateFileManager.loadState(undoStack, redoStack); // Stacks aus Datei laden StateFileManager.loadState(undoStack, redoStack); // Stacks aus Datei laden
} }

View File

@ -5,19 +5,25 @@ import java.net.URLDecoder;
import java.util.*; import java.util.*;
import java.util.jar.*; import java.util.jar.*;
/**
* Die Klasse BoardLoader lädt Spielfeld-Dateien für das Hitori-Spiel aus dem Dateisystem oder einem JAR-Archiv
* und bietet Funktionen zum Parsen der Dateien in ein 2D-Array.
*/
public class BoardLoader { public class BoardLoader {
/** public static class BoardData {
* Lädt eine Liste von Spielfelddateien aus dem Verzeichnis oder dem JAR-Archiv. private final int[][] board;
* Sortiert die Dateien nach Schwierigkeitsgrad (leicht, medium) und Größe. private final List<String> solution;
*
* @return Eine Liste der gefundenen Spielfelddateien. public BoardData(int[][] board, List<String> solution) {
*/ this.board = board;
this.solution = solution;
}
public int[][] getBoard() {
return board;
}
public List<String> getSolution() {
return solution;
}
}
public static List<String> loadBoardsAsList() { public static List<String> loadBoardsAsList() {
List<String> boardFiles = new ArrayList<>(); List<String> boardFiles = new ArrayList<>();
@ -81,13 +87,8 @@ public class BoardLoader {
return boardFiles; return boardFiles;
} }
/**
* Lädt ein Spielfeld aus einer Ressourcendatei und gibt es als 2D-Array zurück.
*
* @param resourcePath Der Pfad zur Ressourcendatei.
* @return Ein 2D-Array, das das geladene Spielfeld darstellt.
* @throws IOException Wenn die Datei nicht gefunden oder fehlerhaft ist.
*/
public static int[][] loadBoard(String resourcePath) throws IOException { public static int[][] loadBoard(String resourcePath) throws IOException {
List<int[]> rows = new ArrayList<>(); List<int[]> rows = new ArrayList<>();
@ -141,10 +142,6 @@ public class BoardLoader {
} }
//--------------------------Hilfsmethoden----------------------------------
// Hilfsmethode zum Extrahieren der Größe aus dem Dateinamen // Hilfsmethode zum Extrahieren der Größe aus dem Dateinamen
private static int extractSize(String fileName) { private static int extractSize(String fileName) {
try { try {

View File

@ -2,34 +2,22 @@ package PR2.HitoriSpiel.GUI;
import PR2.HitoriSpiel.Fassade.HighscoreManager; import PR2.HitoriSpiel.Fassade.HighscoreManager;
import PR2.HitoriSpiel.Fassade.Setup; import PR2.HitoriSpiel.Fassade.Setup;
import javax.swing.table.DefaultTableModel; import javax.swing.table.DefaultTableModel;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.util.*; import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
// aktueller Stand
/**
* Die Klasse HighscoreDialog erstellt ein Dialogfenster, das die Highscores für verschiedene Spielfelder des Hitori-Spiels
* anzeigt und verwaltet. Sie ermöglicht die Auswahl eines Spielfelds, das Anzeigen der zugehörigen Highscores und die
* Berechnung der Durchschnittszeiten.
*/
public class HighscoreDialog extends JDialog { public class HighscoreDialog extends JDialog {
private final HighscoreManager highscoreManager; private final HighscoreManager highscoreManager;
private final DefaultTableModel tableModel; private final DefaultTableModel tableModel;
private final JComboBox<String> boardSelector; private final JComboBox<String> boardSelector;
/**
* Konstruktor, der das Highscore-Dialogfenster erstellt und initialisiert.
*
* @param parentFrame Das übergeordnete Fenster, in dem der Dialog angezeigt wird.
* @param boardNames Eine Liste von Spielfelddateinamen.
*/
public HighscoreDialog(JFrame parentFrame, List<String> boardNames) { public HighscoreDialog(JFrame parentFrame, List<String> boardNames) {
super(parentFrame, "Highscoreliste", true); super(parentFrame, "Highscoreliste", true);
@ -76,6 +64,7 @@ public class HighscoreDialog extends JDialog {
closeButton.setFont(new Font(closeButton.getFont().getName(), closeButton.getFont().getStyle(), 18)); // Schriftgröße ändern closeButton.setFont(new Font(closeButton.getFont().getName(), closeButton.getFont().getStyle(), 18)); // Schriftgröße ändern
closeButton.addActionListener(e -> dispose()); closeButton.addActionListener(e -> dispose());
buttonPanel.add(closeButton); buttonPanel.add(closeButton);
Setup.stylePanel(buttonPanel); // Hintergrundstil setzen Setup.stylePanel(buttonPanel); // Hintergrundstil setzen
add(buttonPanel, BorderLayout.SOUTH); add(buttonPanel, BorderLayout.SOUTH);
@ -86,11 +75,6 @@ public class HighscoreDialog extends JDialog {
} }
} }
/**
* Lädt die Highscores für ein ausgewähltes Spielfeld und aktualisiert die Tabelle.
*
* @param boardName Der Name des ausgewählten Spielfelds.
*/
private void loadHighscoresForBoard(String boardName) { private void loadHighscoresForBoard(String boardName) {
tableModel.setRowCount(0); // Tabelle zurücksetzen tableModel.setRowCount(0); // Tabelle zurücksetzen

View File

@ -7,24 +7,9 @@ import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
/**
* Die Klasse PauseMenu erstellt ein Dialogfenster, das während einer Pause im Hitori-Spiel angezeigt wird.
* Sie bietet Optionen, das Spiel fortzusetzen, zum Hauptmenü zurückzukehren oder das Spiel zu beenden.
*/
public class PauseMenu extends JDialog { public class PauseMenu extends JDialog {
private final GameBoard gameBoard; private final GameBoard gameBoard;
/**
* Konstruktor, der das Pause-Menü erstellt und die entsprechenden Buttons konfiguriert.
*
* @param parent Das übergeordnete Fenster, in dem der Dialog angezeigt wird.
* @param gameBoard Das aktuelle GameBoard-Objekt, um den Timer fortzusetzen.
* @param resumeAction Aktion, die ausgeführt wird, wenn der "Spiel fortsetzen"-Button geklickt wird.
* @param mainMenuAction Aktion, die ausgeführt wird, wenn der "Zum Hauptmenü"-Button geklickt wird.
* @param exitAction Aktion, die ausgeführt wird, wenn der "Spiel beenden"-Button geklickt wird.
*/
public PauseMenu(JFrame parent, GameBoard gameBoard, ActionListener resumeAction, ActionListener mainMenuAction, ActionListener exitAction) { public PauseMenu(JFrame parent, GameBoard gameBoard, ActionListener resumeAction, ActionListener mainMenuAction, ActionListener exitAction) {
super(parent, "Pause", true); super(parent, "Pause", true);
this.gameBoard = gameBoard; // Instanz speichern this.gameBoard = gameBoard; // Instanz speichern

View File

@ -1,31 +1,22 @@
package PR2.HitoriSpiel.GUI; package PR2.HitoriSpiel.GUI;
import PR2.HitoriSpiel.Fassade.BoardManager; import PR2.HitoriSpiel.Domain.HitoriBoard;
import PR2.HitoriSpiel.Fassade.GameBoard; import PR2.HitoriSpiel.Fassade.GameBoard;
import PR2.HitoriSpiel.Fassade.HitoriSolutionLoader; import PR2.HitoriSpiel.Fassade.HitoriSolutionLoader;
import PR2.HitoriSpiel.Fassade.Setup; import PR2.HitoriSpiel.Fassade.Setup;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.util.*; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Random;
import static PR2.HitoriSpiel.GUI.BoardLoader.loadBoard; import static PR2.HitoriSpiel.GUI.BoardLoader.loadBoard;
/**
* Die Klasse StartMenu erstellt das Hauptmenü des Hitori-Spiels und bietet Optionen wie Spielfeldauswahl,
* Fortsetzen eines Spiels, Start eines zufälligen Spielfelds, Highscore-Anzeige und Spielbeendigung.
*/
public class StartMenu extends JPanel { public class StartMenu extends JPanel {
private final JFrame parentFrame; private final JFrame parentFrame;
/**
* Konstruktor, der das Hauptmenü erstellt und alle relevanten Buttons und Aktionen initialisiert.
*
* @param parentFrame Das übergeordnete JFrame-Fenster, in dem das Menü angezeigt wird.
*/
public StartMenu(JFrame parentFrame) { public StartMenu(JFrame parentFrame) {
this.parentFrame = parentFrame; this.parentFrame = parentFrame;
setLayout(new GridBagLayout()); setLayout(new GridBagLayout());
@ -65,21 +56,11 @@ public class StartMenu extends JPanel {
} }
/**
* Aktion für den "Spiel fortsetzen"-Button.
* Zeigt einen Hinweis an, da die Funktion noch nicht implementiert ist.
*/
public void continueGame() { public void continueGame() {
// TODO: Logik zum Fortsetzen des Spiels implementieren // TODO: Logik zum Fortsetzen des Spiels implementieren
JOptionPane.showMessageDialog(this, "Spiel fortsetzen wurde angeklickt"); JOptionPane.showMessageDialog(this, "Spiel fortsetzen wurde angeklickt");
} }
/**
* Aktion für den "Spielfeld aussuchen"-Button.
* Öffnet ein Dialogfenster zur Auswahl eines Spielfelds aus der verfügbaren Liste.
*/
public void selectBoard() { public void selectBoard() {
// Lade die Liste der Dateinamen // Lade die Liste der Dateinamen
@ -128,10 +109,6 @@ public class StartMenu extends JPanel {
} }
} }
/**
* Aktion für den "Zufälliges Spielfeld"-Button.
* Wählt ein zufälliges Spielfeld aus der Liste und lädt es.
*/
public void randomBoard() { public void randomBoard() {
List<String> boardFileNames = BoardLoader.loadBoardsAsList(); List<String> boardFileNames = BoardLoader.loadBoardsAsList();
@ -144,10 +121,6 @@ public class StartMenu extends JPanel {
loadAndDisplayBoard(randomFile); loadAndDisplayBoard(randomFile);
} }
/**
* Aktion für den "Highscoreliste anschauen"-Button.
* Öffnet das Highscore-Dialogfenster.
*/
public void highscorelist() { public void highscorelist() {
List<String> boardNames = BoardLoader.loadBoardsAsList(); // Lade die Spielfeldnamen List<String> boardNames = BoardLoader.loadBoardsAsList(); // Lade die Spielfeldnamen
@ -159,12 +132,6 @@ public class StartMenu extends JPanel {
new HighscoreDialog((JFrame) SwingUtilities.getWindowAncestor(this), boardNames).setVisible(true); new HighscoreDialog((JFrame) SwingUtilities.getWindowAncestor(this), boardNames).setVisible(true);
} }
/**
* Lädt ein Spielfeld aus einer Datei und zeigt es im Hauptfenster an.
*
* @param selectedFile Der Name der ausgewählten Datei.
*/
private void loadAndDisplayBoard(String selectedFile) { private void loadAndDisplayBoard(String selectedFile) {
try { try {
String resourcePath = "/persistent/Hitori_Spielfelder/" + selectedFile; String resourcePath = "/persistent/Hitori_Spielfelder/" + selectedFile;
@ -173,10 +140,8 @@ public class StartMenu extends JPanel {
int[][] boardData = loadBoard(resourcePath); int[][] boardData = loadBoard(resourcePath);
java.util.List<String> solutionCoordinates = HitoriSolutionLoader.loadSolution(resourcePath); java.util.List<String> solutionCoordinates = HitoriSolutionLoader.loadSolution(resourcePath);
//HitoriBoard hitoriBoard = new HitoriBoard(boardData, solutionCoordinates, selectedFile); // Stelle sicher, dass die Lösung korrekt geladen wird HitoriBoard hitoriBoard = new HitoriBoard(boardData, solutionCoordinates, selectedFile); // Stelle sicher, dass die Lösung korrekt geladen wird
// BoardManager-Instanz erstellen GameBoard gameBoard = new GameBoard(hitoriBoard);
BoardManager boardManager = new BoardManager();
GameBoard gameBoard = new GameBoard(boardManager.createBoard(boardData, solutionCoordinates, selectedFile));
loadGameBoard(gameBoard, solutionCoordinates); loadGameBoard(gameBoard, solutionCoordinates);
System.out.println("Ausgewählte Datei: " + selectedFile); System.out.println("Ausgewählte Datei: " + selectedFile);
@ -187,9 +152,7 @@ public class StartMenu extends JPanel {
} }
} }
// --------------------Hilfsmethoden-------------------------------------------- // Hilfsmethoden
//Aktualisiert die Benutzeroberfläche, um das geladene Spielfeld anzuzeigen
private void loadGameBoard(GameBoard gameBoard, java.util.List<String> solutionCoordinates) { private void loadGameBoard(GameBoard gameBoard, java.util.List<String> solutionCoordinates) {
removeAll(); removeAll();
setLayout(new BorderLayout()); setLayout(new BorderLayout());

View File

@ -1,6 +1,7 @@
package PR2.HitoriSpiel.Main; package PR2.HitoriSpiel.Main;
import PR2.HitoriSpiel.GUI.StartMenu; import PR2.HitoriSpiel.GUI.StartMenu;
import PR2.HitoriSpiel.Fassade.StateManager;
import javax.swing.*; import javax.swing.*;
/** /**
@ -10,6 +11,9 @@ import javax.swing.*;
public class Main { public class Main {
public static void main(String[] args) { public static void main(String[] args) {
// Initialisiere den StateManager
StateManager stateManager = new StateManager();
// Füge einen Shutdown-Hook hinzu, um Zustände bei Beendigung zu speichern // Füge einen Shutdown-Hook hinzu, um Zustände bei Beendigung zu speichern
Runtime.getRuntime().addShutdownHook(new Thread(() -> { Runtime.getRuntime().addShutdownHook(new Thread(() -> {
//stateManager.saveStateToFile(); // Speichert die aktuellen Zustände in einer Datei //stateManager.saveStateToFile(); // Speichert die aktuellen Zustände in einer Datei

View File

@ -72,10 +72,6 @@ package PR2.HitoriSpiel.Fassade {
+ addHighscore(String playerName, int score, String boardName, int errors): void + addHighscore(String playerName, int score, String boardName, int errors): void
+ getHighscoresForBoard(String boardName): List<Highscore> + getHighscoresForBoard(String boardName): List<Highscore>
} }
class BoardManager {
+ createBoard(int[][] boardData, List<String> solutionCoordinates, String selectedFile): HitoriBoard
}
} }
package PR2.HitoriSpiel.GUI { package PR2.HitoriSpiel.GUI {
@ -111,7 +107,7 @@ package PR2.HitoriSpiel.Main {
PR2.HitoriSpiel.Domain.HitoriBoard "1" *-- "many" PR2.HitoriSpiel.Domain.HitoriCell PR2.HitoriSpiel.Domain.HitoriBoard "1" *-- "many" PR2.HitoriSpiel.Domain.HitoriCell
PR2.HitoriSpiel.Domain.StateManager "1" *-- "many" PR2.HitoriSpiel.Domain.Action PR2.HitoriSpiel.Domain.StateManager "1" *-- "many" PR2.HitoriSpiel.Domain.Action
PR2.HitoriSpiel.Fassade.GameBoard "1" *-- "1" PR2.HitoriSpiel.Domain.HitoriBoard PR2.HitoriSpiel.Fassade.GameBoard "1" *-- "1" PR2.HitoriSpiel.Domain.HitoriBoard
PR2.HitoriSpiel.Fassade.BoardManager ..> PR2.HitoriSpiel.GUI.StartMenu PR2.HitoriSpiel.GUI.BoardLoader ..> PR2.HitoriSpiel.Domain.HitoriBoard
PR2.HitoriSpiel.GUI.HighscoreDialog ..> PR2.HitoriSpiel.Fassade.HighscoreManager PR2.HitoriSpiel.GUI.HighscoreDialog ..> PR2.HitoriSpiel.Fassade.HighscoreManager
PR2.HitoriSpiel.GUI.PauseMenu --> PR2.HitoriSpiel.Fassade.GameBoard PR2.HitoriSpiel.GUI.PauseMenu --> PR2.HitoriSpiel.Fassade.GameBoard
PR2.HitoriSpiel.GUI.StartMenu --> PR2.HitoriSpiel.GUI.BoardLoader PR2.HitoriSpiel.GUI.StartMenu --> PR2.HitoriSpiel.GUI.BoardLoader

View File

@ -25,4 +25,12 @@ public class HitoriBoardTest {
assertEquals(HitoriCell.CellState.GRAY, board.getCell(0, 0).getState()); assertEquals(HitoriCell.CellState.GRAY, board.getCell(0, 0).getState());
} }
@Test
public void testSetAndGetNumbers() {
int[][] initialNumbers = {{9, 10}, {11, 12}};
HitoriBoard board = new HitoriBoard(initialNumbers, Arrays.asList(), "SetNumbersTest");
int[][] newNumbers = {{1, 2}, {3, 4}};
board.setNumbers(newNumbers);
assertArrayEquals(newNumbers, board.getNumbers());
}
} }