From 981547383fc0ba9679963dd9916ef9cd34a992fe Mon Sep 17 00:00:00 2001 From: dustineversmann Date: Mon, 6 Jan 2025 22:11:30 +0100 Subject: [PATCH 1/5] feature: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implementierung der Datenstruktur, die, welche die Hitorispieldaten und die Lösung enthalten --- .../java/de/deversmann/domain/PuzzleData.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/main/java/de/deversmann/domain/PuzzleData.java diff --git a/src/main/java/de/deversmann/domain/PuzzleData.java b/src/main/java/de/deversmann/domain/PuzzleData.java new file mode 100644 index 0000000..fe0c9e4 --- /dev/null +++ b/src/main/java/de/deversmann/domain/PuzzleData.java @@ -0,0 +1,22 @@ +package de.deversmann.domain; + +import java.util.List; + +public class PuzzleData { + + private final int[][] puzzle; + private final List solutionCoordinates; + + public PuzzleData(int[][] puzzle, List solutionCoordinates) { + this.puzzle = puzzle; + this.solutionCoordinates = solutionCoordinates; + } + + public int[][] getPuzzle() { + return puzzle; + } + + public List getSolutionCoordinates() { + return solutionCoordinates; + } +} \ No newline at end of file From 57147ae81898ef0d0ec2ed7cbd1f52eea7b4ecf5 Mon Sep 17 00:00:00 2001 From: dustineversmann Date: Mon, 6 Jan 2025 22:25:13 +0100 Subject: [PATCH 2/5] feature(domain): Datenstruktur eines Highscores Implementierung der Highscoredatenstruktur zur Nutzung durch den Highscoremanager --- .../de/deversmann/domain/HighscoreEntry.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/main/java/de/deversmann/domain/HighscoreEntry.java diff --git a/src/main/java/de/deversmann/domain/HighscoreEntry.java b/src/main/java/de/deversmann/domain/HighscoreEntry.java new file mode 100644 index 0000000..6649286 --- /dev/null +++ b/src/main/java/de/deversmann/domain/HighscoreEntry.java @@ -0,0 +1,31 @@ +package de.deversmann.domain; + +public class HighscoreEntry { + + private final String playerName; + private final long timeSeconds; + private final int errorCount; + + public HighscoreEntry(String playerName, long timeSeconds, int errorCount) { + this.playerName = playerName; + this.timeSeconds = timeSeconds; + this.errorCount = errorCount; + } + + public String getPlayerName() { + return playerName; + } + + public long getTimeSeconds() { + return timeSeconds; + } + + public int getErrorCount() { + return errorCount; + } + + @Override + public String toString() { + return playerName + " - " + timeSeconds + "s (Fehler: " + errorCount + ")"; + } +} \ No newline at end of file From 1a32962ba8072bd98643a2d3ec82580c5b78b8ac Mon Sep 17 00:00:00 2001 From: dustineversmann Date: Mon, 6 Jan 2025 22:34:08 +0100 Subject: [PATCH 3/5] feature(domain): Verwaltung der Highscores und der Durchschnittszeiten Implementierung des HighscoreManagers, der die erzielten Highscores und die Durschnittszeit ausliest und in der entsprechenden Highscore-Datei speichert. --- .../deversmann/domain/HighscoreManager.java | 94 +++++++++++++++++-- 1 file changed, 88 insertions(+), 6 deletions(-) diff --git a/src/main/java/de/deversmann/domain/HighscoreManager.java b/src/main/java/de/deversmann/domain/HighscoreManager.java index c0146c7..a78d19d 100644 --- a/src/main/java/de/deversmann/domain/HighscoreManager.java +++ b/src/main/java/de/deversmann/domain/HighscoreManager.java @@ -4,17 +4,99 @@ import java.io.*; import java.util.*; public class HighscoreManager { - private List highscores + + private Map> highscoreMap; public HighscoreManager() { - this.highscores = new ArrayList<>(); + this.highscoreMap = new HashMap<>(); } - public void addHighscore(int score) { - highscores.add(score); + public void addHighscore(String puzzleName, String playerName, long timeSeconds, int errorCount) { + highscoreMap.putIfAbsent(puzzleName, new ArrayList<>()); + highscoreMap.get(puzzleName).add(new HighscoreEntry(playerName, timeSeconds, errorCount)); } - public List getHighscores() { - return new ArrayList<>(highscores); + public List getHighscores(String puzzleName) { + return highscoreMap.getOrDefault(puzzleName, Collections.emptyList()); + } + + public double getAverageTime(String puzzleName) { + List entries = highscoreMap.get(puzzleName); + if (entries == null || entries.isEmpty()) { + return -1; + } + long sum = 0; + for (HighscoreEntry e : entries) { + sum += e.getTimeSeconds(); + } + return (double) sum / entries.size(); + } + + public void saveSinglePuzzle(String puzzleName, String filename) throws IOException { + List entries = getHighscores(puzzleName); + + entries.sort(Comparator + .comparingInt(HighscoreEntry::getErrorCount) + .thenComparingLong(HighscoreEntry::getTimeSeconds)); + + double avgTime = getAverageTime(puzzleName); + + try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) { + if (avgTime < 0) { + writer.write("Durchschnittszeit: Keine Einträge"); + } else { + writer.write(String.format("Durchschnittszeit: %.2f s", avgTime)); + } + writer.newLine(); + writer.newLine(); + + writer.write(String.format("%-6s | %-5s | %-6s | %-15s", + "Platz", "Zeit", "Fehler", "Name")); + writer.newLine(); + writer.write("------------------------------------------------------------"); + writer.newLine(); + + int place = 1; + for (HighscoreEntry e : entries) { + writer.write(String.format("%-6s | %-5d | %-6d | %-15s", + place + ".", + e.getTimeSeconds(), + e.getErrorCount(), + e.getPlayerName())); + writer.newLine(); + place++; + } + } + } + + public void loadSinglePuzzle(String puzzleName, String filename) throws IOException { + File file = new File(filename); + if (!file.exists()) { + System.out.println("Highscore-Datei " + filename + " existiert nicht; wird neu erstellt."); + return; + } + + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + // Durchschnittszeit und Header überspringen + reader.readLine(); + reader.readLine(); + reader.readLine(); + reader.readLine(); + + String line; + while ((line = reader.readLine()) != null) { + String[] parts = line.split("\\|"); + if (parts.length < 4) continue; + + String timeStr = parts[1].trim(); + String errorStr = parts[2].trim(); + String nameStr = parts[3].trim(); + + long timeSeconds = Long.parseLong(timeStr); + int errorCount = Integer.parseInt(errorStr); + + addHighscore(puzzleName, nameStr, timeSeconds, errorCount); + } + } } } \ No newline at end of file From 92bc8247e4eedf64906d0f255de66fb006e395cd Mon Sep 17 00:00:00 2001 From: dustineversmann Date: Mon, 6 Jan 2025 22:41:15 +0100 Subject: [PATCH 4/5] feature(domain): CSVReader angepasst --- .../java/de/deversmann/domain/CSVReader.java | 56 ++++++++++++------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/src/main/java/de/deversmann/domain/CSVReader.java b/src/main/java/de/deversmann/domain/CSVReader.java index 42b8c90..7ebfe1a 100644 --- a/src/main/java/de/deversmann/domain/CSVReader.java +++ b/src/main/java/de/deversmann/domain/CSVReader.java @@ -4,36 +4,54 @@ import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; +import java.util.List; public class CSVReader { - /** - * Liest die CSV-Datei ein und gibt die enthaltenen Werte als zweidimensionales int-Array zurück. - * - * @param csvFile Pfad zur CSV-Datei - * @return zweidimensionales Array, das die Zahlen aus der CSV enthält - * @throws IOException wenn ein Fehler beim Lesen auftritt - */ - public int[][] readCsvToIntArray(String csvFile) throws IOException { - ArrayList rows = new ArrayList<>(); + public PuzzleData readPuzzleWithSolution(String csvFile) throws IOException { + List puzzleRows = new ArrayList<>(); + List solutionCoordinates = new ArrayList<>(); try (BufferedReader br = new BufferedReader(new FileReader(csvFile))) { String line; + boolean inSolutionPart = false; + while ((line = br.readLine()) != null) { - String[] tokens = line.split(","); - int[] row = new int[tokens.length]; - for (int i = 0; i < tokens.length; i++) { - row[i] = Integer.parseInt(tokens[i].trim()); + line = line.trim(); + if (line.isEmpty()) { + continue; + } + if (line.startsWith("//Lösung") || line.startsWith("//")) { + inSolutionPart = true; + continue; + } + + if (!inSolutionPart) { + // Puzzle-Teil + String[] tokens = line.split(","); + int[] rowValues = new int[tokens.length]; + for (int i = 0; i < tokens.length; i++) { + rowValues[i] = Integer.parseInt(tokens[i].trim()); + } + puzzleRows.add(rowValues); + } else { + // Lösungsteil + String[] coords = line.split(","); + if (coords.length == 2) { + int row = Integer.parseInt(coords[0].trim()); + int col = Integer.parseInt(coords[1].trim()); + solutionCoordinates.add(new int[]{row, col}); + } } - rows.add(row); } } - // ArrayList in 2D-Array umwandeln - int[][] result = new int[rows.size()][]; - for (int i = 0; i < rows.size(); i++) { - result[i] = rows.get(i); + // puzzleRows -> 2D-Array + int[][] puzzleArray = new int[puzzleRows.size()][]; + for (int i = 0; i < puzzleRows.size(); i++) { + puzzleArray[i] = puzzleRows.get(i); } - return result; + + return new PuzzleData(puzzleArray, solutionCoordinates); } } \ No newline at end of file From cabd4db873cc393912c31e108dbfbc67ba2c98e3 Mon Sep 17 00:00:00 2001 From: dustineversmann Date: Mon, 6 Jan 2025 22:44:59 +0100 Subject: [PATCH 5/5] =?UTF-8?q?feature(facade):=20Facade=20hinzugef=C3=BCg?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Facade als Anlaufpunkt für das Backend implementiert, um Trennung zu gewährleisten --- .../java/de/deversmann/facade/Facade.java | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/src/main/java/de/deversmann/facade/Facade.java b/src/main/java/de/deversmann/facade/Facade.java index d8dcef2..2fc2428 100644 --- a/src/main/java/de/deversmann/facade/Facade.java +++ b/src/main/java/de/deversmann/facade/Facade.java @@ -1,4 +1,101 @@ package de.deversmann.facade; +import de.deversmann.domain.*; + +import java.io.IOException; +import java.util.List; + public class Facade { + + private final CSVReader csvReader; + private final HighscoreManager highscoreManager; + private final Zeiterfassung zeiterfassung; + + private PuzzleData currentPuzzleData; + private String currentPuzzleName; + private int currentErrorCount = 0; + + public Facade() { + this.csvReader = new CSVReader(); + this.highscoreManager = new HighscoreManager(); + this.zeiterfassung = new Zeiterfassung(); + } + + public void ladeSpielfeld(String csvPfad) throws IOException { + this.currentPuzzleData = csvReader.readPuzzleWithSolution(csvPfad); + this.currentPuzzleName = csvPfad; + this.currentErrorCount = 0; + + zeiterfassung.stop(); + zeiterfassung.start(); + System.out.println("Neues Spielfeld geladen, Timer + Fehler=0"); + } + + public long stopZeiterfassung() { + zeiterfassung.stop(); + return zeiterfassung.getElapsedTimeInSeconds(); + } + + public long getElapsedTimeSoFar() { + return zeiterfassung.getElapsedTimeInSeconds(); + } + + public int[][] getAktuellesPuzzle() { + return (currentPuzzleData != null) ? currentPuzzleData.getPuzzle() : null; + } + + public List getLoesungsKoordinaten() { + return (currentPuzzleData != null) ? currentPuzzleData.getSolutionCoordinates() : null; + } + + // Fehler-Tracking + public void incrementErrorCount() { + currentErrorCount++; + } + + public int getCurrentErrorCount() { + return currentErrorCount; + } + + // Highscore + public void addHighscoreForCurrentPuzzle(String playerName, long timeSeconds, int errorCount) { + if (currentPuzzleName != null && !currentPuzzleName.isEmpty()) { + highscoreManager.addHighscore(currentPuzzleName, playerName, timeSeconds, errorCount); + } + } + + public List getHighscoresForCurrentPuzzle() { + if (currentPuzzleName == null) { + return List.of(); + } + return highscoreManager.getHighscores(currentPuzzleName); + } + + public double getAverageTimeForCurrentPuzzle() { + if (currentPuzzleName == null) { + return -1; + } + return highscoreManager.getAverageTime(currentPuzzleName); + } + + public String getCurrentPuzzleName() { + return currentPuzzleName; + } + + public void saveCurrentPuzzleHighscoreToFile() throws IOException { + if (currentPuzzleName == null) return; + String base = currentPuzzleName.replaceAll(".*/", ""); + String shortName = base.replace(".csv", "") + "_highscore.txt"; + highscoreManager.saveSinglePuzzle(currentPuzzleName, shortName); + System.out.println("Highscore zu " + base + " in " + shortName + " gespeichert."); + } + + + public void loadCurrentPuzzleHighscoreFromFile() throws IOException { + if (currentPuzzleName == null) return; + String base = currentPuzzleName.replaceAll(".*/", ""); + String shortName = base.replace(".csv", "") + "_highscore.txt"; + highscoreManager.loadSinglePuzzle(currentPuzzleName, shortName); + System.out.println("Highscore zu " + base + " aus " + shortName + " geladen (falls existiert)."); + } } \ No newline at end of file