diff --git a/Hitori/src/main/java/PR2/HitoriSpiel/Domain/Action.java b/Hitori/src/main/java/PR2/HitoriSpiel/Domain/Action.java new file mode 100644 index 0000000..b15c8eb --- /dev/null +++ b/Hitori/src/main/java/PR2/HitoriSpiel/Domain/Action.java @@ -0,0 +1,63 @@ +package PR2.HitoriSpiel.Domain; + +public class Action { + private final int row; // Zeile, 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 newState; // Neuer Zustand der Zelle + + public Action(int row, int col, String oldState, String newState) { + this.row = row; + this.col = col; + this.oldState = oldState; + this.newState = newState; + } + + // Alternative Konstruktor für Standardwerte + public Action(int row, int col, String newState) { + this(row, col, "GRAY", newState); + } + + public int getRow() { + return row; + } + + public int getCol() { + return col; + } + + public String getOldState() { + return oldState; + } + + public String getNewState() { + return newState; + } + + @Override + public String toString() { + return row + "," + col + "," + oldState + "," + newState; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Action action = (Action) o; + + return row == action.row && + col == action.col && + oldState.equals(action.oldState) && + newState.equals(action.newState); + } + + @Override + public int hashCode() { + int result = row; + result = 31 * result + col; + result = 31 * result + oldState.hashCode(); + result = 31 * result + newState.hashCode(); + return result; + } +} diff --git a/Hitori/src/main/java/PR2/HitoriSpiel/Domain/StateFileManager.java b/Hitori/src/main/java/PR2/HitoriSpiel/Domain/StateFileManager.java new file mode 100644 index 0000000..ca02e0f --- /dev/null +++ b/Hitori/src/main/java/PR2/HitoriSpiel/Domain/StateFileManager.java @@ -0,0 +1,83 @@ +package PR2.HitoriSpiel.Domain; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Stack; + +public class StateFileManager { + // Speicherort für die Undo/Redo-Datei im Benutzerverzeichnis + private static final String BASE_DIR = System.getProperty("user.home") + "/HitoriGame"; + private static final String FILE_NAME = BASE_DIR + "/undoRedoLog.txt"; + + // Stellt sicher, dass der Ordner und die Datei existieren + private static void ensureFileExists() { + try { + // Erstellt den Ordner, falls er nicht existiert + Path directoryPath = Paths.get(BASE_DIR); + if (!Files.exists(directoryPath)) { + Files.createDirectories(directoryPath); + System.out.println("Ordner erstellt: " + BASE_DIR); + } + + // Erstellt die Datei, falls sie nicht existiert + Path filePath = Paths.get(FILE_NAME); + if (!Files.exists(filePath)) { + Files.createFile(filePath); + System.out.println("Datei erstellt: " + FILE_NAME); + } + } catch (IOException e) { + System.err.println("Fehler beim Erstellen von Ordner oder Datei: " + e.getMessage()); + } + } + + // Speichert den Zustand in der Datei + public static void saveState(Stack undoStack, Stack redoStack) { + ensureFileExists(); // Stellt sicher, dass die Datei existiert + try (BufferedWriter writer = new BufferedWriter(new FileWriter(FILE_NAME))) { + writer.write("UNDO"); // Kennzeichnung für Undo-Stack + writer.newLine(); + for (Action action : undoStack) { + writer.write(action.toString()); + writer.newLine(); + } + + writer.write("REDO"); // Kennzeichnung für Redo-Stack + writer.newLine(); + for (Action action : redoStack) { + writer.write(action.toString()); + writer.newLine(); + } + } catch (IOException e) { + System.err.println("Fehler beim Speichern der Zustände: " + e.getMessage()); + } + } + + // Lädt den Zustand aus der Datei + public static void loadState(Stack undoStack, Stack redoStack) { + ensureFileExists(); // Stellt sicher, dass die Datei existiert + try (BufferedReader reader = new BufferedReader(new FileReader(FILE_NAME))) { + String line; + Stack currentStack = undoStack; // Standard: Undo-Stack + while ((line = reader.readLine()) != null) { + if (line.equals("UNDO")) { + currentStack = undoStack; + } else if (line.equals("REDO")) { + currentStack = redoStack; + } else { + String[] parts = line.split(","); + int row = Integer.parseInt(parts[0]); + int col = Integer.parseInt(parts[1]); + String oldState = parts[2]; + String newState = parts[3]; + currentStack.push(new Action(row, col, oldState, newState)); + } + } + } catch (FileNotFoundException e) { + System.out.println("Keine Undo/Redo-Datei gefunden. Es werden leere Stacks verwendet."); + } catch (IOException e) { + System.err.println("Fehler beim Laden der Zustände: " + e.getMessage()); + } + } +} diff --git a/Hitori/src/main/java/PR2/HitoriSpiel/Domain/StateManager.java b/Hitori/src/main/java/PR2/HitoriSpiel/Domain/StateManager.java index ab4fe1c..0f49a1f 100644 --- a/Hitori/src/main/java/PR2/HitoriSpiel/Domain/StateManager.java +++ b/Hitori/src/main/java/PR2/HitoriSpiel/Domain/StateManager.java @@ -4,74 +4,37 @@ import java.io.*; import java.util.Stack; public class StateManager implements Serializable { - private final Stack undoStack = new Stack<>(); - private final Stack redoStack = new Stack<>(); + private final Stack undoStack = new Stack<>(); + private final Stack redoStack = new Stack<>(); - public void saveState(int[][] state) { - undoStack.push(copyState(state)); + public void saveAction(int row, int col, String oldState, String newState) { + undoStack.push(new Action(row, col, oldState, newState)); redoStack.clear(); // Redo-Stack leeren, da ein neuer Zustand hinzugefügt wurde + StateFileManager.saveState(undoStack, redoStack); // Speichern in Datei } - // Macht die letzte Änderung rückgängig - public int[][] undo(int[][] currentState) { + public Action undo() { if (!undoStack.isEmpty()) { - redoStack.push(copyState(currentState)); // Speichere aktuellen Zustand für Redo - return undoStack.pop(); // Gib den vorherigen Zustand zurück + Action action = undoStack.pop(); + redoStack.push(action); + StateFileManager.saveState(undoStack, redoStack); // Aktuellen Zustand speichern + return action; } - return null; // Kein Zustand mehr verfügbar + return null; } - // Wiederholt die letzte rückgängig gemachte Änderung - public int[][] redo(int[][] currentState) { + public Action redo() { if (!redoStack.isEmpty()) { - undoStack.push(copyState(currentState)); // Speichere aktuellen Zustand für Undo - return redoStack.pop(); // Gib den Redo-Zustand zurück + Action action = redoStack.pop(); + undoStack.push(action); + StateFileManager.saveState(undoStack, redoStack); // Aktuellen Zustand speichern + return action; } - return null; // Kein Zustand verfügbar + return null; } - // Hilfsmethode, um eine Kopie des Zustands zu erstellen - private int[][] copyState(int[][] state) { - int[][] copy = new int[state.length][state[0].length]; - for (int i = 0; i < state.length; i++) { - System.arraycopy(state[i], 0, copy[i], 0, state[i].length); - } - return copy; - } - - // Speichert die gesamte StateManager-Instanz in einer Datei - public void saveStateToFile() { - try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("undoRedoState.dat"))) { - out.writeObject(undoStack); // Schreibe den Undo-Stack - out.writeObject(redoStack); // Schreibe den Redo-Stack - System.out.println("Zustände wurden erfolgreich gespeichert."); - } catch (IOException e) { - System.err.println("Fehler beim Speichern der Zustände: " + e.getMessage()); - } - } - - // Lädt den StateManager aus einer Datei - public static StateManager loadStateFromFile() { - try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("undoRedoState.dat"))) { - StateManager stateManager = new StateManager(); - stateManager.undoStack.addAll((Stack) in.readObject()); - stateManager.redoStack.addAll((Stack) in.readObject()); - System.out.println("Zustände wurden erfolgreich geladen."); - return stateManager; - } catch (IOException | ClassNotFoundException e) { - System.err.println("Fehler beim Laden der Zustände: " + e.getMessage()); - return new StateManager(); // Gibt eine neue Instanz zurück, falls ein Fehler auftritt - } - } - - // Überprüft, ob Undo-Operationen verfügbar sind - public boolean canUndo() { - return !undoStack.isEmpty(); - } - - // Überprüft, ob Redo-Operationen verfügbar sind - public boolean canRedo() { - return !redoStack.isEmpty(); - + public void loadStateFromFile() { + StateFileManager.loadState(undoStack, redoStack); // Stacks aus Datei laden } } + diff --git a/Hitori/src/main/java/PR2/HitoriSpiel/GUI/GameBoard.java b/Hitori/src/main/java/PR2/HitoriSpiel/GUI/GameBoard.java index 7e11e2d..94061bd 100644 --- a/Hitori/src/main/java/PR2/HitoriSpiel/GUI/GameBoard.java +++ b/Hitori/src/main/java/PR2/HitoriSpiel/GUI/GameBoard.java @@ -1,5 +1,6 @@ package PR2.HitoriSpiel.GUI; +import PR2.HitoriSpiel.Domain.Action; import PR2.HitoriSpiel.Domain.HitoriValidator; import PR2.HitoriSpiel.Domain.StateManager; import PR2.HitoriSpiel.Domain.HitoriBoard; @@ -75,7 +76,7 @@ public class GameBoard extends JPanel { private JButton createResetButton() { JButton resetButton = Setup.createGameBoardButton("Zurücksetzen", 150, 30); resetButton.addActionListener(e -> { - saveStateForUndo(); + //saveStateForUndo(); resetBoard(); }); return resetButton; @@ -84,24 +85,29 @@ public class GameBoard extends JPanel { private JButton createUndoButton() { JButton undoButton = Setup.createGameBoardButton("Undo", 80, 30); undoButton.addActionListener(e -> { - int[][] previousState = stateManager.undo(board.getNumbers()); - if (previousState != null) { - board.setNumbers(previousState); - updateBoard(); + Action action = stateManager.undo(); + if (action != null) { + HitoriCell cell = board.getCell(action.getRow(), action.getCol()); + cell.setState(HitoriCell.CellState.valueOf(action.getOldState())); + JButton button = (JButton) gamePanel.getComponent(action.getRow() * board.getSize() + action.getCol()); + updateButtonState(button, cell); } else { JOptionPane.showMessageDialog(this, "Kein Zustand zum Zurücksetzen vorhanden.", "Undo", JOptionPane.INFORMATION_MESSAGE); } }); + return undoButton; } private JButton createRedoButton() { JButton redoButton = Setup.createGameBoardButton("Redo", 80, 30); redoButton.addActionListener(e -> { - int[][] nextState = stateManager.redo(board.getNumbers()); - if (nextState != null) { - board.setNumbers(nextState); - updateBoard(); + Action action = stateManager.redo(); + if (action != null) { + HitoriCell cell = board.getCell(action.getRow(), action.getCol()); + cell.setState(HitoriCell.CellState.valueOf(action.getNewState())); + JButton button = (JButton) gamePanel.getComponent(action.getRow() * board.getSize() + action.getCol()); + updateButtonState(button, cell); } else { JOptionPane.showMessageDialog(this, "Kein Zustand zum Wiederholen vorhanden.", "Redo", JOptionPane.INFORMATION_MESSAGE); } @@ -164,45 +170,62 @@ public class GameBoard extends JPanel { JButton button = new JButton(String.valueOf(cell.getNumber())); button.setBackground(Color.LIGHT_GRAY); button.addActionListener(e -> { - toggleCellState(cell); + toggleCellState(cell, row, col); updateButtonState(button, cell); }); return button; } - private void toggleCellState(HitoriCell cell) { + private void toggleCellState(HitoriCell cell, int row, int col) { if (cell == null) { System.err.println("Ungültige Zelle! Der Zustand kann nicht geändert werden."); return; } + String oldState = cell.getState().toString(); + String newState; + switch (cell.getState()) { - case GRAY -> cell.setState(HitoriCell.CellState.BLACK); - case BLACK -> cell.setState(HitoriCell.CellState.WHITE); - case WHITE -> cell.setState(HitoriCell.CellState.GRAY); + case GRAY -> { + cell.setState(HitoriCell.CellState.BLACK); + newState = HitoriCell.CellState.BLACK.toString(); + } + case BLACK -> { + cell.setState(HitoriCell.CellState.WHITE); + newState = HitoriCell.CellState.WHITE.toString(); + } + case WHITE -> { + cell.setState(HitoriCell.CellState.GRAY); + newState = HitoriCell.CellState.GRAY.toString(); + } + default -> throw new IllegalStateException("Unerwarteter Zustand: " + cell.getState()); } + + stateManager.saveAction(row, col, oldState, newState); + + // Debug-Ausgabe + System.out.println("Aktion gespeichert: " + oldState + " -> " + newState); } private void updateButtonState(JButton button, HitoriCell cell) { switch (cell.getState()) { case GRAY -> { - button.setBackground(Color.LIGHT_GRAY); - button.setText(String.valueOf(cell.getNumber())); + button.setBackground(Color.LIGHT_GRAY); // Hintergrund grau + button.setForeground(Color.BLACK); // Textfarbe schwarz + button.setText(String.valueOf(cell.getNumber())); // Zeigt die Zahl an } case BLACK -> { - button.setBackground(Color.BLACK); - button.setForeground(Color.WHITE); + button.setBackground(Color.BLACK); // Hintergrund schwarz + button.setForeground(Color.WHITE); // Textfarbe weiß + button.setText(String.valueOf(cell.getNumber())); // Zeigt die Zahl an } case WHITE -> { - button.setBackground(Color.WHITE); - button.setForeground(Color.BLACK); + button.setBackground(Color.WHITE); // Hintergrund weiß + button.setForeground(Color.BLACK); // Textfarbe schwarz + button.setText(String.valueOf(cell.getNumber())); // Zeigt die Zahl an } } } - private void saveStateForUndo() { - stateManager.saveState(board.getNumbers()); - } - private void addHighscore(int elapsedTime, String boardName) { String playerName = JOptionPane.showInputDialog(this, "Neuer Highscore! Bitte Namen eingeben:", @@ -306,10 +329,10 @@ public class GameBoard extends JPanel { for (int i = 0; i < board.getSize(); i++) { for (int j = 0; j < board.getSize(); j++) { HitoriCell cell = board.getCell(i, j); - JButton button = (JButton) gamePanel.getComponent(i * board.getSize() + j); // Hole den Button - updateButtonState(button, cell); // Aktualisiere den Button basierend auf dem Zustand + JButton button = (JButton) gamePanel.getComponent(i * board.getSize() + j); // Holt den Button + updateButtonState(button, cell); // Aktualisiert den Button basierend auf dem Zustand } } - } + } } diff --git a/Hitori/src/main/java/PR2/HitoriSpiel/GUI/HighscoreDialog.java b/Hitori/src/main/java/PR2/HitoriSpiel/GUI/HighscoreDialog.java index d329401..2f29cd5 100644 --- a/Hitori/src/main/java/PR2/HitoriSpiel/GUI/HighscoreDialog.java +++ b/Hitori/src/main/java/PR2/HitoriSpiel/GUI/HighscoreDialog.java @@ -3,13 +3,11 @@ package PR2.HitoriSpiel.GUI; import PR2.HitoriSpiel.Utils.HighscoreManager; import PR2.HitoriSpiel.Utils.Setup; -import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import javax.swing.*; import java.awt.*; import java.util.Comparator; import java.util.List; -import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; // aktueller Stand @@ -20,11 +18,11 @@ public class HighscoreDialog extends JDialog { public HighscoreDialog(JFrame parentFrame) { super(parentFrame, "Highscoreliste", true); - this.highscoreManager = new HighscoreManager(); - - setLayout(new BorderLayout()); - setSize(700, 400); + setSize(600, 400); setLocationRelativeTo(parentFrame); + + this.highscoreManager = new HighscoreManager(); + setLayout(new BorderLayout()); Setup.stylePanel((JPanel) getContentPane()); // Hintergrundfarbe setzen JLabel titleLabel = new JLabel("Highscores", SwingConstants.CENTER); @@ -32,84 +30,46 @@ public class HighscoreDialog extends JDialog { Setup.styleLabel(titleLabel); // Schriftstil setzen add(titleLabel, BorderLayout.NORTH); - String[] columnNames = {"Platz", "Name", "Zeit (Sek.)", "Spielfeld", "Durchschnittszeit des Spielfelds"}; + String[] columnNames = {"Platz", "Name", "Zeit (Sek.)", "Spielfeld"}; tableModel = new DefaultTableModel(columnNames, 0); JTable highscoreTable = new JTable(tableModel); highscoreTable.setFillsViewportHeight(true); highscoreTable.setEnabled(false); // Tabelle nur zur Anzeige - - // Renderer, um Text in der Mitte der Zellen zu platzieren - DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer(); - centerRenderer.setHorizontalAlignment(SwingConstants.CENTER); - - for (int i = 0; i < highscoreTable.getColumnCount(); i++) { - highscoreTable.getColumnModel().getColumn(i).setCellRenderer(centerRenderer); - } - - // Automatische Spaltenbreitenanpassung und individuelle Breiten setzen - highscoreTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); - highscoreTable.getColumnModel().getColumn(0).setPreferredWidth(50); // Platz - highscoreTable.getColumnModel().getColumn(1).setPreferredWidth(150); // Name - highscoreTable.getColumnModel().getColumn(2).setPreferredWidth(100); // Punkte - highscoreTable.getColumnModel().getColumn(3).setPreferredWidth(150); // Spielfeld - highscoreTable.getColumnModel().getColumn(4).setPreferredWidth(230); // Durchschnittszeit - JScrollPane scrollPane = new JScrollPane(highscoreTable); add(scrollPane, BorderLayout.CENTER); JPanel buttonPanel = new JPanel(); - // Schließen-Button JButton closeButton = Setup.createButton("Schließen", 200, 40); closeButton.setFont(new Font(closeButton.getFont().getName(), closeButton.getFont().getStyle(), 18)); // Schriftgröße ändern closeButton.addActionListener(e -> dispose()); buttonPanel.add(closeButton); - // Button zum Löschen aller Einträge - JButton clearButton = Setup.createButton("Einträge löschen", 200, 40); - clearButton.setFont(new Font(closeButton.getFont().getName(), closeButton.getFont().getStyle(), 18)); - clearButton.addActionListener(e -> { - int confirmation = JOptionPane.showConfirmDialog( - this, - "Möchten Sie wirklich alle Highscores löschen?", - "Highscores löschen", - JOptionPane.YES_NO_OPTION - ); - - if (confirmation == JOptionPane.YES_OPTION) { - highscoreManager.clearHighscores(); - loadHighscoresWithAverages(); // Tabelle aktualisieren - JOptionPane.showMessageDialog(this, "Alle Highscores wurden gelöscht."); - } - }); - buttonPanel.add(clearButton); - Setup.stylePanel(buttonPanel); // Hintergrundstil setzen add(buttonPanel, BorderLayout.SOUTH); // Highscores laden - loadHighscoresWithAverages(); + loadHighscores(); } - private void loadHighscoresWithAverages() { + private void loadHighscores() { tableModel.setRowCount(0); // Tabelle zurücksetzen - List highscores = highscoreManager.getSortedHighscores(); - Map averageTimes = highscoreManager.getAverageTimesByBoard(); + List highscores = highscoreManager.getHighscores(); - int rank = 1; + highscores.sort(Comparator.comparingInt(HighscoreManager.Highscore::getScore)); // Sortierung: Kürzeste Zeit zuerst + + int rank = 1; // Platznummer for (HighscoreManager.Highscore highscore : highscores) { - double averageTime = averageTimes.getOrDefault(highscore.getBoardName(), 0.0); tableModel.addRow(new Object[]{ - rank++, // Platzierung - highscore.getPlayerName(), // Name - highscore.getScore(), // Punkte - highscore.getBoardName(), // Spielfeld - String.format("%.2f", averageTime) // Durchschnittszeit + rank++, // Platznummer hochzählen + highscore.getPlayerName(), // Name des Spielers + highscore.getScore(), // Zeit in Sekunden + highscore.getBoardName() // Name des Spielfelds }); } } public void refreshHighscores() { - loadHighscoresWithAverages(); + loadHighscores(); } } diff --git a/Hitori/src/main/java/PR2/HitoriSpiel/Main/Main.java b/Hitori/src/main/java/PR2/HitoriSpiel/Main/Main.java index b2a93d5..916621b 100644 --- a/Hitori/src/main/java/PR2/HitoriSpiel/Main/Main.java +++ b/Hitori/src/main/java/PR2/HitoriSpiel/Main/Main.java @@ -19,7 +19,7 @@ public class Main { // Füge einen Shutdown-Hook hinzu, um Zustände bei Beendigung zu speichern 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 System.out.println("Spielzustände wurden gespeichert."); })); diff --git a/Hitori/src/main/java/PR2/HitoriSpiel/Utils/HighscoreManager.java b/Hitori/src/main/java/PR2/HitoriSpiel/Utils/HighscoreManager.java index 129b39b..5f8ea85 100644 --- a/Hitori/src/main/java/PR2/HitoriSpiel/Utils/HighscoreManager.java +++ b/Hitori/src/main/java/PR2/HitoriSpiel/Utils/HighscoreManager.java @@ -1,12 +1,15 @@ package PR2.HitoriSpiel.Utils; import java.io.*; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; import java.util.concurrent.locks.ReentrantLock; public class HighscoreManager { - private static final String HIGHSCORE_FILE = "Files/highscores.txt"; + private static final String HIGHSCORE_FILE = "highscores.txt"; private static final ReentrantLock fileLock = new ReentrantLock(); // Highscore-Datenstruktur private final List highscoreList = new ArrayList<>(); @@ -44,7 +47,7 @@ public class HighscoreManager { // Neue Methode: Überprüfung, ob ein neuer Highscore erreicht wurde public boolean isNewHighscore(int elapsedTime, String boardName) { - List highscores = getSortedHighscores(); + List highscores = getHighscores(); // Prüfen, ob das Board bereits einen Highscore-Eintrag hat for (Highscore highscore : highscores) { @@ -60,25 +63,16 @@ public class HighscoreManager { // Highscores laden private void loadHighscores() { fileLock.lock(); - try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(HIGHSCORE_FILE)) { - if (inputStream == null) { - System.out.println("Datei highscores.txt nicht im Classpath gefunden."); - return; - } - - try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { - highscoreList.clear(); // Liste zurücksetzen - String line; - while ((line = reader.readLine()) != null) { - String[] parts = line.split(","); - if (parts.length == 3) { // Name, Punkte, Spielfeld - String playerName = parts[0].trim(); - int score = Integer.parseInt(parts[1].trim()); - String boardName = parts[2].trim(); - highscoreList.add(new Highscore(playerName, score, boardName)); - } + try (BufferedReader reader = new BufferedReader(new FileReader(HIGHSCORE_FILE))) { + String line; + while ((line = reader.readLine()) != null) { + String[] parts = line.split(","); + if (parts.length == 3) { // Name, Punkte, Spielfeld + highscoreList.add(new Highscore(parts[0], Integer.parseInt(parts[1]), parts[2])); } } + } catch (FileNotFoundException e) { + System.out.println("Highscore-Datei nicht gefunden. Sie wird nach dem ersten Speichern erstellt."); } catch (IOException | NumberFormatException e) { System.err.println("Fehler beim Laden der Highscores: " + e.getMessage()); } finally { @@ -102,40 +96,11 @@ public class HighscoreManager { } } - // Highscores abrufen (sortiert nach kürzester Zeit) - public List getSortedHighscores() { - fileLock.lock(); - try { - highscoreList.sort(Comparator.comparingInt(Highscore::getScore)); // Kürzeste Zeit zuerst - return new ArrayList<>(highscoreList); // Kopie der Liste zurückgeben - } finally { - fileLock.unlock(); - } + // Highscores abrufen + public List getHighscores() { + return new ArrayList<>(highscoreList); // Modifizierbare Kopie zurückgeben } - - // Durchschnittszeit für jedes Spielfeld berechnen - public Map getAverageTimesByBoard() { - fileLock.lock(); - try { - Map> boardScores = new HashMap<>(); - - for (Highscore highscore : highscoreList) { - boardScores.computeIfAbsent(highscore.getBoardName(), k -> new ArrayList<>()).add(highscore.getScore()); - } - - Map averageTimes = new HashMap<>(); - for (Map.Entry> entry : boardScores.entrySet()) { - List scores = entry.getValue(); - double average = scores.stream().mapToInt(Integer::intValue).average().orElse(0.0); - averageTimes.put(entry.getKey(), average); - } - - return averageTimes; - } finally { - fileLock.unlock(); - } - } - + // Löscht alle Highscores public void clearHighscores() { fileLock.lock(); diff --git a/Hitori/src/main/resources/Files/undoRedoLog.txt b/Hitori/src/main/resources/Files/undoRedoLog.txt new file mode 100644 index 0000000..e69de29