diff --git a/Hitori/src/main/java/PR2/HitoriSpiel/GUI/BoardLoader.java b/Hitori/src/main/java/PR2/HitoriSpiel/GUI/BoardLoader.java index 4411bfe..508d1e3 100644 --- a/Hitori/src/main/java/PR2/HitoriSpiel/GUI/BoardLoader.java +++ b/Hitori/src/main/java/PR2/HitoriSpiel/GUI/BoardLoader.java @@ -28,7 +28,7 @@ public class BoardLoader { } } - public static List loadBoardsAsList() { + /* public static List loadBoardsAsList() { List boardFiles = new ArrayList<>(); try { var resource = BoardLoader.class.getClassLoader().getResource("persistent/Hitori_Spielfelder/"); @@ -61,11 +61,55 @@ public class BoardLoader { System.err.println("Fehler beim Laden der Spielfelder: " + e.getMessage()); } return boardFiles; - } + }*/ + public static List loadBoardsAsList() { + List boardFiles = new ArrayList<>(); + try { + // Zugriff auf die Ressource + var resource = BoardLoader.class.getClassLoader().getResource("persistent/Hitori_Spielfelder/"); + if (resource == null) { + throw new IOException("Das Verzeichnis 'persistent/Hitori_Spielfelder/' wurde nicht gefunden."); + } + + // Unterscheide zwischen Dateisystem und JAR-Umgebung + if ("file".equals(resource.getProtocol())) { + // Entwicklungsmodus: Zugriff auf das Dateisystem + File directory = new File(resource.toURI()); + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isFile() && file.getName().endsWith(".csv")) { + boardFiles.add(file.getName()); + } + } + } else { + System.err.println("Das Verzeichnis ist leer: " + directory.getAbsolutePath()); + } + } else if ("jar".equals(resource.getProtocol())) { + // Produktionsmodus: Zugriff im JAR + String path = resource.getPath().substring(5, resource.getPath().indexOf("!")); // JAR-Pfad extrahieren + try (JarFile jar = new JarFile(URLDecoder.decode(path, "UTF-8"))) { + Enumeration entries = jar.entries(); + while (entries.hasMoreElements()) { + String name = entries.nextElement().getName(); + if (name.startsWith("persistent/Hitori_Spielfelder/") && name.endsWith(".csv")) { + boardFiles.add(name.substring(name.lastIndexOf("/") + 1)); + } + } + } + } else { + throw new IOException("Unbekanntes Protokoll: " + resource.getProtocol()); + } + } catch (Exception e) { + System.err.println("Fehler beim Laden der Spielfelder: " + e.getMessage()); + } + return boardFiles; + } - public static int[][] loadBoard(String resourcePath) throws IOException { + + public static int[][] loadBoard(String resourcePath) throws IOException { List rows = new ArrayList<>(); try (InputStream inputStream = BoardLoader.class.getResourceAsStream(resourcePath); diff --git a/Hitori/src/main/java/PR2/HitoriSpiel/GUI/GameBoard.java b/Hitori/src/main/java/PR2/HitoriSpiel/GUI/GameBoard.java index 94061bd..83475f7 100644 --- a/Hitori/src/main/java/PR2/HitoriSpiel/GUI/GameBoard.java +++ b/Hitori/src/main/java/PR2/HitoriSpiel/GUI/GameBoard.java @@ -143,17 +143,39 @@ public class GameBoard extends JPanel { } private void handleHighscore() { - int elapsedTime = (int) ((System.currentTimeMillis() - startTime) / 1000); + int elapsedTime = (int) ((System.currentTimeMillis() - startTime) / 1000); // Spielzeit in Sekunden String boardName = board.getBoardName(); + int errors = 0; // Aktuelle Fehlerzahl (falls vorhanden, muss noch angepasst werden) HighscoreManager manager = new HighscoreManager(); boolean isNewHighscore = manager.isNewHighscore(elapsedTime, boardName); - if (isNewHighscore) { - addHighscore(elapsedTime, boardName); - } else { - JOptionPane.showMessageDialog(this, "Das Spielfeld ist korrekt gelöst!", "Erfolg", JOptionPane.INFORMATION_MESSAGE); + // Zeige ein Dialogfenster zur Namenseingabe + String playerName = JOptionPane.showInputDialog(this, + isNewHighscore + ? "Neuer Highscore! Bitte geben Sie Ihren Namen ein:" + : "Das Spielfeld ist korrekt gelöst! Bitte geben Sie Ihren Namen ein:", + "Highscore speichern", + JOptionPane.PLAIN_MESSAGE); + + if (playerName == null || playerName.trim().isEmpty()) { + JOptionPane.showMessageDialog(this, "Name wurde nicht eingegeben. Kein Highscore gespeichert.", "Info", JOptionPane.INFORMATION_MESSAGE); + return; } + + // Speichere den Highscore + manager.addHighscore(playerName, elapsedTime, boardName, errors); + + JOptionPane.showMessageDialog(this, + isNewHighscore + ? "Glückwunsch! Dein Highscore wurde gespeichert!" + : "Dein Eintrag wurde gespeichert!", + "Highscore", + JOptionPane.INFORMATION_MESSAGE); + + // Kehre ins Hauptmenü zurück + returnToMainMenu(); + } private void showPauseMenu() { @@ -226,7 +248,7 @@ public class GameBoard extends JPanel { } } - private void addHighscore(int elapsedTime, String boardName) { + private void addHighscore(int elapsedTime, String boardName, int errors) { String playerName = JOptionPane.showInputDialog(this, "Neuer Highscore! Bitte Namen eingeben:", "Highscore speichern", @@ -235,7 +257,7 @@ public class GameBoard extends JPanel { if (playerName == null || playerName.trim().isEmpty()) return; HighscoreManager manager = new HighscoreManager(); - manager.addHighscore(playerName, elapsedTime, boardName); + manager.addHighscore(playerName, elapsedTime, boardName, errors); JOptionPane.showMessageDialog(this, "Highscore erfolgreich gespeichert!", @@ -246,7 +268,6 @@ public class GameBoard extends JPanel { private void returnToMainMenu() { /// Eltern-Frame abrufen JFrame parentFrame = (JFrame) SwingUtilities.getWindowAncestor(this); - if (parentFrame != null) { // Hauptmenü erstellen und im Frame setzen StartMenu startMenu = new StartMenu(parentFrame); diff --git a/Hitori/src/main/java/PR2/HitoriSpiel/GUI/HighscoreDialog.java b/Hitori/src/main/java/PR2/HitoriSpiel/GUI/HighscoreDialog.java index 2f29cd5..46ec8ac 100644 --- a/Hitori/src/main/java/PR2/HitoriSpiel/GUI/HighscoreDialog.java +++ b/Hitori/src/main/java/PR2/HitoriSpiel/GUI/HighscoreDialog.java @@ -2,27 +2,29 @@ package PR2.HitoriSpiel.GUI; import PR2.HitoriSpiel.Utils.HighscoreManager; import PR2.HitoriSpiel.Utils.Setup; - import javax.swing.table.DefaultTableModel; + import javax.swing.*; import java.awt.*; import java.util.Comparator; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; // aktueller Stand public class HighscoreDialog extends JDialog { private final HighscoreManager highscoreManager; private final DefaultTableModel tableModel; + private final JComboBox boardSelector; - public HighscoreDialog(JFrame parentFrame) { + public HighscoreDialog(JFrame parentFrame, List boardNames) { super(parentFrame, "Highscoreliste", true); - setSize(600, 400); - setLocationRelativeTo(parentFrame); this.highscoreManager = new HighscoreManager(); + this.tableModel = new DefaultTableModel(new String[]{"Platz", "Name", "Zeit (Sek.)", "Fehler", "Spielfeld"}, 0); + setLayout(new BorderLayout()); + setSize(600, 400); + setLocationRelativeTo(parentFrame); Setup.stylePanel((JPanel) getContentPane()); // Hintergrundfarbe setzen JLabel titleLabel = new JLabel("Highscores", SwingConstants.CENTER); @@ -30,12 +32,15 @@ public class HighscoreDialog extends JDialog { Setup.styleLabel(titleLabel); // Schriftstil setzen add(titleLabel, BorderLayout.NORTH); - 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 - JScrollPane scrollPane = new JScrollPane(highscoreTable); + + // Spielfeld-Auswahl (ComboBox) + boardSelector = new JComboBox<>(boardNames.toArray(new String[0])); + boardSelector.addActionListener(e -> loadHighscoresForBoard((String) boardSelector.getSelectedItem())); + add(boardSelector, BorderLayout.NORTH); + + // Tabelle für Highscores + JTable table = new JTable(tableModel); + JScrollPane scrollPane = new JScrollPane(table); add(scrollPane, BorderLayout.CENTER); JPanel buttonPanel = new JPanel(); @@ -48,28 +53,31 @@ public class HighscoreDialog extends JDialog { Setup.stylePanel(buttonPanel); // Hintergrundstil setzen add(buttonPanel, BorderLayout.SOUTH); - // Highscores laden - loadHighscores(); + loadHighscoresForBoard(boardNames.get(0)); } - private void loadHighscores() { + private void loadHighscoresForBoard(String boardName) { tableModel.setRowCount(0); // Tabelle zurücksetzen - List highscores = highscoreManager.getHighscores(); + List highscores = highscoreManager.getHighscoresForBoard(boardName); - highscores.sort(Comparator.comparingInt(HighscoreManager.Highscore::getScore)); // Sortierung: Kürzeste Zeit zuerst + if (highscores.isEmpty()) { + JOptionPane.showMessageDialog(this, + "Keine Highscores für das ausgewählte Spielfeld vorhanden.", + "Info", + JOptionPane.INFORMATION_MESSAGE); + return; + } - int rank = 1; // Platznummer + int rank = 1; for (HighscoreManager.Highscore highscore : highscores) { tableModel.addRow(new Object[]{ - rank++, // Platznummer hochzählen - highscore.getPlayerName(), // Name des Spielers - highscore.getScore(), // Zeit in Sekunden - highscore.getBoardName() // Name des Spielfelds + rank++, // Platznummer + highscore.getPlayerName(), // Spielername + highscore.getTime(), // Zeit in Sekunden + highscore.getErrors(), // Anzahl der Fehler + highscore.getBoardName() // Spielfeldname }); } } - public void refreshHighscores() { - loadHighscores(); - } } diff --git a/Hitori/src/main/java/PR2/HitoriSpiel/GUI/PauseMenu.java b/Hitori/src/main/java/PR2/HitoriSpiel/GUI/PauseMenu.java index e239b26..7c71ee4 100644 --- a/Hitori/src/main/java/PR2/HitoriSpiel/GUI/PauseMenu.java +++ b/Hitori/src/main/java/PR2/HitoriSpiel/GUI/PauseMenu.java @@ -1,5 +1,7 @@ package PR2.HitoriSpiel.GUI; +import PR2.HitoriSpiel.Utils.Setup; + import javax.swing.*; import java.awt.*; import java.awt.event.ActionListener; @@ -13,7 +15,7 @@ public class PauseMenu extends JDialog { setLocationRelativeTo(parent); // "Spiel fortsetzen"-Button - JButton resumeButton = new JButton("Spiel fortsetzen"); + JButton resumeButton = Setup.createButton("Spiel fortsetzen", 200, 40); resumeButton.addActionListener(e -> { dispose(); if (resumeAction != null) { @@ -22,7 +24,7 @@ public class PauseMenu extends JDialog { }); // "Zum Hauptmenü"-Button - JButton mainMenuButton = new JButton("Zum Hauptmenü"); + JButton mainMenuButton = Setup.createButton("Zum Hauptmenü", 200, 40); mainMenuButton.addActionListener(e -> { dispose(); if (mainMenuAction != null) { @@ -31,7 +33,7 @@ public class PauseMenu extends JDialog { }); // "Spiel beenden"-Button - JButton exitButton = new JButton("Spiel beenden"); + JButton exitButton = Setup.createButton("Spiel beenden", 200, 40); exitButton.addActionListener(e -> { dispose(); if (exitAction != null) { diff --git a/Hitori/src/main/java/PR2/HitoriSpiel/GUI/StartMenu.java b/Hitori/src/main/java/PR2/HitoriSpiel/GUI/StartMenu.java index 1f13738..a3e1236 100644 --- a/Hitori/src/main/java/PR2/HitoriSpiel/GUI/StartMenu.java +++ b/Hitori/src/main/java/PR2/HitoriSpiel/GUI/StartMenu.java @@ -108,7 +108,13 @@ public class StartMenu extends JPanel { private void highscorelist() { - new HighscoreDialog((JFrame) SwingUtilities.getWindowAncestor(this)).setVisible(true); + List boardNames = BoardLoader.loadBoardsAsList(); // Lade die Spielfeldnamen + if (boardNames == null || boardNames.isEmpty()) { + JOptionPane.showMessageDialog(this, "Keine Highscores verfügbar, da keine Spielfelder gefunden wurden.", "Info", JOptionPane.INFORMATION_MESSAGE); + return; + } + + new HighscoreDialog((JFrame) SwingUtilities.getWindowAncestor(this), boardNames).setVisible(true); } diff --git a/Hitori/src/main/java/PR2/HitoriSpiel/Utils/HighscoreManager.java b/Hitori/src/main/java/PR2/HitoriSpiel/Utils/HighscoreManager.java index 5f8ea85..251c323 100644 --- a/Hitori/src/main/java/PR2/HitoriSpiel/Utils/HighscoreManager.java +++ b/Hitori/src/main/java/PR2/HitoriSpiel/Utils/HighscoreManager.java @@ -2,7 +2,6 @@ package PR2.HitoriSpiel.Utils; import java.io.*; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.concurrent.locks.ReentrantLock; @@ -18,81 +17,82 @@ public class HighscoreManager { loadHighscores(); } - // Highscore hinzufügen - public synchronized void addHighscore(String playerName, int time, String boardName) { - fileLock.lock(); - try { - // Prüfe, ob es bereits Highscores für das Spielfeld gibt - boolean isShorterTime = highscoreList.stream() - .filter(highscore -> highscore.getBoardName().equals(boardName)) - .allMatch(highscore -> time < highscore.getScore()); - - if (isShorterTime) { - // Entferne alle Highscores für das Spielfeld, die länger oder gleich sind - highscoreList.removeIf(highscore -> highscore.getBoardName().equals(boardName)); - - // Füge den neuen Highscore hinzu - highscoreList.add(new Highscore(playerName, time, boardName)); - - // Sortiere die Liste - highscoreList.sort(Comparator.comparingInt(Highscore::getScore)); // Kürzeste Zeit zuerst - saveHighscores(); - } else { - System.out.println("Neuer Highscore ist nicht kürzer als die bestehenden Einträge."); - } - } finally { - fileLock.unlock(); - } - } - - // Neue Methode: Überprüfung, ob ein neuer Highscore erreicht wurde + // Prüft, ob die gegebene Zeit ein neuer Highscore ist public boolean isNewHighscore(int elapsedTime, String boardName) { - List highscores = getHighscores(); - - // Prüfen, ob das Board bereits einen Highscore-Eintrag hat - for (Highscore highscore : highscores) { - if (highscore.getBoardName().equals(boardName)) { - return elapsedTime < highscore.getScore(); // Neuer Highscore, wenn Zeit besser ist - } + // Filtere die Highscores für das gegebene Spielfeld + List highscoresForBoard = getHighscoresForBoard(boardName); + // Falls es keinen Highscore für das Spielfeld gibt, ist es automatisch ein neuer Highscore + if (highscoresForBoard.isEmpty()) { + return true; } - // Wenn das Board keinen Highscore hat, ist dies der erste Eintrag - return true; + // Finde die kürzeste Zeit im Highscore + int bestTime = highscoresForBoard.stream() + .mapToInt(Highscore::getTime) + .min() + .orElse(Integer.MAX_VALUE); + + // Überprüfe, ob die aktuelle Zeit besser ist + return elapsedTime < bestTime; } - // Highscores laden - private void loadHighscores() { - fileLock.lock(); - 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 { - fileLock.unlock(); - } + // Highscore hinzufügen + public synchronized void addHighscore(String playerName, int time, String boardName, int errors) { + highscoreList.add(new Highscore(playerName, time, boardName, errors)); + saveHighscores(); } + // Highscores für ein bestimmtes Spielfeld abrufen + public List getHighscoresForBoard(String boardName) { + return highscoreList.stream() + .filter(highscore -> highscore.getBoardName().equals(boardName)) + .sorted(Comparator.comparingInt(Highscore::getTime)) // Sortiere nach kürzester Zeit + .limit(10) // Zeige nur die Top 10 + .toList(); + } // Highscores speichern private void saveHighscores() { - fileLock.lock(); try (BufferedWriter writer = new BufferedWriter(new FileWriter(HIGHSCORE_FILE))) { for (Highscore highscore : highscoreList) { - writer.write(highscore.getPlayerName() + "," + highscore.getScore() + "," + highscore.getBoardName()); + writer.write(highscore.toString()); writer.newLine(); } } catch (IOException e) { System.err.println("Fehler beim Speichern der Highscores: " + e.getMessage()); - } finally { - fileLock.unlock(); + } + } + + // Highscores laden + private void loadHighscores() { + try (BufferedReader reader = new BufferedReader(new FileReader(HIGHSCORE_FILE))) { + String line; + while ((line = reader.readLine()) != null) { + String[] parts = line.split(","); + + // Validierung der Datenstruktur + if (parts.length < 4) { + System.err.println("Fehlerhafter Eintrag in Highscore-Datei: " + line); + String[] fixedParts = new String[4]; + System.arraycopy(parts, 0, fixedParts, 0, parts.length); + fixedParts[3] = "0"; // Standardwert für fehlende Fehleranzahl + parts = fixedParts; + } + + try { + String playerName = parts[0]; + int time = Integer.parseInt(parts[1]); + String boardName = parts[2]; + int errors = Integer.parseInt(parts[3]); + + // Füge den Eintrag der Liste hinzu + highscoreList.add(new Highscore(playerName, time, boardName, errors)); + } catch (NumberFormatException e) { + System.err.println("Ungültiges Zahlenformat in Zeile: " + line); + } + } + } catch (IOException e) { + System.out.println("Highscore-Datei nicht gefunden. Eine neue wird erstellt."); } } @@ -115,27 +115,37 @@ public class HighscoreManager { // Innere Highscore-Klasse public static class Highscore { private final String playerName; - private final int score; + private final int time; private final String boardName; + private final int errors; - public Highscore(String playerName, int score, String boardName) { + public Highscore(String playerName, int score, String boardName, int errors) { this.playerName = playerName; - this.score = score; + this.time = score; this.boardName = boardName; + this.errors = errors; } public String getPlayerName() { return playerName; } - public int getScore() { - return score; + public int getTime() { + return time; } public String getBoardName() { return boardName; } - } + public int getErrors() { + return errors; + } + + @Override + public String toString() { + return playerName + "," + time + "," + boardName + "," + errors; + } + } }